From a611957c2c9b318f8da963390f5de18ff8fbe987 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Mon, 13 Oct 2025 15:51:29 +0500 Subject: [PATCH 01/15] =?UTF-8?q?=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- TEST_REPORT.md | 213 -------- analyze-potential-issues.js | 6 +- chrome-extension/package.json | 2 +- chrome-extension/public/background.js | 2 +- chrome-extension/public/offscreen.js | 8 +- chrome-extension/public/options/index.html | 8 +- .../public/plugins/ozon-analyzer/README.md | 4 +- .../plugins/ozon-analyzer/manifest.json | 39 +- .../plugins/ozon-analyzer/mcp_server.py | 82 ++-- .../plugins/ozon-analyzer/options/index.html | 6 +- .../ozon-analyzer/production-config.json | 2 +- chrome-extension/public/pyodide/package.json | 2 +- .../side-panel/assets/index-B6mlbBPM.js | 50 -- .../side-panel/assets/index-B6mlbBPM.js.map | 1 - .../side-panel/assets/index-BDNBm95e.js | 50 ++ .../side-panel/assets/index-BDNBm95e.js.map | 1 + chrome-extension/public/side-panel/index.html | 2 +- .../src/background/ai-api-client.ts | 4 +- chrome-extension/src/background/index.ts | 10 +- chrome-extension/src/background/package.json | 2 +- chrome-extension/test-chunk-transmission.html | 403 --------------- chrome-extension/test-communication.js | 38 -- chrome-extension/test-direct-page.html | 136 ------ chrome-extension/test-large-html.html | 105 ---- chrome-extension/test-pyodide-direct.js | 96 ---- docs/api-documentation.md | 8 +- docs/architecture.md | 4 +- docs/integration-guide.md | 2 +- .../development/ozon-analyzer-testing.md | 4 +- .../docs/lessons-learned.md | 2 +- .../ui/ozon-analyzer-ui-integration.md | 4 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- .../options/src/components/PluginDetails.tsx | 40 +- pages/options/src/hooks/useAIKeys.ts | 6 +- pages/options/src/locales/en.json | 4 +- pages/options/src/locales/ru.json | 4 +- pages/options/src/utils/mcpIntegration.ts | 10 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- src/background/offscreen.ts | 5 +- temp_before.json | 99 ---- temp_mcp_bridge.ts | 25 - test-chat-system.js | 141 ------ test-diagnostic.html | 259 ---------- test-load-prompts.html | 204 -------- test-options-browser.html | 440 ----------------- test-options-interface.js | 164 ------- test-ozon-chat.js | 226 --------- test-pyodide-message.js | 248 ---------- test-recovery-analysis.cjs | 187 ------- test-recovery-large-html.html | 191 -------- test-recovery-workflow.js | 183 ------- test-scripts/test-prompt-system.js | 14 +- test_custom_prompts_scenario.py | 326 ------------ test_fallback_damaged_manifest.py | 458 ----------------- test_fixes.sh | 116 ----- test_get_user_prompts.py | 347 ------------- test_integration_full_cycle.py | 462 ------------------ test_integration_prompts.py | 395 --------------- test_json_parsing.py | 165 ------- test_json_parsing_final.py | 208 -------- test_manifest_prompts_loading.py | 255 ---------- test_manifest_scenarios.py | 230 --------- test_manifest_transmission.py | 146 ------ test_manifest_transmission_integration.py | 376 -------------- test_prompts_extraction.py | 412 ---------------- test_prompts_mechanism.py | 412 ---------------- test_prompts_standalone.py | 369 -------------- test_text_truncation_fix.py | 110 ----- tests/e2e/package.json | 2 +- .../mocks/environment.js | 2 +- .../specs/ai-api.test.js | 2 +- 92 files changed, 222 insertions(+), 8365 deletions(-) delete mode 100644 TEST_REPORT.md delete mode 100644 chrome-extension/public/side-panel/assets/index-B6mlbBPM.js delete mode 100644 chrome-extension/public/side-panel/assets/index-B6mlbBPM.js.map create mode 100644 chrome-extension/public/side-panel/assets/index-BDNBm95e.js create mode 100644 chrome-extension/public/side-panel/assets/index-BDNBm95e.js.map delete mode 100644 chrome-extension/test-chunk-transmission.html delete mode 100644 chrome-extension/test-communication.js delete mode 100644 chrome-extension/test-direct-page.html delete mode 100644 chrome-extension/test-large-html.html delete mode 100644 chrome-extension/test-pyodide-direct.js delete mode 100644 temp_before.json delete mode 100644 temp_mcp_bridge.ts delete mode 100644 test-chat-system.js delete mode 100644 test-diagnostic.html delete mode 100644 test-load-prompts.html delete mode 100644 test-options-browser.html delete mode 100644 test-options-interface.js delete mode 100644 test-ozon-chat.js delete mode 100644 test-pyodide-message.js delete mode 100644 test-recovery-analysis.cjs delete mode 100644 test-recovery-large-html.html delete mode 100644 test-recovery-workflow.js delete mode 100644 test_custom_prompts_scenario.py delete mode 100644 test_fallback_damaged_manifest.py delete mode 100755 test_fixes.sh delete mode 100644 test_get_user_prompts.py delete mode 100644 test_integration_full_cycle.py delete mode 100644 test_integration_prompts.py delete mode 100644 test_json_parsing.py delete mode 100644 test_json_parsing_final.py delete mode 100644 test_manifest_prompts_loading.py delete mode 100644 test_manifest_scenarios.py delete mode 100644 test_manifest_transmission.py delete mode 100644 test_manifest_transmission_integration.py delete mode 100644 test_prompts_extraction.py delete mode 100644 test_prompts_mechanism.py delete mode 100644 test_prompts_standalone.py delete mode 100644 test_text_truncation_fix.py diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index a2efb806..87b78aa6 100644 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1345", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1345", + "version": "1.0.1350", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1350", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/TEST_REPORT.md b/TEST_REPORT.md deleted file mode 100644 index 61d7961d..00000000 --- a/TEST_REPORT.md +++ /dev/null @@ -1,213 +0,0 @@ -# 📋 ОТЧЁТ О ТЕСТИРОВАНИИ ИНТЕРФЕЙСА РЕДАКТИРОВАНИЯ ПРОМПТОВ - -**Ozon Analyzer Plugin** - тестирование нового интерфейса редактирования промптов - -**Дата тестирования:** 7 октября 2025 г. -**Тестировщик:** Roo (эксперт по отладке) -**Среда тестирования:** Linux, Node.js, Chrome Extension Framework - ---- - -## 🎯 ОБЩИЙ СТАТУС ТЕСТИРОВАНИЯ - -### ✅ РЕЗУЛЬТАТ: **ИНТЕРФЕЙС СТРУКТУРНО ГОТОВ, НО ТРЕБУЕТ ДОРАБОТОК ДЛЯ PRODUCTION** - -**Общая оценка:** 7/10 (функционально работоспособен, требует исправления выявленных проблем) - ---- - -## 📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ ПО КРИТЕРИЯМ - -### 1. ✅ Загрузка интерфейса options -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Интерфейс options/index.html успешно найден и содержит все необходимые элементы -- **Найденные элементы:** - - Вкладки типов промптов (оптимизированный/глубокий анализ) - - Вкладки языков (русский/английский) - - Поля для оригинального и кастомного промптов - - Кнопки копирования и сохранения - - Система уведомлений - -### 2. ✅ Правильная загрузка оригинальных промптов из manifest.json -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Промпты корректно определены в manifest.json -- **Структура промптов:** - ``` - options.prompts = { - optimized: { ru: {...}, en: {...} }, - deep: { ru: {...}, en: {...} } - } - ``` -- **Размеры промптов:** optimized/ru: ~37 симв., optimized/en: ~41 симв. - -### 3. ✅ Отображение вкладок для типов промптов и языков -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** HTML структура содержит необходимые вкладки -- **Элементы интерфейса:** prompt-type-tabs, language-tabs - -### 4. ✅ Функциональность переключения между типами промптов -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** JavaScript логика для switchPromptType() присутствует -- **Поддержка:** optimized ↔ deep анализ - -### 5. ✅ Функциональность переключения между языками -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** JavaScript логика для switchLanguage() присутствует -- **Поддержка:** ru ↔ en - -### 6. ✅ Копирование текста из левого поля в правое -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Функция copyPrompt() и кнопка копирования присутствуют -- **Логика:** Левый textarea (readonly) → Правый textarea (editable) - -### 7. ✅ Сохранение изменений в chrome.storage -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Функция saveSettings() использует chrome.storage.sync -- **Формат хранения:** `{'ozon-analyzer-prompts': {type: {lang: prompt}}}` -- **Обработка:** Сохранение только при наличии изменений - -### 8. ✅ Интеграция с backend (mcp_server.py) -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Функция get_user_prompts() корректно реализована -- **Fallback логика:** Кастомные промпты → manifest.json → пустые значения -- **Валидация:** Проверка длины промптов (>10 символов) - -### 9. ✅ Fallback логика при отсутствии кастомных промптов -- **Статус:** ✅ ПРОЙДЕН -- **Результат:** Корректная последовательность fallback -- **Приоритет:** chrome.storage → manifest.json → пустые строки - -### 10. ⚠️ Обработка ошибок (ЧАСТИЧНО) -- **Статус:** ⚠️ ТРЕБУЕТ УЛУЧШЕНИЙ -- **Результат:** Базовая обработка ошибок присутствует, но требует доработки -- **Текущие проблемы:** См. раздел "Выявленные проблемы" - ---- - -## 🚨 ВЫЯВЛЕННЫЕ ПРОБЛЕМЫ И РЕКОМЕНДАЦИИ - -### 🔴 КРИТИЧЕСКИЕ ПРОБЛЕМЫ (ВЫСОКИЙ ПРИОРИТЕТ) - -#### 1. **Загрузка manifest.json через fetch (CORS проблемы)** -```javascript -// Текущий проблемный код в options/index.html:348 -const response = await fetch('../manifest.json'); -``` -**Проблемы:** -- CORS ограничения в расширениях Chrome -- Неправильный путь к файлу -- Отсутствие обработки сетевых ошибок - -**Решение:** -```javascript -const manifestUrl = chrome.runtime.getURL('../manifest.json'); -const response = await fetch(manifestUrl); -``` - -#### 2. **Отсутствие валидации структуры manifest в Python коде** -```python -# mcp_server.py:235-236 -manifest = get_pyodide_var('manifest', {}) -manifest_prompts = safe_dict_get(manifest, 'options.prompts', {}) -``` -**Проблемы:** -- manifest может отсутствовать в Pyodide globals -- Нет проверки структуры данных -- Возможны поврежденные данные - -**Рекомендация:** Добавить валидацию структуры manifest и улучшенный fallback. - -### 🟡 ПРОБЛЕМЫ СРЕДНЕГО ПРИОРИТЕТА - -#### 3. **Недостаточная обработка ошибок** -- Нет проверки валидности JSON при загрузке -- Пользователь не всегда получает уведомления об ошибках -- Возможны silent failures - -#### 4. **Синхронизация данных chrome.storage и Python backend** -- Разные форматы ключей хранения -- Возможна рассинхронизация данных -- Нет механизма инвалидации кеша - -#### 5. **Производительность с длинными промптами** -- Нет ограничений на размер промптов -- Возможны проблемы с памятью при очень длинных текстах -- Рекомендация: добавить компрессию для промптов > 1KB - -### 🟢 ПРОБЛЕМЫ НИЗКОГО ПРИОРИТЕТА - -#### 6. **User Experience улучшения** -- Добавить индикатор загрузки при сохранении -- Улучшить валидацию вводимых данных -- Добавить подсказки для пользователей - ---- - -## 🧪 МЕТОДОЛОГИЯ ТЕСТИРОВАНИЯ - -### Выполненные тесты: -1. **Структура файлов** - проверка наличия всех необходимых файлов -2. **Статический анализ кода** - проверка HTML/JS/Python структур -3. **Логика сохранения** - валидация форматов данных -4. **Интеграция компонентов** - проверка взаимодействия frontend/backend -5. **Обработка ошибок** - анализ fallback механизмов - -### Тестовые инструменты: -- Node.js скрипты для структурного анализа -- HTML файл для имитации интерфейса -- Анализ кода на потенциальные проблемы - -### Ограничения тестирования: -- Невозможно полностью протестировать chrome.storage API вне расширения -- Ограниченное тестирование fetch API в изолированной среде -- Невозможно протестировать Pyodide globals без запущенного расширения - ---- - -## 🔧 РЕКОМЕНДАЦИИ ПО ИСПРАВЛЕНИЮ - -### Немедленные действия (до production): - -1. **Исправить fetch('../manifest.json')** на `chrome.runtime.getURL('../manifest.json')` -2. **Добавить try-catch блоки** вокруг всех async операций -3. **Улучшить валидацию JSON** при загрузке данных -4. **Добавить пользовательские уведомления** об ошибках - -### Краткосрочные улучшения: - -5. **Стандартизировать ключи хранения** в chrome.storage -6. **Добавить механизм синхронизации** данных -7. **Реализовать компрессию** для длинных промптов -8. **Улучшить UX** с индикаторами загрузки - -### Долгосрочные улучшения: - -9. **Добавить unit тесты** для критических функций -10. **Реализовать механизм backup/restore** промптов -11. **Добавить версионирование** промптов -12. **Оптимизировать производительность** для больших объемов данных - ---- - -## 📈 МЕТРИКИ КАЧЕСТВА КОДА - -- **Функциональная полнота:** 95% (все основные функции реализованы) -- **Надежность:** 70% (требует улучшения обработки ошибок) -- **Производительность:** 80% (хорошая, но есть возможности оптимизации) -- **Удобство сопровождения:** 85% (хорошо структурированный код) -- **Тестируемость:** 60% (требует дополнительных тестов) - ---- - -## ✅ ЗАКЛЮЧЕНИЕ - -**Интерфейс редактирования промптов для плагина Ozon Analyzer структурно корректен и функционально готов к использованию.** Основные компоненты (загрузка, отображение, сохранение, интеграция с backend) работают корректно. - -**Однако, для стабильной работы в production необходимо исправить выявленные критические проблемы, особенно связанные с загрузкой manifest.json и обработкой ошибок.** - -**Рекомендуется провести дополнительное тестирование в реальном расширении Chrome перед выпуском в production.** - ---- - -**Тестирование завершено:** 7 октября 2025 г. -**Следующие шаги:** Исправление выявленных проблем и регрессионное тестирование. \ No newline at end of file diff --git a/analyze-potential-issues.js b/analyze-potential-issues.js index f2c8ff4d..06550cec 100644 --- a/analyze-potential-issues.js +++ b/analyze-potential-issues.js @@ -58,13 +58,13 @@ console.log('📊 АНАЛИЗ СТРУКТУРЫ ПРОМПТОВ'); console.log('===========================\n'); const manifestPrompts = { - "optimized": { + "basic_analysis": { "ru": { "default": "Ты - токсиколог и химик-косметолог..." }, "en": { "default": "You are a board-certified toxicologist..." } }, - "deep": { + "deep_analysis": { "ru": { "default": "Длинный текст промпта глубокого анализа..." }, - "en": { "default": "Long English deep analysis prompt..." } + "en": { "default": "Long English deep_analysis analysis prompt..." } } }; diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 43bd3d63..e4329466 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/background.js b/chrome-extension/public/background.js index 6a708856..2fc513c6 100644 --- a/chrome-extension/public/background.js +++ b/chrome-extension/public/background.js @@ -1557,7 +1557,7 @@ function checkAiAlerts(stats) { } } const MODEL_CONFIGS = { - "gemini-flash": { + "gemini-flash-lite": { provider: "google", model_name: "gemini-flash-lite-latest:generateContent", endpoint: "https://generativelanguage.googleapis.com/v1beta/models/", diff --git a/chrome-extension/public/offscreen.js b/chrome-extension/public/offscreen.js index bf1ec5a3..68f54927 100644 --- a/chrome-extension/public/offscreen.js +++ b/chrome-extension/public/offscreen.js @@ -10,16 +10,16 @@ */ // Маппинг типов анализа на технические имена моделей const ANALYSIS_TYPE_MAPPING = { - 'basic_analysis': 'gemini-flash', + 'basic_analysis': 'gemini-flash-lite', 'detailed_comparison': 'gemini-pro', 'deep_analysis': 'gemini-pro', - 'scraping_fallback': 'gemini-flash', - 'compliance_check': 'gemini-flash' + 'scraping_fallback': 'gemini-flash-lite', + 'compliance_check': 'gemini-flash-lite' }; // Маппинг технических имён на конкретные модели API const MODEL_NAME_MAPPING = { - 'gemini-flash': 'gemini-flash-lite-latest:generateContent', + 'gemini-flash-lite': 'gemini-flash-lite-latest:generateContent', 'gemini-pro': 'gemini-2.5-pro:generateContent' }; diff --git a/chrome-extension/public/options/index.html b/chrome-extension/public/options/index.html index 80fd8f19..de484b88 100644 --- a/chrome-extension/public/options/index.html +++ b/chrome-extension/public/options/index.html @@ -380,19 +380,19 @@

ℹ️ Информация

// Fallback на встроенные значения промптов defaultPrompts = { - optimized: { + basic_analysis: { ru: { type: "text", default: "Ты - токсиколог и химик-косметолог с опытом. Проведи анализ косметического продукта...", - label: { ru: "Оптимизированный промпт (русский)", en: "Optimized Prompt (Russian)" } + label: { ru: "Базовый промпт (русский)", en: "basic_analysis Prompt (Russian)" } }, en: { type: "text", default: "You are a toxicologist and cosmetic chemist. Conduct analysis of cosmetic product...", - label: { ru: "Оптимизированный промпт (английский)", en: "Optimized Prompt (English)" } + label: { ru: "Базовый промпт (английский)", en: "basic_analysis Prompt (English)" } } }, - deep: { + deep_analysis: { ru: { type: "text", default: "Промпт для глубокого анализа на русском языке...", diff --git a/chrome-extension/public/plugins/ozon-analyzer/README.md b/chrome-extension/public/plugins/ozon-analyzer/README.md index 6a08ab3d..9f7b7403 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/README.md +++ b/chrome-extension/public/plugins/ozon-analyzer/README.md @@ -93,7 +93,7 @@ ozon-analyzer/ - **`gpt-4`** - Максимальная точность #### Google Gemini модели -- **`gemini-flash`** - Быстрый и качественный +- **`gemini-flash-lite`** - Быстрый и качественный - **`gemini-pro`** - Продвинутый анализа - **`gemini-pro`** - Максимальная глубина @@ -127,7 +127,7 @@ ozon-analyzer/ { "ai_models": { "basic_analysis": "gpt-4o-mini", - "detailed_comparison": "gemini-flash", + "detailed_comparison": "gemini-flash-lite", "deep_analysis": "gemini-pro" } } diff --git a/chrome-extension/public/plugins/ozon-analyzer/manifest.json b/chrome-extension/public/plugins/ozon-analyzer/manifest.json index 54c9f0e1..c3c39b28 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/manifest.json +++ b/chrome-extension/public/plugins/ozon-analyzer/manifest.json @@ -15,11 +15,11 @@ "scripting" ], "ai_models": { - "basic_analysis": "gemini-flash", - "compliance_check": "gemini-flash", + "basic_analysis": "gemini-flash-lite", + "compliance_check": "gemini-flash-lite", "detailed_comparison": "gemini-pro", "deep_analysis": "gemini-pro", - "scraping_fallback": "gemini-flash" + "scraping_fallback": "gemini-flash-lite" }, "options": { "enable_deep_analysis": { @@ -27,11 +27,11 @@ "default": true, "label": { "ru": "Включить глубокий анализ", - "en": "Enable deep analysis" + "en": "Enable deep_analysis analysis" }, "description": { - "ru": "Запускать дополнительный анализ при низкой оценке соответствия", - "en": "Run additional analysis when compliance score is low" + "ru": "Запускать дополнительный анализ при слишком низкой или высокой оценке соответствия", + "en": "Run additional analysis when the compliance score is too low or too high" } }, "auto_request_deep_analysis": { @@ -39,7 +39,7 @@ "default": true, "label": { "ru": "Автоматически запрашивать глубокий анализ", - "en": "Automatically request deep analysis" + "en": "Automatically request deep_analysis analysis" } }, "search_for_analogs": { @@ -93,15 +93,14 @@ } }, "prompts": { - "optimized": { + "basic_analysis": { "ru": { "type": "text", - "default": "Тестовый вопрос: сколько будет 16+16", - "default_": "Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки.\n\n ДАННЫЕ:\n Описание: {description}\n Состав: {composition}\n\n ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА:\n\n 1. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1):\n - Проверь КАЖДЫЙ компонент на:\n * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea)\n * Парабены (особенно butyl-, propyl-)\n * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone)\n * Потенциальные эндокринные дизрапторы\n - Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10\n\n 2. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ:\n - Термины типа \"3D/4D/5D\", \"революционный\", \"инновационный\" - ТРЕБУЮТ доказательств\n - Для каждого заявления (\"лифтинг\", \"против морщин\"):\n * Найди КОНКРЕТНЫЙ активный компонент\n * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг)\n * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте\n\n 3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ:\n - Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален\n - Collagen/Elastin: молекулы НЕ проникают, работают только как пленка\n - Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины\n\n 4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ:\n - Современная косметика = без парабенов, с новыми консервантами\n - Устаревшие формулы → снижение оценки на 2-3 балла\n\n КРИТИЧЕСКИ ВАЖНО:\n - Будь СКЕПТИЧЕН к маркетингу\n - НЕ завышай оценку из вежливости\n - Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10\n - Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку\n\n ФОРМАТ ОТВЕТА - ТОЛЬКО JSON:\n {{\n \"score\": число_от_1_до_10,\n \"reasoning\": \"ДЕТАЛЬНЫЙ анализ:\n 1. Проверка маркетинга: [разбери каждое заявление]\n 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты]\n 3. Реальная эффективность активов: [концентрации vs заявления]\n 4. Сравнение с современными стандартами: [почему устарел/актуален]\n 5. Итоговый вердикт: [честное заключение]\",\n \"confidence\": число_от_0_до_1,\n \"red_flags\": [\"список всех токсичных/проблемных компонентов\"],\n \"marketing_lies\": [\"список не подтвержденных маркетинговых заявлений\"]\n }}\n\n ЯЗЫК: Русский, технический стиль с примерами.", - + "default_": "Тестовый вопрос: сколько будет 16+16", + "default": "Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки.\n\n ДАННЫЕ:\n Описание: {description}\n Состав: {composition}\n\n ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА:\n\n 1. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1):\n - Проверь КАЖДЫЙ компонент на:\n * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea)\n * Парабены (особенно butyl-, propyl-)\n * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone)\n * Потенциальные эндокринные дизрапторы\n - Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10\n\n 2. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ:\n - Термины типа \"3D/4D/5D\", \"революционный\", \"инновационный\" - ТРЕБУЮТ доказательств\n - Для каждого заявления (\"лифтинг\", \"против морщин\"):\n * Найди КОНКРЕТНЫЙ активный компонент\n * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг)\n * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте\n\n 3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ:\n - Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален\n - Collagen/Elastin: молекулы НЕ проникают, работают только как пленка\n - Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины\n\n 4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ:\n - Современная косметика = без парабенов, с новыми консервантами\n - Устаревшие формулы → снижение оценки на 2-3 балла\n\n КРИТИЧЕСКИ ВАЖНО:\n - Будь СКЕПТИЧЕН к маркетингу\n - НЕ завышай оценку из вежливости\n - Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10\n - Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку\n\n ФОРМАТ ОТВЕТА - ТОЛЬКО JSON:\n {{\n \"score\": число_от_1_до_10,\n \"reasoning\": \"ДЕТАЛЬНЫЙ анализ:\n 1. Проверка маркетинга: [разбери каждое заявление]\n 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты]\n 3. Реальная эффективность активов: [концентрации vs заявления]\n 4. Сравнение с современными стандартами: [почему устарел/актуален]\n 5. Итоговый вердикт: [честное заключение]\",\n \"confidence\": число_от_0_до_1,\n \"red_flags\": [\"список всех токсичных/проблемных компонентов\"],\n \"marketing_lies\": [\"список не подтвержденных маркетинговых заявлений\"]\n }}\n\n ЯЗЫК: Русский, технический стиль с примерами.", "label": { - "ru": "Оптимизированный промпт (русский)", - "en": "Optimized Prompt (Russian)" + "ru": "Базовый промпт (русский)", + "en": "Basic Prompt (Russian)" }, "description": { "ru": "Промпт для базового анализа соответствия описания и состава", @@ -110,10 +109,10 @@ }, "en": { "type": "text", - "default": "You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation.\n\n DATA:\n Description: {description}\n Composition: {composition}\n\n MANDATORY ANALYSIS PROTOCOL:\n\n 1. TOXICOLOGICAL SCREENING (highest priority):\n - Screen EVERY ingredient for:\n * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea)\n * Parabens (particularly butylparaben, propylparaben - EU restricted)\n * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone)\n * Known/suspected endocrine disruptors\n - HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum\n\n 2. MARKETING CLAIMS VERIFICATION:\n - Buzzwords like \"3D/4D/5D technology\", \"revolutionary\", \"clinical breakthrough\" - DEMAND evidence\n - For each claim (\"lifting\", \"anti-wrinkle\", \"firming\"):\n * Identify the SPECIFIC active ingredient responsible\n * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose)\n * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product\n\n 3. REALISTIC EFFICACY ASSESSMENT:\n - Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect\n - Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers\n - Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep wrinkles\n\n 4. MODERN FORMULATION STANDARDS COMPARISON:\n - 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails\n - Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction\n\n 5. EVIDENCE-BASED SCORING RUBRIC (strict grading):\n - 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile\n - 7-8: Well-formulated, minor concerns only, actives present at reasonable levels\n - 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims)\n - 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing\n - 1-2: Potentially harmful or fraudulent product\n\n CRITICAL ASSESSMENT RULES:\n - Maintain scientific skepticism toward all marketing language\n - Apply evidence-based standards, NOT brand reputation\n - Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10\n - Claims unsupported by adequate active concentrations = reduce score proportionally\n - Default to LOWER score when ingredient concentrations are ambiguous\n\n OUTPUT FORMAT - VALID JSON ONLY:\n {{\n \"score\": integer_1_to_10,\n \"reasoning\": \"COMPREHENSIVE ANALYSIS:\n 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks]\n 2. Marketing Claims Audit: [fact-check each claim against ingredient reality]\n 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence]\n 4. Formulation Modernity Assessment: [evaluate against current industry standards]\n 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]\",\n \"confidence\": float_0_to_1,\n \"red_flags\": [\"comprehensive list of problematic/toxic/outdated ingredients\"],\n \"marketing_lies\": [\"specific unsubstantiated or misleading marketing claims\"]\n }}\n\n RESPONSE LANGUAGE: English, using precise technical terminology.", + "default": "You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation.\n\n DATA:\n Description: {description}\n Composition: {composition}\n\n MANDATORY ANALYSIS PROTOCOL:\n\n 1. TOXICOLOGICAL SCREENING (highest priority):\n - Screen EVERY ingredient for:\n * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea)\n * Parabens (particularly butylparaben, propylparaben - EU restricted)\n * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone)\n * Known/suspected endocrine disruptors\n - HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum\n\n 2. MARKETING CLAIMS VERIFICATION:\n - Buzzwords like \"3D/4D/5D technology\", \"revolutionary\", \"clinical breakthrough\" - DEMAND evidence\n - For each claim (\"lifting\", \"anti-wrinkle\", \"firming\"):\n * Identify the SPECIFIC active ingredient responsible\n * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose)\n * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product\n\n 3. REALISTIC EFFICACY ASSESSMENT:\n - Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect\n - Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers\n - Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep_analysis wrinkles\n\n 4. MODERN FORMULATION STANDARDS COMPARISON:\n - 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails\n - Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction\n\n 5. EVIDENCE-BASED SCORING RUBRIC (strict grading):\n - 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile\n - 7-8: Well-formulated, minor concerns only, actives present at reasonable levels\n - 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims)\n - 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing\n - 1-2: Potentially harmful or fraudulent product\n\n CRITICAL ASSESSMENT RULES:\n - Maintain scientific skepticism toward all marketing language\n - Apply evidence-based standards, NOT brand reputation\n - Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10\n - Claims unsupported by adequate active concentrations = reduce score proportionally\n - Default to LOWER score when ingredient concentrations are ambiguous\n\n OUTPUT FORMAT - VALID JSON ONLY:\n {{\n \"score\": integer_1_to_10,\n \"reasoning\": \"COMPREHENSIVE ANALYSIS:\n 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks]\n 2. Marketing Claims Audit: [fact-check each claim against ingredient reality]\n 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence]\n 4. Formulation Modernity Assessment: [evaluate against current industry standards]\n 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]\",\n \"confidence\": float_0_to_1,\n \"red_flags\": [\"comprehensive list of problematic/toxic/outdated ingredients\"],\n \"marketing_lies\": [\"specific unsubstantiated or misleading marketing claims\"]\n }}\n\n RESPONSE LANGUAGE: English, using precise technical terminology.", "label": { - "ru": "Оптимизированный промпт (английский)", - "en": "Optimized Prompt (English)" + "ru": "Базовый промпт (английский)", + "en": "Basic Prompt (English)" }, "description": { "ru": "Промпт для базового анализа соответствия описания и состава", @@ -121,14 +120,15 @@ } } }, - "deep": { + "deep_analysis": { "ru": { "type": "text", "default": "Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки.\n\n ДАННЫЕ:\n Описание: {description}\n Состав: {composition}\n\n ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА:\n\n 1. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ:\n - Термины типа \"3D/4D/5D\", \"революционный\", \"инновационный\" - ТРЕБУЮТ доказательств\n - Для каждого заявления (\"лифтинг\", \"против морщин\"):\n * Найди КОНКРЕТНЫЙ активный компонент\n * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг)\n * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте\n\n 2. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1):\n - Проверь КАЖДЫЙ компонент на:\n * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, и т.д.)\n * Парабены (особенно butyl-, propyl-)\n * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone)\n * Потенциальные эндокринные дизрапторы\n - Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10\n\n 3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ:\n - Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален\n - Collagen/Elastin: молекулы НЕ проникают, работают только как пленка\n - Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины\n\n 4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ:\n - Современная косметика = без парабенов, с новыми консервантами\n - Устаревшие формулы → снижение оценки на 2-3 балла\n\n 5. ШКАЛА ОЦЕНКИ (СТРОГАЯ):\n - 9-10: Идеальный состав, доказанные активы в высоких концентрациях, без токсичных компонентов\n - 7-8: Хороший состав, минимум проблемных компонентов\n - 5-6: Средний продукт, есть проблемные компоненты ИЛИ активы в низких дозах\n - 3-4: Устаревшая формула, много токсичных компонентов, маркетинговые заявления не подтверждены\n - 1-2: Опасный или полностью бесполезный продукт\n\n КРИТИЧЕСКИ ВАЖНО:\n - Будь СКЕПТИЧЕН к маркетингу\n - НЕ завышай оценку из вежливости\n - Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10\n - Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку\n\n ФОРМАТ ОТВЕТА - ТОЛЬКО JSON:\n {{\n \"score\": число_от_1_до_10,\n \"reasoning\": \"ДЕТАЛЬНЫЙ анализ:\n 1. Проверка маркетинга: [разбери каждое заявление]\n 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты]\n 3. Реальная эффективность активов: [концентрации vs заявления]\n 4. Сравнение с современными стандартами: [почему устарел/актуален]\n 5. Итоговый вердикт: [честное заключение]\",\n \"confidence\": число_от_0_до_1,\n \"red_flags\": [\"список всех токсичных/проблемных компонентов\"],\n \"marketing_lies\": [\"список не подтвержденных маркетинговых заявлений\"]\n }}\n\n ЯЗЫК: Русский, технический стиль с примерами.", "label": { "ru": "Промпт глубокого анализа (русский)", "en": "Deep Analysis Prompt (Russian)" - } + }, + "default_LLM": "gemini-2.5-pro:generateContent" }, "en": { "type": "text", @@ -136,7 +136,8 @@ "label": { "ru": "Промпт глубокого анализа (английский)", "en": "Deep Analysis Prompt (English)" - } + }, + "default_LLM": "gemini-2.5-pro:generateContent" } } } diff --git a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py index e3e8ff24..ea3558e6 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py +++ b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py @@ -219,7 +219,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s plugin_settings: Настройки плагина из Pyodide globals Returns: - Структура промптов: {optimized: {ru: "...", en: "..."}, deep: {ru: "...", en: "..."}} + Структура промптов: {basic_analysis: {ru: "...", en: "..."}, deep_analysis: {ru: "...", en: "..."}} """ console_log(f"🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ =====") @@ -264,8 +264,8 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s console_log(f" custom_prompts_raw type: {type(custom_prompts_raw)}") prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } # Используем manifest, полученный выше (из plugin_settings или globals) @@ -290,7 +290,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s manifest_prompts = {} # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: + for prompt_type in ['basic_analysis', 'deep_analysis']: for lang in ['ru', 'en']: console_log(f"🔍 Обработка {prompt_type}.{lang}:") @@ -327,7 +327,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s else: console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) + # default_value = _get_builtin_default_prompt(prompt_type, lang) if default_value: prompts[prompt_type][lang] = default_value console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") @@ -353,7 +353,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s # Подробная диагностика каждого промпта console_log(f"🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: + for prompt_type in ['basic_analysis', 'deep_analysis']: for lang in ['ru', 'en']: prompt_value = prompts[prompt_type][lang] if prompt_value and len(prompt_value.strip()) > 0: @@ -368,7 +368,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s # ДИАГНОСТИКА ПРОБЛЕМЫ: Проверяем источник каждого промпта console_log(f"🔍 ДИАГНОСТИКА ИСТОЧНИКОВ ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: + for prompt_type in ['basic_analysis', 'deep_analysis']: for lang in ['ru', 'en']: prompt_value = prompts[prompt_type][lang] if prompt_value and len(prompt_value.strip()) > 0: @@ -402,13 +402,13 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s # Критический fallback - возвращаем пустые промпты console_log(f"⚠️ Возвращаем критический fallback с пустыми промптами") return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } # Получаем manifest из plugin_settings или globals для fallback значений @@ -418,7 +418,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s manifest = get_pyodide_var('manifest', {}) manifest_prompts = safe_dict_get(manifest, 'options.prompts', {}) - for prompt_type in ['optimized', 'deep']: + for prompt_type in ['basic_analysis', 'deep_analysis']: for lang in ['ru', 'en']: # Правильно извлекаем промпт из nested структуры plugin_settings prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) @@ -451,8 +451,8 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s console_log(f" Plugin settings type: {type(plugin_settings)}") # Критический fallback - возвращаем пустые промпты return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } @@ -461,14 +461,14 @@ def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: Возвращает встроенные промпты по умолчанию для случаев когда они не найдены в manifest.json. Args: - prompt_type: Тип промпта ('optimized' или 'deep') + prompt_type: Тип промпта ('basic_analysis' или 'deep_analysis') lang: Язык промпта ('ru' или 'en') Returns: Строка с промптом по умолчанию или пустая строка если не найден """ builtin_prompts = { - 'optimized': { + 'basic_analysis': { 'ru': # """Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки. @@ -596,7 +596,7 @@ def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: RESPONSE LANGUAGE: English, using precise technical terminology.""" }, - 'deep': { + 'deep_analysis': { 'ru': """Проведи глубокий анализ товара с медицинской и научной точки зрения. Описание: {description} Состав: {composition} @@ -3751,28 +3751,28 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: except Exception as e: console_log(f"❌ Ошибка загрузки промптов: {str(e)}, используем fallback") user_prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } # console_log(f"Глубокий анализ: desc='{description[:100]}...', comp='{composition[:100]}...', язык={content_language}") # Получить промпт из пользовательских настроек или дефолтный try: - deep_prompt = user_prompts.get('deep', {}).get(content_language) + deep_prompt = user_prompts.get('deep_analysis', {}).get(content_language) if not deep_prompt or len(deep_prompt.strip()) < 3: - console_log(f"⚠️ Промпт deep.{content_language} не найден или слишком короткий, используем fallback") + console_log(f"⚠️ Промпт deep_analysis.{content_language} не найден или слишком короткий, используем fallback") deep_prompt = None else: - console_log(f"✅ Используем промпт deep.{content_language}") + console_log(f"✅ Используем промпт deep_analysis.{content_language}") except Exception as e: - console_log(f"❌ Ошибка получения промпта deep.{content_language}: {str(e)}, используем fallback") + console_log(f"❌ Ошибка получения промпта deep_analysis.{content_language}: {str(e)}, используем fallback") deep_prompt = None - # Оптимизированный промпт с учетом выбранного языка + # Базовый промпт с учетом выбранного языка if deep_prompt: prompt = deep_prompt - console_log(f"📋 Используем пользовательский промпт deep для языка {content_language}") + console_log(f"📋 Используем пользовательский промпт deep_analysis для языка {content_language}") elif content_language == "ru": # Старый хардкод промпт как fallback prompt = f""" @@ -3785,11 +3785,11 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: 3. Эффективность по сравнению с аналогами. Верни детальный максимально подробный обоснованный анализ в структурированном виде (используй Markdown). """ - console_log(f"⚠️ Используем fallback промпт deep для языка {content_language}") + console_log(f"⚠️ Используем fallback промпт deep_analysis для языка {content_language}") else: # English # Старый хардкод промпт как fallback для английского prompt = f""" - Conduct a deep analysis of the product from a medical and scientific perspective. + Conduct a deep_analysis analysis of the product from a medical and scientific perspective. Description: {description} Composition: {composition} Analyze: @@ -3798,7 +3798,7 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: 3. Effectiveness compared to analogs. Return a detailed, maximally comprehensive, reasoned analysis in a structured form (use Markdown). """ - console_log(f"⚠️ Используем fallback промпт deep для языка {content_language}") + console_log(f"⚠️ Используем fallback промпт deep_analysis для языка {content_language}") try: # "deep_analysis" - это псевдоним из `manifest.json` этого плагина. @@ -4161,8 +4161,8 @@ async def _analyze_composition_vs_description(description: str, composition: str except Exception as e: console_log(f"❌ Ошибка загрузки промптов: {str(e)}, используем fallback") user_prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} + 'basic_analysis': {'ru': '', 'en': ''}, + 'deep_analysis': {'ru': '', 'en': ''} } # ИСПОЛЬЗУЕМ ПЕРЕДАННЫЙ content_language ИЗ analyze_ozon_product @@ -4184,21 +4184,21 @@ async def _analyze_composition_vs_description(description: str, composition: str # Получить промпт из пользовательских настроек или дефолтный try: - optimized_prompt = user_prompts.get('optimized', {}).get(content_language) - if not optimized_prompt or len(optimized_prompt.strip()) < 3: + basic_analysis_prompt = user_prompts.get('basic_analysis', {}).get(content_language) + if not basic_analysis_prompt or len(basic_analysis_prompt.strip()) < 3: # Fallback на старый хардкод промпт - console_log(f"⚠️ Промпт optimized.{content_language} не найден или слишком короткий, используем fallback") - optimized_prompt = None + console_log(f"⚠️ Промпт basic_analysis.{content_language} не найден или слишком короткий, используем fallback") + basic_analysis_prompt = None else: - console_log(f"✅ Используем промпт optimized.{content_language}") + console_log(f"✅ Используем промпт basic_analysis.{content_language}") except Exception as e: - console_log(f"❌ Ошибка получения промпта optimized.{content_language}: {str(e)}, используем fallback") - optimized_prompt = None + console_log(f"❌ Ошибка получения промпта basic_analysis.{content_language}: {str(e)}, используем fallback") + basic_analysis_prompt = None - # Оптимизированный промпт с учетом выбранного языка - if optimized_prompt: - prompt = optimized_prompt - console_log(f"📋 Используем пользовательский промпт optimized для языка {content_language}") + # Базовый промпт с учетом выбранного языка + if basic_analysis_prompt: + prompt = basic_analysis_prompt + console_log(f"📋 Используем пользовательский промпт basic_analysis для языка {content_language}") elif content_language == "ru": # prompt = f""" # Проведи глубокий анализ (reasoning) соответствия Описания и Состава товара с медицинской и научной точки зрения: diff --git a/chrome-extension/public/plugins/ozon-analyzer/options/index.html b/chrome-extension/public/plugins/ozon-analyzer/options/index.html index 4b0dc128..220e4a91 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/options/index.html +++ b/chrome-extension/public/plugins/ozon-analyzer/options/index.html @@ -290,8 +290,8 @@

📝 Редактор промптов

- - + +
@@ -326,7 +326,7 @@

Кастомный промпт (редактируемый)

// Глобальные переменные let currentSettings = {}; let originalPrompts = {}; - let currentType = 'optimized'; + let currentType = 'basic_analysis'; let currentLang = 'ru'; // Показ уведомления diff --git a/chrome-extension/public/plugins/ozon-analyzer/production-config.json b/chrome-extension/public/plugins/ozon-analyzer/production-config.json index 90ff540c..c2837a62 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/production-config.json +++ b/chrome-extension/public/plugins/ozon-analyzer/production-config.json @@ -33,7 +33,7 @@ "ai_providers": { "google": { - "fallback_chain": ["gemini-flash", "gemini-pro"], + "fallback_chain": ["gemini-flash-lite", "gemini-pro"], "rate_limits": { "requests_per_minute": 60, "requests_per_hour": 1000, diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index d0f9e382..92c25d0e 100644 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1370", + "version": "0.27.1375", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js b/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js deleted file mode 100644 index f0f3851f..00000000 --- a/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js +++ /dev/null @@ -1,50 +0,0 @@ -(function(){const m=document.createElement("link").relList;if(m&&m.supports&&m.supports("modulepreload"))return;for(const A of document.querySelectorAll('link[rel="modulepreload"]'))c(A);new MutationObserver(A=>{for(const z of A)if(z.type==="childList")for(const D of z.addedNodes)D.tagName==="LINK"&&D.rel==="modulepreload"&&c(D)}).observe(document,{childList:!0,subtree:!0});function h(A){const z={};return A.integrity&&(z.integrity=A.integrity),A.referrerPolicy&&(z.referrerPolicy=A.referrerPolicy),A.crossOrigin==="use-credentials"?z.credentials="include":A.crossOrigin==="anonymous"?z.credentials="omit":z.credentials="same-origin",z}function c(A){if(A.ep)return;A.ep=!0;const z=h(A);fetch(A.href,z)}})();var au=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Rh(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var xo={exports:{}},La={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Dd;function Uh(){if(Dd)return La;Dd=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.fragment");function h(c,A,z){var D=null;if(z!==void 0&&(D=""+z),A.key!==void 0&&(D=""+A.key),"key"in A){z={};for(var I in A)I!=="key"&&(z[I]=A[I])}else z=A;return A=z.ref,{$$typeof:o,type:c,key:D,ref:A!==void 0?A:null,props:z}}return La.Fragment=m,La.jsx=h,La.jsxs=h,La}var Rd;function Nh(){return Rd||(Rd=1,xo.exports=Uh()),xo.exports}var y=Nh();const jh=({isDraftSaved:o,isDraftLoading:m,draftError:h,messageLength:c,minLength:A,maxLength:z})=>{const D=()=>m?y.jsx("div",{className:"draft-status-loader"}):h?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):o?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cm?"Загрузка черновика...":h||(o?"Черновик сохранен":c>0&&c=A&&c<=z?"Черновик будет сохранен автоматически":c>z?"Превышен лимит символов":""),H=()=>m?"draft-status loading":h?"draft-status error":o?"draft-status saved":c>0&&c=A&&c<=z?"draft-status ready":c>z?"draft-status error":"draft-status";return c===0&&!m&&!h?null:y.jsxs("div",{className:H(),children:[D(),y.jsx("span",{className:"draft-status-text",children:I()})]})};var Eo={exports:{}},be={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ud;function wh(){if(Ud)return be;Ud=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),A=Symbol.for("react.profiler"),z=Symbol.for("react.consumer"),D=Symbol.for("react.context"),I=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),Z=Symbol.iterator;function $(f){return f===null||typeof f!="object"?null:(f=Z&&f[Z]||f["@@iterator"],typeof f=="function"?f:null)}var ue={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ie=Object.assign,oe={};function k(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}k.prototype.isReactComponent={},k.prototype.setState=function(f,R){if(typeof f!="object"&&typeof f!="function"&&f!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,f,R,"setState")},k.prototype.forceUpdate=function(f){this.updater.enqueueForceUpdate(this,f,"forceUpdate")};function K(){}K.prototype=k.prototype;function te(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}var V=te.prototype=new K;V.constructor=te,ie(V,k.prototype),V.isPureReactComponent=!0;var de=Array.isArray,C={H:null,A:null,T:null,S:null,V:null},F=Object.prototype.hasOwnProperty;function J(f,R,W,L,ee,me){return W=me.ref,{$$typeof:o,type:f,key:R,ref:W!==void 0?W:null,props:me}}function Y(f,R){return J(f.type,R,void 0,void 0,void 0,f.props)}function ye(f){return typeof f=="object"&&f!==null&&f.$$typeof===o}function Ae(f){var R={"=":"=0",":":"=2"};return"$"+f.replace(/[=:]/g,function(W){return R[W]})}var pe=/\/+/g;function Te(f,R){return typeof f=="object"&&f!==null&&f.key!=null?Ae(""+f.key):R.toString(36)}function ut(){}function Ke(f){switch(f.status){case"fulfilled":return f.value;case"rejected":throw f.reason;default:switch(typeof f.status=="string"?f.then(ut,ut):(f.status="pending",f.then(function(R){f.status==="pending"&&(f.status="fulfilled",f.value=R)},function(R){f.status==="pending"&&(f.status="rejected",f.reason=R)})),f.status){case"fulfilled":return f.value;case"rejected":throw f.reason}}throw f}function we(f,R,W,L,ee){var me=typeof f;(me==="undefined"||me==="boolean")&&(f=null);var re=!1;if(f===null)re=!0;else switch(me){case"bigint":case"string":case"number":re=!0;break;case"object":switch(f.$$typeof){case o:case m:re=!0;break;case O:return re=f._init,we(re(f._payload),R,W,L,ee)}}if(re)return ee=ee(f),re=L===""?"."+Te(f,0):L,de(ee)?(W="",re!=null&&(W=re.replace(pe,"$&/")+"/"),we(ee,R,W,"",function(st){return st})):ee!=null&&(ye(ee)&&(ee=Y(ee,W+(ee.key==null||f&&f.key===ee.key?"":(""+ee.key).replace(pe,"$&/")+"/")+re)),R.push(ee)),1;re=0;var De=L===""?".":L+":";if(de(f))for(var Ne=0;Ne>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},Gh="Platform Settings",Bh="Available plugins",Ph="AI API Keys",qh="Free",Yh="Custom API keys",Lh="+ Add new key",Xh="Enter API key",Qh="Enter key name",Vh="Save all keys",Kh="Test connections",Zh="General Settings",kh="Automatic plugin updates",Jh="Show notifications",$h="Interface theme:",Wh="Light",Fh="Dark",Ih="System",ey="Security",ty="Verify plugin signatures",ly="Isolated execution mode",ny="Performance",ay="Maximum number of active plugins:",iy="Cache plugin data",uy="Plugin Details",sy={options:Hh,options_settings_title:Gh,options_plugins_title:Bh,options_settings_aiKeys_title:Ph,options_settings_aiKeys_badges_free:qh,options_settings_aiKeys_customKeys_title:Yh,options_settings_aiKeys_customKeys_addButton:Lh,options_settings_aiKeys_customKeys_keyPlaceholder:Xh,options_settings_aiKeys_customKeys_namePlaceholder:Qh,options_settings_aiKeys_actions_save:Vh,options_settings_aiKeys_actions_test:Kh,options_settings_general_title:Zh,options_settings_general_autoUpdate:kh,options_settings_general_showNotifications:Jh,options_settings_general_theme:$h,options_settings_general_theme_light:Wh,options_settings_general_theme_dark:Fh,options_settings_general_theme_system:Ih,options_settings_security_title:ey,options_settings_security_checkSignatures:ty,options_settings_security_isolatedMode:ly,options_settings_performance_title:ny,options_settings_performance_maxPlugins:ay,options_settings_performance_cacheData:iy,options_plugins_details_title:uy},oy={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",optimized:"Оптимизированный",deep:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},cy="Настройки Платформы",ry="Доступные плагины",fy="API Ключи нейросетей",dy="Бесплатный",gy="Пользовательские API ключи",my="+ Добавить новый ключ",hy="Введите API ключ",yy="Введите название ключа",py="Сохранить все ключи",vy="Тестировать подключения",by="Общие настройки",Sy="Автоматическое обновление плагинов",xy="Показывать уведомления",Ey="Тема интерфейса:",_y="Светлая",Ay="Тёмная",Ty="Системная",Cy="Безопасность",Oy="Проверять подписи плагинов",My="Изолированный режим выполнения",zy="Производительность",Dy="Максимальное количество активных плагинов:",Ry="Кэширование данных плагинов",Uy="Детали плагина",Ny={options:oy,options_settings_title:cy,options_plugins_title:ry,options_settings_aiKeys_title:fy,options_settings_aiKeys_badges_free:dy,options_settings_aiKeys_customKeys_title:gy,options_settings_aiKeys_customKeys_addButton:my,options_settings_aiKeys_customKeys_keyPlaceholder:hy,options_settings_aiKeys_customKeys_namePlaceholder:yy,options_settings_aiKeys_actions_save:py,options_settings_aiKeys_actions_test:vy,options_settings_general_title:by,options_settings_general_autoUpdate:Sy,options_settings_general_showNotifications:xy,options_settings_general_theme:Ey,options_settings_general_theme_light:_y,options_settings_general_theme_dark:Ay,options_settings_general_theme_system:Ty,options_settings_security_title:Cy,options_settings_security_checkSignatures:Oy,options_settings_security_isolatedMode:My,options_settings_performance_title:zy,options_settings_performance_maxPlugins:Dy,options_settings_performance_cacheData:Ry,options_plugins_details_title:Uy},jd={en:sy,ru:Ny},jy=(o="en")=>({t:Q.useMemo(()=>{const h=jd[o]||{},c=(A,z)=>z.split(".").reduce((D,I)=>D&&D[I]?D[I]:void 0,A);return A=>{const z=c(h,A);return z!==void 0?z:A}},[o,jd]),locale:o}),_o=({checked:o,onChange:m,disabled:h,label:c,iconOn:A,iconOff:z})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:h?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:o,disabled:h,onChange:D=>m(D.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:o?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:o?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:o?A:z})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),wy=({error:o,resetError:m})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),o&&y.jsx("pre",{children:o.message}),m&&y.jsx("button",{onClick:m,children:"Сбросить"})]}),Hy=({children:o})=>{const[m,h]=ou.useState(null),c=ou.useCallback(()=>h(null),[]);if(m)return y.jsx(wy,{error:m,resetError:c});try{return y.jsx(y.Fragment,{children:o})}catch(A){return h(A),null}},Gy=(...o)=>o.filter(Boolean).join(" "),By=({value:o,manifest:m,disabled:h,onSave:c,locale:A,t:z})=>{const[D,I]=Q.useState("optimized"),[H,b]=Q.useState("ru"),[O,Z]=Q.useState(""),$=()=>{try{const k=m?.options?.prompts;if(!k)return"";const V=((k[D]||{})[H]||{}).default||"";return typeof V=="object"?JSON.stringify(V,null,2):V}catch{return""}},ue=()=>{try{const te=((o||{})[D]||{})[H];return typeof te=="string"?te:te==null?"":JSON.stringify(te,null,2)}catch{return""}};Q.useEffect(()=>{Z(ue())},[D,H,o]);const ie=()=>{Z($())},oe=()=>{try{const k={...o};k[D]||(k[D]={ru:"",en:""}),k[D][H]=O,c(k)}catch(k){console.error("Failed to save custom prompt:",k)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:z("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:z("options.plugins.prompts.type")}),y.jsxs("select",{value:D,onChange:k=>I(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"optimized",children:z("options.plugins.prompts.optimized")}),y.jsx("option",{value:"deep",children:z("options.plugins.prompts.deep")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:z("options.plugins.prompts.language")}),y.jsxs("select",{value:H,onChange:k=>b(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:z("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:z("options.plugins.prompts.english")})]})]})]}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:z("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:$(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:ie,disabled:h,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:z("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:z("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:O,onChange:k=>Z(k.target.value),disabled:h,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:oe,disabled:h,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:z("options.plugins.prompts.save")})})]})})},Py=o=>{const{selectedPlugin:m,locale:h="en",onUpdateSetting:c}=o,{t:A}=jy(h),[z,D]=Q.useState(null),[I,H]=Q.useState(null),b=C=>C?typeof C=="string"?C:C[h]||C.ru||C.en||"":"";if(Q.useEffect(()=>{m?.manifest?.options&&$()},[m?.id]),!m||typeof m!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:A("options_plugins_details_title")}),y.jsx("p",{children:A("options.plugins.details.selectPlugin")})]});const O=m.settings||{enabled:!0,autorun:!1},Z=m.manifest?.host_permissions||[],$=async()=>{if(!(!m||!m.manifest?.options||I!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const F=Object.keys(m.manifest.options).map(ye=>`${m.id}_${ye}`),J=await chrome.storage.local.get(F),Y={};Object.entries(J).forEach(([ye,Ae])=>{const pe=ye.replace(`${m.id}_`,"");Ae!==void 0&&(Y[pe]=Ae)}),H(Y)}}catch(C){console.warn("Failed to load custom settings from chrome.storage.local:",C)}},ue=async(C,F)=>{if(m)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const J=`${m.id}_${C}`;await chrome.storage.local.set({[J]:F}),H(Y=>({...Y,[C]:F})),C==="prompts"&&await ie()}else console.warn("chrome.storage.local is not available")}catch(J){throw console.error(`Failed to save setting ${C} to chrome.storage.local:`,J),J}},ie=async()=>{if(m)try{const C=`${m.id}_prompts`,F=await chrome.storage.local.get([C]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${m.id}`),console.log(` Storage key: ${C}`),console.log(" Сохраненные промпты:",F[C]),F[C]){const J=F[C];console.log(" Структура промптов:"),console.log(` - optimized.ru: ${J.optimized?.ru?"✓":"✗"} (${J.optimized?.ru?.length||0} символов)`),console.log(` - optimized.en: ${J.optimized?.en?"✓":"✗"} (${J.optimized?.en?.length||0} символов)`),console.log(` - deep.ru: ${J.deep?.ru?"✓":"✗"} (${J.deep?.ru?.length||0} символов)`),console.log(` - deep.en: ${J.deep?.en?"✓":"✗"} (${J.deep?.en?.length||0} символов)`)}}catch(C){console.error("Ошибка диагностики промптов:",C)}},oe=(C,F)=>{if(I&&I[C]!==void 0)return I[C];if(C==="prompts"&&typeof F=="object"&&F!==null){const J=F,Y={optimized:{ru:{},en:{}},deep:{ru:{},en:{}}};return J.optimized?.ru?.default&&(Y.optimized.ru=J.optimized.ru.default),J.optimized?.en?.default&&(Y.optimized.en=J.optimized.en.default),J.deep?.ru?.default&&(Y.deep.ru=J.deep.ru.default),J.deep?.en?.default&&(Y.deep.en=J.deep.en.default),Y}return F},k=(C,F)=>{const J=oe(C,F.default),Y=z===C||!(O.enabled??!0),ye=b(F.label),Ae=b(F.description);if(C==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(By,{value:J,manifest:m.manifest,disabled:Y,onSave:pe=>K(C,pe),locale:h,t:A})},C);if(F.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:J,disabled:Y,onChange:pe=>K(C,pe),label:y.jsxs(y.Fragment,{children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"})]})})},C);if(F.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"}),y.jsx("select",{id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:F.values?.map(pe=>y.jsx("option",{value:pe,children:b(F.labels?.[pe])||pe},pe))})]})},C);if(F.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"text",id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C);if(F.type==="number"){const pe=Te=>{const ut=parseFloat(Te.target.value);if(isNaN(ut))return;let Ke=ut;F.min!==void 0&&KeF.max&&(Ke=F.max),K(C,Ke)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"number",id:C,name:C,value:J,disabled:Y,onChange:pe,min:F.min,max:F.max,step:F.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C)}return null},K=async(C,F)=>{if(!m)return;const J=m.manifest?.options;if(J&&J[C]){I===null&&await $();try{D(C),await ue(C,F)}catch(Y){console.error(`Failed to update custom setting ${C}:`,Y)}finally{D(null)}}else if(c)try{D(C),await c(m.id,C,F)}catch(Y){console.error(`Failed to update setting ${C}:`,Y)}finally{D(null)}},V=Object.entries(m.manifest?.options??{}).map(([C,F])=>k(C,F)).filter(C=>C!==null),de=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:O.enabled??!0,disabled:z==="enabled",onChange:C=>K("enabled",C),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:O.autorun??!1,disabled:z==="autorun"||!(O.enabled??!0),onChange:C=>K("autorun",C),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(Hy,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:m.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",m.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:Gy("status-badge",O.enabled?"status-active":"status-inactive"),children:O.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",m.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",m.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:m.description})]}),Z.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:Z.map((C,F)=>y.jsx("li",{children:C},F))})]}),Array.isArray(m.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(m.manifest?.permissions??[]).map((C,F)=>y.jsx("li",{children:C},F))})]}):null,y.jsx(de,{}),m.manifest?.options&&Object.keys(m.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),V]}):null]})]})})},qy=({plugin:o,onUpdateSetting:m})=>{const h=m?async(A,z,D)=>{try{return await m(A,z,D),!0}catch(I){return console.error(`Failed to update setting ${z}:`,I),!1}}:void 0,c={selectedPlugin:{...o,description:o.description||"Описание не указано",icon:o.icon||"",manifest:o.manifest||{}},locale:"ru",onUpdateSetting:h};return y.jsx(Py,{...c})},wd=function(o){if(!o)return"unknown-page";try{const m=new URL(o);return m.search="",m.hash="",m.toString()}catch{return o}},Yy=({pluginId:o,pageKey:m,debounceMs:h=1e3})=>{const[c,A]=Q.useState(""),[z,D]=Q.useState(!1),[I,H]=Q.useState(!1),[b,O]=Q.useState(null),[Z,$]=Q.useState(""),ue=Q.useRef(null),ie=Q.useRef(""),oe=Q.useCallback(async V=>{if(V!==ie.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:o,pageKey:m,text:V});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:V}),ie.current=V,D(!0),O(null),$(V),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:o,pageKey:m,text:V})}catch(de){console.error("[useLazyChatSync] Error saving draft:",de),O("Ошибка сохранения черновика"),D(!1)}}},[o,m]),k=Q.useCallback(async()=>{H(!0),O(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:o,pageKey:m});try{const V=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m});V?.draftText?(A(V.draftText),ie.current=V.draftText,D(!0),$(V.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:o,pageKey:m,draft:V.draftText})):(A(""),ie.current="",D(!1),$(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:o,pageKey:m}))}catch(V){console.error("[useLazyChatSync] Error loading draft:",V),O("Ошибка загрузки черновика")}finally{H(!1)}},[o,m]),K=Q.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:o,pageKey:m});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:""}),ie.current="",D(!1),O(null),$(""),A(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:o,pageKey:m})}catch(V){console.error("[useLazyChatSync] Error clearing draft:",V),O("Ошибка очистки черновика")}},[o,m]),te=Q.useCallback(V=>{A(V),ue.current&&clearTimeout(ue.current),V.length===0?K():ue.current=setTimeout(()=>{oe(V)},h),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:o,pageKey:m,text:V})},[h,oe,K,o,m]);return Q.useEffect(()=>()=>{ue.current&&clearTimeout(ue.current)},[]),Q.useEffect(()=>{k()},[k]),Q.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",m),D(!1),H(!1),O(null),ie.current="",ue.current&&(clearTimeout(ue.current),ue.current=null),k()},[m,k]),{message:c,setMessage:te,isDraftSaved:z,isDraftLoading:I,draftError:b,loadDraft:k,clearDraft:K,draftText:Z}};var su={exports:{}},Ly=su.exports,Hd;function Xy(){return Hd||(Hd=1,(function(o,m){(function(h,c){c()})(Ly,function(){function h(b,O){return typeof O>"u"?O={autoBom:!1}:typeof O!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),O={autoBom:!O}),O.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(b.type)?new Blob(["\uFEFF",b],{type:b.type}):b}function c(b,O,Z){var $=new XMLHttpRequest;$.open("GET",b),$.responseType="blob",$.onload=function(){H($.response,O,Z)},$.onerror=function(){console.error("could not download file")},$.send()}function A(b){var O=new XMLHttpRequest;O.open("HEAD",b,!1);try{O.send()}catch{}return 200<=O.status&&299>=O.status}function z(b){try{b.dispatchEvent(new MouseEvent("click"))}catch{var O=document.createEvent("MouseEvents");O.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),b.dispatchEvent(O)}}var D=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof au=="object"&&au.global===au?au:void 0,I=D.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),H=D.saveAs||(typeof window!="object"||window!==D?function(){}:"download"in HTMLAnchorElement.prototype&&!I?function(b,O,Z){var $=D.URL||D.webkitURL,ue=document.createElement("a");O=O||b.name||"download",ue.download=O,ue.rel="noopener",typeof b=="string"?(ue.href=b,ue.origin===location.origin?z(ue):A(ue.href)?c(b,O,Z):z(ue,ue.target="_blank")):(ue.href=$.createObjectURL(b),setTimeout(function(){$.revokeObjectURL(ue.href)},4e4),setTimeout(function(){z(ue)},0))}:"msSaveOrOpenBlob"in navigator?function(b,O,Z){if(O=O||b.name||"download",typeof b!="string")navigator.msSaveOrOpenBlob(h(b,Z),O);else if(A(b))c(b,O,Z);else{var $=document.createElement("a");$.href=b,$.target="_blank",setTimeout(function(){z($)})}}:function(b,O,Z,$){if($=$||open("","_blank"),$&&($.document.title=$.document.body.innerText="downloading..."),typeof b=="string")return c(b,O,Z);var ue=b.type==="application/octet-stream",ie=/constructor/i.test(D.HTMLElement)||D.safari,oe=/CriOS\/[\d]+/.test(navigator.userAgent);if((oe||ue&&ie||I)&&typeof FileReader<"u"){var k=new FileReader;k.onloadend=function(){var V=k.result;V=oe?V:V.replace(/^data:[^;]*;/,"data:attachment/file;"),$?$.location.href=V:location=V,$=null},k.readAsDataURL(b)}else{var K=D.URL||D.webkitURL,te=K.createObjectURL(b);$?$.location=te:location.href=te,$=null,setTimeout(function(){K.revokeObjectURL(te)},4e4)}});D.saveAs=H.saveAs=H,o.exports=H})})(su)),su.exports}var Qy=Xy(),Ln;(function(o){o.Local="local",o.Sync="sync",o.Managed="managed",o.Session="session"})(Ln||(Ln={}));var jo;(function(o){o.ExtensionPagesOnly="TRUSTED_CONTEXTS",o.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(jo||(jo={}));const Yn=globalThis.chrome,Gd=async(o,m)=>{const h=A=>typeof A=="function",c=A=>A instanceof Promise;return h(o)?(c(o),o(m)):o};let Bd=!1;const Pd=o=>{if(Yn&&!Yn.storage[o])throw new Error(`"storage" permission in manifest.ts: "storage ${o}" isn't defined`)},eg=(o,m,h)=>{let c=null,A=!1,z=[];const D=h?.storageEnum??Ln.Local,I=h?.liveUpdate??!1,H=h?.serialization?.serialize??(k=>k),b=h?.serialization?.deserialize??(k=>k);Bd===!1&&D===Ln.Session&&h?.sessionAccessForContentScripts===!0&&(Pd(D),Yn?.storage[D].setAccessLevel({accessLevel:jo.ExtensionPagesAndContentScripts}).catch(k=>{console.error(k),console.error("Please call .setAccessLevel() into different context, like a background script.")}),Bd=!0);const O=async()=>{Pd(D);const k=await Yn?.storage[D].get([o]);return k?b(k[o])??m:m},Z=async k=>{A||(c=await O()),c=await Gd(k,c),await Yn?.storage[D].set({[o]:H(c)}),ie()},$=k=>(z=[...z,k],()=>{z=z.filter(K=>K!==k)}),ue=()=>c,ie=()=>{z.forEach(k=>k())},oe=async k=>{if(k[o]===void 0)return;const K=b(k[o].newValue);c!==K&&(c=await Gd(K,c),ie())};return O().then(k=>{c=k,A=!0,ie()}),I&&Yn?.storage[D].onChanged.addListener(oe),{get:O,set:Z,getSnapshot:ue,subscribe:$}},qd=eg("theme-storage-key",{theme:"system",isLight:tg()},{storageEnum:Ln.Local,liveUpdate:!0});function tg(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Ao={...qd,toggle:async()=>{await qd.set(o=>{let m;switch(o.theme){case"light":m="dark";break;case"dark":m="system";break;case"system":default:m="light";break}const h=m==="system"?tg():m==="light";return{theme:m,isLight:h}})}},To=eg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Ln.Local,liveUpdate:!0}),Yd={...To,setAlignment:async o=>{await To.set({alignment:o})},getAlignment:async()=>(await To.get()).alignment},Vy=({plugin:o,currentView:m,isRunning:h,isPaused:c,currentTabUrl:A,onStart:z,onPause:D,onStop:I,onClose:H})=>{const[b,O]=Q.useState("chat"),[Z,$]=Q.useState(wd(A)),[ue,ie]=Q.useState("left");Q.useEffect(()=>{const M=wd(A);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:Z,newPageKey:M,currentTabUrl:A,timestamp:new Date().toISOString()}),$(M)},[A]),Q.useEffect(()=>{const M=async()=>{const p=await Yd.getAlignment();ie(p)};return M(),Yd.subscribe(()=>{M()})},[]);const{message:oe,setMessage:k,isDraftSaved:K,isDraftLoading:te,draftError:V,loadDraft:de,clearDraft:C,draftText:F}=Yy({pluginId:o.id,pageKey:Z,debounceMs:1e3}),[J,Y]=Q.useState([]),[ye,Ae]=Q.useState(!1),[pe,Te]=Q.useState(null),[ut,Ke]=Q.useState(60),[we,T]=Q.useState(!1),X=Q.useRef(null),q=Q.useRef(null);Q.useEffect(()=>{},[h]);const Ce=()=>{z()},f=o.name||(typeof o.manifest?.name=="string"?o.manifest.name:"")||o.id,R=o.id,W=Q.useCallback(async M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};try{return await chrome.runtime.sendMessage(p)}catch(B){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",B),B}},[]),L=Q.useCallback(M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};chrome.runtime.sendMessage(p)},[]),ee=Q.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const M={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},g={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},B=N=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:N,hasMessages:N&&"messages"in N,hasChat:N&&"chat"in N,messagesValue:N?.messages,chatValue:N?.chat,isMessagesArray:Array.isArray(N?.messages),isChatArray:Array.isArray(N?.chat),responseType:typeof N,responseKeys:N?Object.keys(N):"response is null/undefined",timestamp:new Date().toISOString()}),N===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let j=null;const P=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],ce=(Se,ve)=>{let xe=Se;for(const Ye of ve)if(xe&&typeof xe=="object"&&Ye in xe)xe=xe[Ye];else return;return xe};if(Array.isArray(N))j=N,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});else if(N&&typeof N=="object"){if(N.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",N.error),Te(`Ошибка от background: ${N.error}`),Y([]);return}for(const{path:Se,description:ve}of P){const xe=ce(N,Se);if(Array.isArray(xe)){j=xe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${ve}':`,{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});break}}j||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof N,responseKeys:Object.keys(N),responseSample:JSON.stringify(N).substring(0,500),timestamp:new Date().toISOString()}),j=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:N,responseType:typeof N,responseStringified:JSON.stringify(N).substring(0,200),timestamp:new Date().toISOString()}),j=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:j,isArray:Array.isArray(j),length:j?.length,firstMessage:j?.[0],firstMessageType:j?.[0]?typeof j[0]:"none"}),Array.isArray(j)&&j.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",j.length);const Se=j.filter(ve=>!ve||typeof ve!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",ve),!1):!0).map((ve,xe)=>{try{let Ye=ve.content||ve.text||"";typeof Ye=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",Ye),Ye=JSON.stringify(Ye)):Ye==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Ye=""):Ye=String(Ye);const cl={id:ve.id||String(ve.timestamp||Date.now()+xe),text:Ye,isUser:ve.role?ve.role==="user":!!ve.isUser,timestamp:ve.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${xe}:`,{id:cl.id,textLength:cl.text.length,textType:typeof cl.text,isUser:cl.isUser}),cl}catch(Ye){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${xe}:`,Ye,ve),{id:`error_${Date.now()}_${xe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:j.length,convertedCount:Se.length,firstConverted:Se[0],allConverted:Se.map(ve=>{try{const xe=typeof ve.text=="string"?ve.text.substring(0,50):String(ve.text||"").substring(0,50);return{id:ve.id,text:xe,isUser:ve.isUser,textType:typeof ve.text}}catch(xe){return console.warn("[PluginControlPanel] Error processing message text:",xe,ve),{id:ve.id,text:"[ERROR: invalid text]",isUser:ve.isUser,textType:typeof ve.text}}})}),Y(Se)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),B(M)}catch(N){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",N)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),B(g)}catch(N){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",N)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),B(p)}catch(N){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",N)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);Q.useCallback(M=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:M,hasMessages:M&&"messages"in M,hasChat:M&&"chat"in M,messagesValue:M?.messages,chatValue:M?.chat,isMessagesArray:Array.isArray(M?.messages),isChatArray:Array.isArray(M?.chat),responseType:typeof M,responseKeys:M?Object.keys(M):"response is null/undefined",timestamp:new Date().toISOString()}),M===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let g=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],B=(N,j)=>{let P=N;for(const ce of j)if(P&&typeof P=="object"&&ce in P)P=P[ce];else return;return P};if(Array.isArray(M))g=M,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});else if(M&&typeof M=="object"){if(M.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",M.error),Te(`Ошибка от background: ${M.error}`),Y([]);return}for(const{path:N,description:j}of p){const P=B(M,N);if(Array.isArray(P)){g=P,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${j}':`,{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});break}}g||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof M,responseKeys:Object.keys(M),responseSample:JSON.stringify(M).substring(0,500),timestamp:new Date().toISOString()}),g=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:M,responseType:typeof M,responseStringified:JSON.stringify(M).substring(0,200),timestamp:new Date().toISOString()}),g=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:g,isArray:Array.isArray(g),length:g?.length,firstMessage:g?.[0],firstMessageType:g?.[0]?typeof g[0]:"none"}),Array.isArray(g)&&g.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",g.length);const N=g.filter(j=>!j||typeof j!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",j),!1):!0).map((j,P)=>{try{let ce=j.content||j.text||"",Se=j.timestamp||Date.now();if(typeof ce=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",ce),ce=JSON.stringify(ce);else if(ce==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),ce="";else{ce=String(ce),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${P}:`,ce);try{const xe=JSON.parse(ce);typeof xe=="object"&&xe!==null&&"content"in xe&&(console.log("[PluginControlPanel] Распарсен JSON из content:",xe),ce=String(xe.content||""),xe.timestamp&&typeof xe.timestamp=="number"&&(Se=xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${P}:`,ce)}const ve={id:j.id||String(Se+P),text:ce,isUser:j.role?j.role==="user":!!j.isUser,timestamp:Se};return console.log(`[PluginControlPanel] Конвертировано сообщение ${P}:`,{id:ve.id,textLength:ve.text.length,textType:typeof ve.text,isUser:ve.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${P}:`,ve.text),ve}catch(ce){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${P}:`,ce,j),{id:`error_${Date.now()}_${P}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:g.length,convertedCount:N.length,firstConverted:N[0],allConverted:N.map(j=>{try{const P=typeof j.text=="string"?j.text.substring(0,50):String(j.text||"").substring(0,50);return{id:j.id,text:P,isUser:j.isUser,textType:typeof j.text}}catch(P){return console.warn("[PluginControlPanel] Error processing message text:",P,j),{id:j.id,text:"[ERROR: invalid text]",isUser:j.isUser,textType:typeof j.text}}})}),Y(N)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const me=Q.useCallback(async()=>{Ae(!0),Te(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString(),isRunning:h,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const M=await W({type:"GET_PLUGIN_CHAT",pluginId:R,pageKey:Z});console.log("[PluginControlPanel] loadChat - получен ответ от background:",M),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:ye,messagesCount:J.length,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),M?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",M.error),Te(`Ошибка загрузки чата: ${M.error}`),Y([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(g=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:g,hasMessages:g&&"messages"in g,hasChat:g&&"chat"in g,messagesValue:g?.messages,chatValue:g?.chat,isMessagesArray:Array.isArray(g?.messages),isChatArray:Array.isArray(g?.chat),responseType:typeof g,responseKeys:g?Object.keys(g):"response is null/undefined",timestamp:new Date().toISOString()}),g===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let p=null;const B=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],N=(j,P)=>{let ce=j;for(const Se of P)if(ce&&typeof ce=="object"&&Se in ce)ce=ce[Se];else return;return ce};if(Array.isArray(g))p=g,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(g&&typeof g=="object"){if(g.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",g.error),Te(`Ошибка от background: ${g.error}`),Y([]);return}for(const{path:j,description:P}of B){const ce=N(g,j);if(Array.isArray(ce)){p=ce,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${P}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof g,responseKeys:Object.keys(g),responseSample:JSON.stringify(g).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:g,responseType:typeof g,responseStringified:JSON.stringify(g).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const j=p.filter(P=>!P||typeof P!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",P),!1):!0).map((P,ce)=>{try{let Se=P.content||P.text||"",ve=P.timestamp||Date.now();if(typeof Se=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",Se),Se=JSON.stringify(Se);else if(Se==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Se="";else{Se=String(Se);try{const Ye=JSON.parse(Se);typeof Ye=="object"&&Ye!==null&&"content"in Ye&&(console.log("[PluginControlPanel] Распаршен JSON из content:",Ye),Se=String(Ye.content||""),Ye.timestamp&&typeof Ye.timestamp=="number"&&(ve=Ye.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const xe={id:P.id||String(ve+ce),text:Se,isUser:P.role?P.role==="user":!!P.isUser,timestamp:ve};return console.log(`[PluginControlPanel] Конвертировано сообщение ${ce}:`,{id:xe.id,textLength:xe.text.length,textType:typeof xe.text,isUser:xe.isUser}),xe}catch(Se){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${ce}:`,Se,P),{id:`error_${Date.now()}_${ce}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:j.length,firstConverted:j[0],allConverted:j.map(P=>{try{const ce=typeof P.text=="string"?P.text.substring(0,50):String(P.text||"").substring(0,50);return{id:P.id,text:ce,isUser:P.isUser,textType:typeof P.text}}catch(ce){return console.warn("[PluginControlPanel] Error processing message text:",ce,P),{id:P.id,text:"[ERROR: invalid text]",isUser:P.isUser,textType:typeof P.text}}})}),Y(j)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(M),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(M){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",M),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:ye,messagesCount:J.length,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:M,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),errorStack:M instanceof Error?M.stack:"No stack trace",errorName:M instanceof Error?M.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:R,pageKey:Z}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let g="Ошибка связи с background";M instanceof Error?(g+=`: ${M.message}`,M.name==="TypeError"&&M.message.includes("substring")&&(g+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:M,stack:M.stack,context:{pluginId:R,pageKey:Z}}))):g+=`: ${String(M)}`,Te(g),Y([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[R,Z,W,A,h,c]);Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString()}),me().catch(M=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",M)})},[me]),Q.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),me().catch(M=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",M)}),de()},[Z,me,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:R,pageKey:Z,timestamp:new Date().toISOString()});const M=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===R&&p.pageKey===Z&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:ye,messagesCount:J.length,currentPageKey:Z,pluginId:R,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),me().catch(B=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",B)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:ye,messagesCount:J.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),Te(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},g=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(M),chrome.runtime.onMessage.removeListener(g),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(M),chrome.runtime.onMessage.removeListener(g)}},[R,Z,L,me]),Q.useEffect(()=>{m==="chat"&&de()},[m,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:R,pageKey:Z,draftText:F,message:oe,currentView:m,isRunning:h,isPaused:c})}),Q.useEffect(()=>{const M=console.error;return console.error=(...g)=>{const p=g.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",g),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),M.apply(console,g)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=M,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),Q.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const M=g=>{const p=g.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),Q.useEffect(()=>{typeof F=="string"&&(k(F),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",F))},[F,k]);const re=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:oe}),!oe.trim())return;const M={id:Date.now().toString(),text:oe.trim(),timestamp:Date.now()};k(""),Te(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),L({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:R,pageKey:Z,message:{role:"user",content:M.text,timestamp:M.timestamp}}),C()};Q.useEffect(()=>{const M=p=>{if(!we)return;const B=document.querySelector(".chat-view");if(!B)return;const N=B.getBoundingClientRect(),j=N.bottom-p.clientY,P=100,ce=N.height-80;j>=P&&j<=ce&&Ke(N.height-j)},g=()=>{T(!1),document.body.style.cursor="",document.body.style.userSelect=""};return we&&(document.addEventListener("mousemove",M),document.addEventListener("mouseup",g)),()=>{document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",g)}},[we]),Q.useEffect(()=>{X.current?.scrollIntoView({behavior:"smooth"})},[J]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:J.length,firstMessage:J[0]?{id:J[0].id,text:typeof J[0].text=="string"?J[0].text.substring(0,50):String(J[0].text||"").substring(0,50),isUser:J[0].isUser,timestamp:J[0].timestamp,textType:typeof J[0].text}:null,allMessages:J.map(M=>{try{const g=typeof M.text=="string"?M.text.substring(0,30):String(M.text||"").substring(0,30);return{id:M.id,text:g,isUser:M.isUser,textType:typeof M.text}}catch(g){return console.warn("[PluginControlPanel] Error in message logging:",g,M),{id:M.id,text:"[LOGGING ERROR]",isUser:M.isUser,textType:typeof M.text}}}),timestamp:new Date().toISOString()})},[J]),Q.useEffect(()=>{m==="chat"&&setTimeout(()=>q.current?.focus(),100)},[m]);const De=M=>{k(M.target.value);const g=M.target;g.style.height="auto";const p=Math.min(Math.max(g.scrollHeight,60),200);g.style.height=`${p}px`,Ke(p)},Ne=M=>{M.key==="Enter"&&!M.shiftKey&&(M.preventDefault(),re())},st=()=>{Ae(!0),Te(null),L({type:"DELETE_PLUGIN_CHAT",pluginId:R,pageKey:Z}),Y([]),C()},zt=()=>{const M=JSON.stringify(J,null,2),g=new Blob([M],{type:"application/json"});Qy.saveAs(g,`plugin-chat-${R}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":ue},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:o.iconUrl||`plugins/${o.id}/${o.icon||"icon.svg"}`,alt:`${f} icon`,onError:M=>{const g=typeof f=="string"&&f.length>0?f.charAt(0):"P";M.currentTarget.src=`data:image/svg+xml;utf8,${g}`}}),y.jsx("h3",{children:f})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${h?"stop-mode":"start-mode"}`,onClick:Ce,disabled:h||c,title:h?"Остановить":"Запустить",children:h?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:D,disabled:!h||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:I,disabled:!h,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:H,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${b==="chat"?"active":""}`,onClick:()=>O("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${b==="details"?"active":""}`,onClick:()=>O("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[b==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:st,disabled:ye||!!pe,children:ye?"Очистка...":pe?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:zt,disabled:ye||!!pe,children:ye?"Экспорт...":pe?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ee,disabled:ye,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[ye&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),pe&&y.jsx("div",{className:"chat-error",children:pe}),!ye&&!pe&&J.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:J.map((M,g)=>{console.log("[PluginControlPanel] render message:",g,M);let p=M.text,B=M.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",g,":",M.text);try{const N=JSON.parse(p);if(typeof N=="object"&&N!==null&&"content"in N){console.log("[PluginControlPanel] Парсинг JSON в рендере:",N);let j=N.content;typeof j=="object"?p=JSON.stringify(j):p=String(j||""),N.timestamp&&typeof N.timestamp=="number"&&(B=N.timestamp)}else if(typeof N=="string")p=N;else if(typeof N=="object"&&N!==null){const j=Object.values(N).filter(P=>typeof P=="string");j.length>0?p=String(j[0]):p=JSON.stringify(N)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",g,":",p),y.jsx("div",{className:`chat-message ${M.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(B).toLocaleTimeString()})]})},M.id||g)})}),y.jsx("div",{ref:X})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:q,className:"message-textarea",value:oe,onChange:De,onKeyPress:Ne,placeholder:"Напишите сообщение...",style:{height:`${ut}px`}}),y.jsx("button",{className:"send-btn",onClick:re,disabled:!oe.trim(),children:"📤"}),y.jsx(jh,{isDraftSaved:K,isDraftLoading:te,draftError:V,messageLength:oe.length,minLength:10,maxLength:1e3})]})]}),b==="details"&&y.jsx(qy,{plugin:o})]})]})},Ky=({toasts:o,onRemove:m})=>y.jsx("div",{className:"toast-container",children:o.map(h=>y.jsx(Zy,{toast:h,onRemove:m},h.id))}),Zy=({toast:o,onRemove:m})=>{const[h,c]=Q.useState(!1),[A,z]=Q.useState(!1);Q.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),o.duration>0){const I=setTimeout(()=>{D()},o.duration);return()=>clearTimeout(I)}},[o.duration]);const D=Q.useCallback(()=>{z(!0),setTimeout(()=>{m(o.id)},300)},[o.id,m]);return y.jsx("div",{className:`toast toast-${o.type} ${h?"toast-visible":""} ${A?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:o.message}),y.jsx("button",{className:"toast-close",onClick:D,"aria-label":"Закрыть уведомление",children:"×"})]})})},ky=({theme:o,isLight:m,onToggle:h,isInSidebar:c=!1})=>{const A=()=>{switch(o){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},z=()=>{switch(o){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},D={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:h,style:D,title:z(),children:A()})};function lg(o){var m,h,c="";if(typeof o=="string"||typeof o=="number")c+=o;else if(typeof o=="object")if(Array.isArray(o)){var A=o.length;for(m=0;m{const m=Fy(o),{conflictingClassGroups:h,conflictingClassGroupModifiers:c}=o;return{getClassGroupId:D=>{const I=D.split(Po);return I[0]===""&&I.length!==1&&I.shift(),ng(I,m)||Wy(D)},getConflictingClassGroupIds:(D,I)=>{const H=h[D]||[];return I&&c[D]?[...H,...c[D]]:H}}},ng=(o,m)=>{if(o.length===0)return m.classGroupId;const h=o[0],c=m.nextPart.get(h),A=c?ng(o.slice(1),c):void 0;if(A)return A;if(m.validators.length===0)return;const z=o.join(Po);return m.validators.find(({validator:D})=>D(z))?.classGroupId},Ld=/^\[(.+)\]$/,Wy=o=>{if(Ld.test(o)){const m=Ld.exec(o)[1],h=m?.substring(0,m.indexOf(":"));if(h)return"arbitrary.."+h}},Fy=o=>{const{theme:m,classGroups:h}=o,c={nextPart:new Map,validators:[]};for(const A in h)wo(h[A],c,A,m);return c},wo=(o,m,h,c)=>{o.forEach(A=>{if(typeof A=="string"){const z=A===""?m:Xd(m,A);z.classGroupId=h;return}if(typeof A=="function"){if(Iy(A)){wo(A(c),m,h,c);return}m.validators.push({validator:A,classGroupId:h});return}Object.entries(A).forEach(([z,D])=>{wo(D,Xd(m,z),h,c)})})},Xd=(o,m)=>{let h=o;return m.split(Po).forEach(c=>{h.nextPart.has(c)||h.nextPart.set(c,{nextPart:new Map,validators:[]}),h=h.nextPart.get(c)}),h},Iy=o=>o.isThemeGetter,ep=o=>{if(o<1)return{get:()=>{},set:()=>{}};let m=0,h=new Map,c=new Map;const A=(z,D)=>{h.set(z,D),m++,m>o&&(m=0,c=h,h=new Map)};return{get(z){let D=h.get(z);if(D!==void 0)return D;if((D=c.get(z))!==void 0)return A(z,D),D},set(z,D){h.has(z)?h.set(z,D):A(z,D)}}},Ho="!",Go=":",tp=Go.length,lp=o=>{const{prefix:m,experimentalParseClassName:h}=o;let c=A=>{const z=[];let D=0,I=0,H=0,b;for(let ie=0;ieH?b-H:void 0;return{modifiers:z,hasImportantModifier:$,baseClassName:Z,maybePostfixModifierPosition:ue}};if(m){const A=m+Go,z=c;c=D=>D.startsWith(A)?z(D.substring(A.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:D,maybePostfixModifierPosition:void 0}}if(h){const A=c;c=z=>h({className:z,parseClassName:A})}return c},np=o=>o.endsWith(Ho)?o.substring(0,o.length-1):o.startsWith(Ho)?o.substring(1):o,ap=o=>{const m=Object.fromEntries(o.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const A=[];let z=[];return c.forEach(D=>{D[0]==="["||m[D]?(A.push(...z.sort(),D),z=[]):z.push(D)}),A.push(...z.sort()),A}},ip=o=>({cache:ep(o.cacheSize),parseClassName:lp(o),sortModifiers:ap(o),...$y(o)}),up=/\s+/,sp=(o,m)=>{const{parseClassName:h,getClassGroupId:c,getConflictingClassGroupIds:A,sortModifiers:z}=m,D=[],I=o.trim().split(up);let H="";for(let b=I.length-1;b>=0;b-=1){const O=I[b],{isExternal:Z,modifiers:$,hasImportantModifier:ue,baseClassName:ie,maybePostfixModifierPosition:oe}=h(O);if(Z){H=O+(H.length>0?" "+H:H);continue}let k=!!oe,K=c(k?ie.substring(0,oe):ie);if(!K){if(!k){H=O+(H.length>0?" "+H:H);continue}if(K=c(ie),!K){H=O+(H.length>0?" "+H:H);continue}k=!1}const te=z($).join(":"),V=ue?te+Ho:te,de=V+K;if(D.includes(de))continue;D.push(de);const C=A(K,k);for(let F=0;F0?" "+H:H)}return H};function op(){let o=0,m,h,c="";for(;o{if(typeof o=="string")return o;let m,h="";for(let c=0;cZ(O),o());return h=ip(b),c=h.cache.get,A=h.cache.set,z=I,I(H)}function I(H){const b=c(H);if(b)return b;const O=sp(H,h);return A(H,O),O}return function(){return z(op.apply(null,arguments))}}const tt=o=>{const m=h=>h[o]||[];return m.isThemeGetter=!0,m},ig=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,ug=/^\((?:(\w[\w-]*):)?(.+)\)$/i,rp=/^\d+\/\d+$/,fp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,dp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,gp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,mp=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,hp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,qn=o=>rp.test(o),_e=o=>!!o&&!Number.isNaN(Number(o)),jl=o=>!!o&&Number.isInteger(Number(o)),Co=o=>o.endsWith("%")&&_e(o.slice(0,-1)),ol=o=>fp.test(o),yp=()=>!0,pp=o=>dp.test(o)&&!gp.test(o),sg=()=>!1,vp=o=>mp.test(o),bp=o=>hp.test(o),Sp=o=>!ne(o)&&!ae(o),xp=o=>Xn(o,rg,sg),ne=o=>ig.test(o),Il=o=>Xn(o,fg,pp),Oo=o=>Xn(o,Cp,_e),Qd=o=>Xn(o,og,sg),Ep=o=>Xn(o,cg,bp),iu=o=>Xn(o,dg,vp),ae=o=>ug.test(o),Xa=o=>Qn(o,fg),_p=o=>Qn(o,Op),Vd=o=>Qn(o,og),Ap=o=>Qn(o,rg),Tp=o=>Qn(o,cg),uu=o=>Qn(o,dg,!0),Xn=(o,m,h)=>{const c=ig.exec(o);return c?c[1]?m(c[1]):h(c[2]):!1},Qn=(o,m,h=!1)=>{const c=ug.exec(o);return c?c[1]?m(c[1]):h:!1},og=o=>o==="position"||o==="percentage",cg=o=>o==="image"||o==="url",rg=o=>o==="length"||o==="size"||o==="bg-size",fg=o=>o==="length",Cp=o=>o==="number",Op=o=>o==="family-name",dg=o=>o==="shadow",Mp=()=>{const o=tt("color"),m=tt("font"),h=tt("text"),c=tt("font-weight"),A=tt("tracking"),z=tt("leading"),D=tt("breakpoint"),I=tt("container"),H=tt("spacing"),b=tt("radius"),O=tt("shadow"),Z=tt("inset-shadow"),$=tt("text-shadow"),ue=tt("drop-shadow"),ie=tt("blur"),oe=tt("perspective"),k=tt("aspect"),K=tt("ease"),te=tt("animate"),V=()=>["auto","avoid","all","avoid-page","page","left","right","column"],de=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],C=()=>[...de(),ae,ne],F=()=>["auto","hidden","clip","visible","scroll"],J=()=>["auto","contain","none"],Y=()=>[ae,ne,H],ye=()=>[qn,"full","auto",...Y()],Ae=()=>[jl,"none","subgrid",ae,ne],pe=()=>["auto",{span:["full",jl,ae,ne]},jl,ae,ne],Te=()=>[jl,"auto",ae,ne],ut=()=>["auto","min","max","fr",ae,ne],Ke=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],we=()=>["start","end","center","stretch","center-safe","end-safe"],T=()=>["auto",...Y()],X=()=>[qn,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...Y()],q=()=>[o,ae,ne],Ce=()=>[...de(),Vd,Qd,{position:[ae,ne]}],f=()=>["no-repeat",{repeat:["","x","y","space","round"]}],R=()=>["auto","cover","contain",Ap,xp,{size:[ae,ne]}],W=()=>[Co,Xa,Il],L=()=>["","none","full",b,ae,ne],ee=()=>["",_e,Xa,Il],me=()=>["solid","dashed","dotted","double"],re=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],De=()=>[_e,Co,Vd,Qd],Ne=()=>["","none",ie,ae,ne],st=()=>["none",_e,ae,ne],zt=()=>["none",_e,ae,ne],M=()=>[_e,ae,ne],g=()=>[qn,"full",...Y()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[ol],breakpoint:[ol],color:[yp],container:[ol],"drop-shadow":[ol],ease:["in","out","in-out"],font:[Sp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[ol],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[ol],shadow:[ol],spacing:["px",_e],text:[ol],"text-shadow":[ol],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",qn,ne,ae,k]}],container:["container"],columns:[{columns:[_e,ne,ae,I]}],"break-after":[{"break-after":V()}],"break-before":[{"break-before":V()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:C()}],overflow:[{overflow:F()}],"overflow-x":[{"overflow-x":F()}],"overflow-y":[{"overflow-y":F()}],overscroll:[{overscroll:J()}],"overscroll-x":[{"overscroll-x":J()}],"overscroll-y":[{"overscroll-y":J()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:ye()}],"inset-x":[{"inset-x":ye()}],"inset-y":[{"inset-y":ye()}],start:[{start:ye()}],end:[{end:ye()}],top:[{top:ye()}],right:[{right:ye()}],bottom:[{bottom:ye()}],left:[{left:ye()}],visibility:["visible","invisible","collapse"],z:[{z:[jl,"auto",ae,ne]}],basis:[{basis:[qn,"full","auto",I,...Y()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[_e,qn,"auto","initial","none",ne]}],grow:[{grow:["",_e,ae,ne]}],shrink:[{shrink:["",_e,ae,ne]}],order:[{order:[jl,"first","last","none",ae,ne]}],"grid-cols":[{"grid-cols":Ae()}],"col-start-end":[{col:pe()}],"col-start":[{"col-start":Te()}],"col-end":[{"col-end":Te()}],"grid-rows":[{"grid-rows":Ae()}],"row-start-end":[{row:pe()}],"row-start":[{"row-start":Te()}],"row-end":[{"row-end":Te()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":ut()}],"auto-rows":[{"auto-rows":ut()}],gap:[{gap:Y()}],"gap-x":[{"gap-x":Y()}],"gap-y":[{"gap-y":Y()}],"justify-content":[{justify:[...Ke(),"normal"]}],"justify-items":[{"justify-items":[...we(),"normal"]}],"justify-self":[{"justify-self":["auto",...we()]}],"align-content":[{content:["normal",...Ke()]}],"align-items":[{items:[...we(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...we(),{baseline:["","last"]}]}],"place-content":[{"place-content":Ke()}],"place-items":[{"place-items":[...we(),"baseline"]}],"place-self":[{"place-self":["auto",...we()]}],p:[{p:Y()}],px:[{px:Y()}],py:[{py:Y()}],ps:[{ps:Y()}],pe:[{pe:Y()}],pt:[{pt:Y()}],pr:[{pr:Y()}],pb:[{pb:Y()}],pl:[{pl:Y()}],m:[{m:T()}],mx:[{mx:T()}],my:[{my:T()}],ms:[{ms:T()}],me:[{me:T()}],mt:[{mt:T()}],mr:[{mr:T()}],mb:[{mb:T()}],ml:[{ml:T()}],"space-x":[{"space-x":Y()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":Y()}],"space-y-reverse":["space-y-reverse"],size:[{size:X()}],w:[{w:[I,"screen",...X()]}],"min-w":[{"min-w":[I,"screen","none",...X()]}],"max-w":[{"max-w":[I,"screen","none","prose",{screen:[D]},...X()]}],h:[{h:["screen","lh",...X()]}],"min-h":[{"min-h":["screen","lh","none",...X()]}],"max-h":[{"max-h":["screen","lh",...X()]}],"font-size":[{text:["base",h,Xa,Il]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,ae,Oo]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Co,ne]}],"font-family":[{font:[_p,ne,m]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[A,ae,ne]}],"line-clamp":[{"line-clamp":[_e,"none",ae,Oo]}],leading:[{leading:[z,...Y()]}],"list-image":[{"list-image":["none",ae,ne]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",ae,ne]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:q()}],"text-color":[{text:q()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...me(),"wavy"]}],"text-decoration-thickness":[{decoration:[_e,"from-font","auto",ae,Il]}],"text-decoration-color":[{decoration:q()}],"underline-offset":[{"underline-offset":[_e,"auto",ae,ne]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:Y()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ae,ne]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ae,ne]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Ce()}],"bg-repeat":[{bg:f()}],"bg-size":[{bg:R()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},jl,ae,ne],radial:["",ae,ne],conic:[jl,ae,ne]},Tp,Ep]}],"bg-color":[{bg:q()}],"gradient-from-pos":[{from:W()}],"gradient-via-pos":[{via:W()}],"gradient-to-pos":[{to:W()}],"gradient-from":[{from:q()}],"gradient-via":[{via:q()}],"gradient-to":[{to:q()}],rounded:[{rounded:L()}],"rounded-s":[{"rounded-s":L()}],"rounded-e":[{"rounded-e":L()}],"rounded-t":[{"rounded-t":L()}],"rounded-r":[{"rounded-r":L()}],"rounded-b":[{"rounded-b":L()}],"rounded-l":[{"rounded-l":L()}],"rounded-ss":[{"rounded-ss":L()}],"rounded-se":[{"rounded-se":L()}],"rounded-ee":[{"rounded-ee":L()}],"rounded-es":[{"rounded-es":L()}],"rounded-tl":[{"rounded-tl":L()}],"rounded-tr":[{"rounded-tr":L()}],"rounded-br":[{"rounded-br":L()}],"rounded-bl":[{"rounded-bl":L()}],"border-w":[{border:ee()}],"border-w-x":[{"border-x":ee()}],"border-w-y":[{"border-y":ee()}],"border-w-s":[{"border-s":ee()}],"border-w-e":[{"border-e":ee()}],"border-w-t":[{"border-t":ee()}],"border-w-r":[{"border-r":ee()}],"border-w-b":[{"border-b":ee()}],"border-w-l":[{"border-l":ee()}],"divide-x":[{"divide-x":ee()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ee()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...me(),"hidden","none"]}],"divide-style":[{divide:[...me(),"hidden","none"]}],"border-color":[{border:q()}],"border-color-x":[{"border-x":q()}],"border-color-y":[{"border-y":q()}],"border-color-s":[{"border-s":q()}],"border-color-e":[{"border-e":q()}],"border-color-t":[{"border-t":q()}],"border-color-r":[{"border-r":q()}],"border-color-b":[{"border-b":q()}],"border-color-l":[{"border-l":q()}],"divide-color":[{divide:q()}],"outline-style":[{outline:[...me(),"none","hidden"]}],"outline-offset":[{"outline-offset":[_e,ae,ne]}],"outline-w":[{outline:["",_e,Xa,Il]}],"outline-color":[{outline:q()}],shadow:[{shadow:["","none",O,uu,iu]}],"shadow-color":[{shadow:q()}],"inset-shadow":[{"inset-shadow":["none",Z,uu,iu]}],"inset-shadow-color":[{"inset-shadow":q()}],"ring-w":[{ring:ee()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:q()}],"ring-offset-w":[{"ring-offset":[_e,Il]}],"ring-offset-color":[{"ring-offset":q()}],"inset-ring-w":[{"inset-ring":ee()}],"inset-ring-color":[{"inset-ring":q()}],"text-shadow":[{"text-shadow":["none",$,uu,iu]}],"text-shadow-color":[{"text-shadow":q()}],opacity:[{opacity:[_e,ae,ne]}],"mix-blend":[{"mix-blend":[...re(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":re()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[_e]}],"mask-image-linear-from-pos":[{"mask-linear-from":De()}],"mask-image-linear-to-pos":[{"mask-linear-to":De()}],"mask-image-linear-from-color":[{"mask-linear-from":q()}],"mask-image-linear-to-color":[{"mask-linear-to":q()}],"mask-image-t-from-pos":[{"mask-t-from":De()}],"mask-image-t-to-pos":[{"mask-t-to":De()}],"mask-image-t-from-color":[{"mask-t-from":q()}],"mask-image-t-to-color":[{"mask-t-to":q()}],"mask-image-r-from-pos":[{"mask-r-from":De()}],"mask-image-r-to-pos":[{"mask-r-to":De()}],"mask-image-r-from-color":[{"mask-r-from":q()}],"mask-image-r-to-color":[{"mask-r-to":q()}],"mask-image-b-from-pos":[{"mask-b-from":De()}],"mask-image-b-to-pos":[{"mask-b-to":De()}],"mask-image-b-from-color":[{"mask-b-from":q()}],"mask-image-b-to-color":[{"mask-b-to":q()}],"mask-image-l-from-pos":[{"mask-l-from":De()}],"mask-image-l-to-pos":[{"mask-l-to":De()}],"mask-image-l-from-color":[{"mask-l-from":q()}],"mask-image-l-to-color":[{"mask-l-to":q()}],"mask-image-x-from-pos":[{"mask-x-from":De()}],"mask-image-x-to-pos":[{"mask-x-to":De()}],"mask-image-x-from-color":[{"mask-x-from":q()}],"mask-image-x-to-color":[{"mask-x-to":q()}],"mask-image-y-from-pos":[{"mask-y-from":De()}],"mask-image-y-to-pos":[{"mask-y-to":De()}],"mask-image-y-from-color":[{"mask-y-from":q()}],"mask-image-y-to-color":[{"mask-y-to":q()}],"mask-image-radial":[{"mask-radial":[ae,ne]}],"mask-image-radial-from-pos":[{"mask-radial-from":De()}],"mask-image-radial-to-pos":[{"mask-radial-to":De()}],"mask-image-radial-from-color":[{"mask-radial-from":q()}],"mask-image-radial-to-color":[{"mask-radial-to":q()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":de()}],"mask-image-conic-pos":[{"mask-conic":[_e]}],"mask-image-conic-from-pos":[{"mask-conic-from":De()}],"mask-image-conic-to-pos":[{"mask-conic-to":De()}],"mask-image-conic-from-color":[{"mask-conic-from":q()}],"mask-image-conic-to-color":[{"mask-conic-to":q()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Ce()}],"mask-repeat":[{mask:f()}],"mask-size":[{mask:R()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",ae,ne]}],filter:[{filter:["","none",ae,ne]}],blur:[{blur:Ne()}],brightness:[{brightness:[_e,ae,ne]}],contrast:[{contrast:[_e,ae,ne]}],"drop-shadow":[{"drop-shadow":["","none",ue,uu,iu]}],"drop-shadow-color":[{"drop-shadow":q()}],grayscale:[{grayscale:["",_e,ae,ne]}],"hue-rotate":[{"hue-rotate":[_e,ae,ne]}],invert:[{invert:["",_e,ae,ne]}],saturate:[{saturate:[_e,ae,ne]}],sepia:[{sepia:["",_e,ae,ne]}],"backdrop-filter":[{"backdrop-filter":["","none",ae,ne]}],"backdrop-blur":[{"backdrop-blur":Ne()}],"backdrop-brightness":[{"backdrop-brightness":[_e,ae,ne]}],"backdrop-contrast":[{"backdrop-contrast":[_e,ae,ne]}],"backdrop-grayscale":[{"backdrop-grayscale":["",_e,ae,ne]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[_e,ae,ne]}],"backdrop-invert":[{"backdrop-invert":["",_e,ae,ne]}],"backdrop-opacity":[{"backdrop-opacity":[_e,ae,ne]}],"backdrop-saturate":[{"backdrop-saturate":[_e,ae,ne]}],"backdrop-sepia":[{"backdrop-sepia":["",_e,ae,ne]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":Y()}],"border-spacing-x":[{"border-spacing-x":Y()}],"border-spacing-y":[{"border-spacing-y":Y()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",ae,ne]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[_e,"initial",ae,ne]}],ease:[{ease:["linear","initial",K,ae,ne]}],delay:[{delay:[_e,ae,ne]}],animate:[{animate:["none",te,ae,ne]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[oe,ae,ne]}],"perspective-origin":[{"perspective-origin":C()}],rotate:[{rotate:st()}],"rotate-x":[{"rotate-x":st()}],"rotate-y":[{"rotate-y":st()}],"rotate-z":[{"rotate-z":st()}],scale:[{scale:zt()}],"scale-x":[{"scale-x":zt()}],"scale-y":[{"scale-y":zt()}],"scale-z":[{"scale-z":zt()}],"scale-3d":["scale-3d"],skew:[{skew:M()}],"skew-x":[{"skew-x":M()}],"skew-y":[{"skew-y":M()}],transform:[{transform:[ae,ne,"","none","gpu","cpu"]}],"transform-origin":[{origin:C()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:g()}],"translate-x":[{"translate-x":g()}],"translate-y":[{"translate-y":g()}],"translate-z":[{"translate-z":g()}],"translate-none":["translate-none"],accent:[{accent:q()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:q()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ae,ne]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":Y()}],"scroll-mx":[{"scroll-mx":Y()}],"scroll-my":[{"scroll-my":Y()}],"scroll-ms":[{"scroll-ms":Y()}],"scroll-me":[{"scroll-me":Y()}],"scroll-mt":[{"scroll-mt":Y()}],"scroll-mr":[{"scroll-mr":Y()}],"scroll-mb":[{"scroll-mb":Y()}],"scroll-ml":[{"scroll-ml":Y()}],"scroll-p":[{"scroll-p":Y()}],"scroll-px":[{"scroll-px":Y()}],"scroll-py":[{"scroll-py":Y()}],"scroll-ps":[{"scroll-ps":Y()}],"scroll-pe":[{"scroll-pe":Y()}],"scroll-pt":[{"scroll-pt":Y()}],"scroll-pr":[{"scroll-pr":Y()}],"scroll-pb":[{"scroll-pb":Y()}],"scroll-pl":[{"scroll-pl":Y()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ae,ne]}],fill:[{fill:["none",...q()]}],"stroke-w":[{stroke:[_e,Xa,Il,Oo]}],stroke:[{stroke:["none",...q()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},zp=cp(Mp),Kd=(...o)=>zp(Jy(o));var Mo,Zd;function Dp(){if(Zd)return Mo;Zd=1;var o=function(te){return m(te)&&!h(te)};function m(K){return!!K&&typeof K=="object"}function h(K){var te=Object.prototype.toString.call(K);return te==="[object RegExp]"||te==="[object Date]"||z(K)}var c=typeof Symbol=="function"&&Symbol.for,A=c?Symbol.for("react.element"):60103;function z(K){return K.$$typeof===A}function D(K){return Array.isArray(K)?[]:{}}function I(K,te){return te.clone!==!1&&te.isMergeableObject(K)?oe(D(K),K,te):K}function H(K,te,V){return K.concat(te).map(function(de){return I(de,V)})}function b(K,te){if(!te.customMerge)return oe;var V=te.customMerge(K);return typeof V=="function"?V:oe}function O(K){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(K).filter(function(te){return Object.propertyIsEnumerable.call(K,te)}):[]}function Z(K){return Object.keys(K).concat(O(K))}function $(K,te){try{return te in K}catch{return!1}}function ue(K,te){return $(K,te)&&!(Object.hasOwnProperty.call(K,te)&&Object.propertyIsEnumerable.call(K,te))}function ie(K,te,V){var de={};return V.isMergeableObject(K)&&Z(K).forEach(function(C){de[C]=I(K[C],V)}),Z(te).forEach(function(C){ue(K,C)||($(K,C)&&V.isMergeableObject(te[C])?de[C]=b(C,V)(K[C],te[C],V):de[C]=I(te[C],V))}),de}function oe(K,te,V){V=V||{},V.arrayMerge=V.arrayMerge||H,V.isMergeableObject=V.isMergeableObject||o,V.cloneUnlessOtherwiseSpecified=I;var de=Array.isArray(te),C=Array.isArray(K),F=de===C;return F?de?V.arrayMerge(K,te,V):ie(K,te,V):I(te,V)}oe.all=function(te,V){if(!Array.isArray(te))throw new Error("first argument should be an array");return te.reduce(function(de,C){return oe(de,C,V)},{})};var k=oe;return Mo=k,Mo}Dp();var zo={exports:{}},Qa={},Do={exports:{}},Ro={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var kd;function Rp(){return kd||(kd=1,(function(o){function m(T,X){var q=T.length;T.push(X);e:for(;0>>1,f=T[Ce];if(0>>1;CeA(L,q))eeA(me,L)?(T[Ce]=me,T[ee]=q,Ce=ee):(T[Ce]=L,T[W]=q,Ce=W);else if(eeA(me,q))T[Ce]=me,T[ee]=q,Ce=ee;else break e}}return X}function A(T,X){var q=T.sortIndex-X.sortIndex;return q!==0?q:T.id-X.id}if(o.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var z=performance;o.unstable_now=function(){return z.now()}}else{var D=Date,I=D.now();o.unstable_now=function(){return D.now()-I}}var H=[],b=[],O=1,Z=null,$=3,ue=!1,ie=!1,oe=!1,k=!1,K=typeof setTimeout=="function"?setTimeout:null,te=typeof clearTimeout=="function"?clearTimeout:null,V=typeof setImmediate<"u"?setImmediate:null;function de(T){for(var X=h(b);X!==null;){if(X.callback===null)c(b);else if(X.startTime<=T)c(b),X.sortIndex=X.expirationTime,m(H,X);else break;X=h(b)}}function C(T){if(oe=!1,de(T),!ie)if(h(H)!==null)ie=!0,F||(F=!0,Te());else{var X=h(b);X!==null&&we(C,X.startTime-T)}}var F=!1,J=-1,Y=5,ye=-1;function Ae(){return k?!0:!(o.unstable_now()-yeT&&Ae());){var Ce=Z.callback;if(typeof Ce=="function"){Z.callback=null,$=Z.priorityLevel;var f=Ce(Z.expirationTime<=T);if(T=o.unstable_now(),typeof f=="function"){Z.callback=f,de(T),X=!0;break t}Z===h(H)&&c(H),de(T)}else c(H);Z=h(H)}if(Z!==null)X=!0;else{var R=h(b);R!==null&&we(C,R.startTime-T),X=!1}}break e}finally{Z=null,$=q,ue=!1}X=void 0}}finally{X?Te():F=!1}}}var Te;if(typeof V=="function")Te=function(){V(pe)};else if(typeof MessageChannel<"u"){var ut=new MessageChannel,Ke=ut.port2;ut.port1.onmessage=pe,Te=function(){Ke.postMessage(null)}}else Te=function(){K(pe,0)};function we(T,X){J=K(function(){T(o.unstable_now())},X)}o.unstable_IdlePriority=5,o.unstable_ImmediatePriority=1,o.unstable_LowPriority=4,o.unstable_NormalPriority=3,o.unstable_Profiling=null,o.unstable_UserBlockingPriority=2,o.unstable_cancelCallback=function(T){T.callback=null},o.unstable_forceFrameRate=function(T){0>T||125Ce?(T.sortIndex=q,m(b,T),h(H)===null&&T===h(b)&&(oe?(te(J),J=-1):oe=!0,we(C,q-Ce))):(T.sortIndex=f,m(H,T),ie||ue||(ie=!0,F||(F=!0,Te()))),T},o.unstable_shouldYield=Ae,o.unstable_wrapCallback=function(T){var X=$;return function(){var q=$;$=X;try{return T.apply(this,arguments)}finally{$=q}}}})(Ro)),Ro}var Jd;function Up(){return Jd||(Jd=1,Do.exports=Rp()),Do.exports}var Uo={exports:{}},gt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var $d;function Np(){if($d)return gt;$d=1;var o=Bo();function m(H){var b="https://react.dev/errors/"+H;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(m){console.error(m)}}return o(),Uo.exports=Np(),Uo.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Fd;function wp(){if(Fd)return Qa;Fd=1;var o=Up(),m=Bo(),h=jp();function c(e){var t="https://react.dev/errors/"+e;if(1f||(e.current=Ce[f],Ce[f]=null,f--)}function L(e,t){f++,Ce[f]=e.current,e.current=t}var ee=R(null),me=R(null),re=R(null),De=R(null);function Ne(e,t){switch(L(re,t),L(me,e),L(ee,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?ad(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=ad(t),e=id(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}W(ee),L(ee,e)}function st(){W(ee),W(me),W(re)}function zt(e){e.memoizedState!==null&&L(De,e);var t=ee.current,l=id(t,e.type);t!==l&&(L(me,e),L(ee,l))}function M(e){me.current===e&&(W(ee),W(me)),De.current===e&&(W(De),Ga._currentValue=q)}var g=Object.prototype.hasOwnProperty,p=o.unstable_scheduleCallback,B=o.unstable_cancelCallback,N=o.unstable_shouldYield,j=o.unstable_requestPaint,P=o.unstable_now,ce=o.unstable_getCurrentPriorityLevel,Se=o.unstable_ImmediatePriority,ve=o.unstable_UserBlockingPriority,xe=o.unstable_NormalPriority,Ye=o.unstable_LowPriority,cl=o.unstable_IdlePriority,gg=o.log,mg=o.unstable_setDisableYieldValue,Vn=null,St=null;function rl(e){if(typeof gg=="function"&&mg(e),St&&typeof St.setStrictMode=="function")try{St.setStrictMode(Vn,e)}catch{}}var xt=Math.clz32?Math.clz32:pg,hg=Math.log,yg=Math.LN2;function pg(e){return e>>>=0,e===0?32:31-(hg(e)/yg|0)|0}var Va=256,Ka=4194304;function wl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Za(e,t,l){var n=e.pendingLanes;if(n===0)return 0;var a=0,i=e.suspendedLanes,u=e.pingedLanes;e=e.warmLanes;var s=n&134217727;return s!==0?(n=s&~i,n!==0?a=wl(n):(u&=s,u!==0?a=wl(u):l||(l=s&~e,l!==0&&(a=wl(l))))):(s=n&~i,s!==0?a=wl(s):u!==0?a=wl(u):l||(l=n&~e,l!==0&&(a=wl(l)))),a===0?0:t!==0&&t!==a&&(t&i)===0&&(i=a&-a,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:a}function Kn(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function vg(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function qo(){var e=Va;return Va<<=1,(Va&4194048)===0&&(Va=256),e}function Yo(){var e=Ka;return Ka<<=1,(Ka&62914560)===0&&(Ka=4194304),e}function cu(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Zn(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function bg(e,t,l,n,a,i){var u=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var s=e.entanglements,r=e.expirationTimes,x=e.hiddenUpdates;for(l=u&~l;0)":-1a||r[n]!==x[a]){var U=` -`+r[n].replace(" at new "," at ");return e.displayName&&U.includes("")&&(U=U.replace("",e.displayName)),U}while(1<=n&&0<=a);break}}}finally{hu=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?un(l):""}function Tg(e){switch(e.tag){case 26:case 27:case 5:return un(e.type);case 16:return un("Lazy");case 13:return un("Suspense");case 19:return un("SuspenseList");case 0:case 15:return yu(e.type,!1);case 11:return yu(e.type.render,!1);case 1:return yu(e.type,!0);case 31:return un("Activity");default:return""}}function Wo(e){try{var t="";do t+=Tg(e),e=e.return;while(e);return t}catch(l){return` -Error generating stack: `+l.message+` -`+l.stack}}function Dt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Fo(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Cg(e){var t=Fo(e)?"checked":"value",l=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var a=l.get,i=l.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(u){n=""+u,i.call(this,u)}}),Object.defineProperty(e,t,{enumerable:l.enumerable}),{getValue:function(){return n},setValue:function(u){n=""+u},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function $a(e){e._valueTracker||(e._valueTracker=Cg(e))}function Io(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var l=t.getValue(),n="";return e&&(n=Fo(e)?e.checked?"true":"false":e.value),e=n,e!==l?(t.setValue(e),!0):!1}function Wa(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Og=/[\n"\\]/g;function Rt(e){return e.replace(Og,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function pu(e,t,l,n,a,i,u,s){e.name="",u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"?e.type=u:e.removeAttribute("type"),t!=null?u==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Dt(t)):e.value!==""+Dt(t)&&(e.value=""+Dt(t)):u!=="submit"&&u!=="reset"||e.removeAttribute("value"),t!=null?vu(e,u,Dt(t)):l!=null?vu(e,u,Dt(l)):n!=null&&e.removeAttribute("value"),a==null&&i!=null&&(e.defaultChecked=!!i),a!=null&&(e.checked=a&&typeof a!="function"&&typeof a!="symbol"),s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.name=""+Dt(s):e.removeAttribute("name")}function ec(e,t,l,n,a,i,u,s){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;l=l!=null?""+Dt(l):"",t=t!=null?""+Dt(t):l,s||t===e.value||(e.value=t),e.defaultValue=t}n=n??a,n=typeof n!="function"&&typeof n!="symbol"&&!!n,e.checked=s?e.checked:!!n,e.defaultChecked=!!n,u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"&&(e.name=u)}function vu(e,t,l){t==="number"&&Wa(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function sn(e,t,l,n){if(e=e.options,t){t={};for(var a=0;a"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),_u=!1;if(kt)try{var Wn={};Object.defineProperty(Wn,"passive",{get:function(){_u=!0}}),window.addEventListener("test",Wn,Wn),window.removeEventListener("test",Wn,Wn)}catch{_u=!1}var dl=null,Au=null,Ia=null;function sc(){if(Ia)return Ia;var e,t=Au,l=t.length,n,a="value"in dl?dl.value:dl.textContent,i=a.length;for(e=0;e=ea),gc=" ",mc=!1;function hc(e,t){switch(e){case"keyup":return tm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function yc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var fn=!1;function nm(e,t){switch(e){case"compositionend":return yc(t);case"keypress":return t.which!==32?null:(mc=!0,gc);case"textInput":return e=t.data,e===gc&&mc?null:e;default:return null}}function am(e,t){if(fn)return e==="compositionend"||!zu&&hc(e,t)?(e=sc(),Ia=Au=dl=null,fn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=n}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Ac(l)}}function Cc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Cc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Oc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Wa(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=Wa(e.document)}return t}function Uu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var dm=kt&&"documentMode"in document&&11>=document.documentMode,dn=null,Nu=null,aa=null,ju=!1;function Mc(e,t,l){var n=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;ju||dn==null||dn!==Wa(n)||(n=dn,"selectionStart"in n&&Uu(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),aa&&na(aa,n)||(aa=n,n=Xi(Nu,"onSelect"),0>=u,a-=u,$t=1<<32-xt(t)+a|l<i?i:8;var u=T.T,s={};T.T=s,bs(e,!1,t,l);try{var r=a(),x=T.S;if(x!==null&&x(s,r),r!==null&&typeof r=="object"&&typeof r.then=="function"){var U=xm(r,n);ba(e,t,U,Ot(e))}else ba(e,t,n,Ot(e))}catch(G){ba(e,t,{then:function(){},status:"rejected",reason:G},Ot())}finally{X.p=i,T.T=u}}function Cm(){}function ps(e,t,l,n){if(e.tag!==5)throw Error(c(476));var a=zr(e).queue;Mr(e,a,t,q,l===null?Cm:function(){return Dr(e),l(n)})}function zr(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:q,baseState:q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:q},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Dr(e){var t=zr(e).next.queue;ba(e,t,{},Ot())}function vs(){return dt(Ga)}function Rr(){return Fe().memoizedState}function Ur(){return Fe().memoizedState}function Om(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Ot();e=hl(l);var n=yl(t,e,l);n!==null&&(Mt(n,t,l),ga(n,t,l)),t={cache:ku()},e.payload=t;return}t=t.return}}function Mm(e,t,l){var n=Ot();l={lane:n,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},_i(e)?jr(t,l):(l=Bu(e,t,l,n),l!==null&&(Mt(l,e,n),wr(l,t,n)))}function Nr(e,t,l){var n=Ot();ba(e,t,l,n)}function ba(e,t,l,n){var a={lane:n,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(_i(e))jr(t,a);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var u=t.lastRenderedState,s=i(u,l);if(a.hasEagerState=!0,a.eagerState=s,Et(s,u))return ui(e,t,a,0),Xe===null&&ii(),!1}catch{}finally{}if(l=Bu(e,t,a,n),l!==null)return Mt(l,e,n),wr(l,t,n),!0}return!1}function bs(e,t,l,n){if(n={lane:2,revertLane:Ws(),action:n,hasEagerState:!1,eagerState:null,next:null},_i(e)){if(t)throw Error(c(479))}else t=Bu(e,l,n,2),t!==null&&Mt(t,e,2)}function _i(e){var t=e.alternate;return e===Ee||t!==null&&t===Ee}function jr(e,t){En=pi=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function wr(e,t,l){if((l&4194048)!==0){var n=t.lanes;n&=e.pendingLanes,l|=n,t.lanes=l,Xo(e,l)}}var Ai={readContext:dt,use:bi,useCallback:Je,useContext:Je,useEffect:Je,useImperativeHandle:Je,useLayoutEffect:Je,useInsertionEffect:Je,useMemo:Je,useReducer:Je,useRef:Je,useState:Je,useDebugValue:Je,useDeferredValue:Je,useTransition:Je,useSyncExternalStore:Je,useId:Je,useHostTransitionStatus:Je,useFormState:Je,useActionState:Je,useOptimistic:Je,useMemoCache:Je,useCacheRefresh:Je},Hr={readContext:dt,use:bi,useCallback:function(e,t){return pt().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:br,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Ei(4194308,4,_r.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Ei(4194308,4,e,t)},useInsertionEffect:function(e,t){Ei(4,2,e,t)},useMemo:function(e,t){var l=pt();t=t===void 0?null:t;var n=e();if(kl){rl(!0);try{e()}finally{rl(!1)}}return l.memoizedState=[n,t],n},useReducer:function(e,t,l){var n=pt();if(l!==void 0){var a=l(t);if(kl){rl(!0);try{l(t)}finally{rl(!1)}}}else a=t;return n.memoizedState=n.baseState=a,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:a},n.queue=e,e=e.dispatch=Mm.bind(null,Ee,e),[n.memoizedState,e]},useRef:function(e){var t=pt();return e={current:e},t.memoizedState=e},useState:function(e){e=gs(e);var t=e.queue,l=Nr.bind(null,Ee,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:hs,useDeferredValue:function(e,t){var l=pt();return ys(l,e,t)},useTransition:function(){var e=gs(!1);return e=Mr.bind(null,Ee,e.queue,!0,!1),pt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var n=Ee,a=pt();if(je){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Xe===null)throw Error(c(349));(Re&124)!==0||nr(n,t,l)}a.memoizedState=l;var i={value:l,getSnapshot:t};return a.queue=i,br(ir.bind(null,n,i,e),[e]),n.flags|=2048,An(9,xi(),ar.bind(null,n,i,l,t),null),l},useId:function(){var e=pt(),t=Xe.identifierPrefix;if(je){var l=Wt,n=$t;l=(n&~(1<<32-xt(n)-1)).toString(32)+l,t="«"+t+"R"+l,l=vi++,0ge?(it=se,se=null):it=se.sibling;var Ue=E(v,se,S[ge],w);if(Ue===null){se===null&&(se=it);break}e&&se&&Ue.alternate===null&&t(v,se),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue,se=it}if(ge===S.length)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;gege?(it=se,se=null):it=se.sibling;var Nl=E(v,se,Ue.value,w);if(Nl===null){se===null&&(se=it);break}e&&se&&Nl.alternate===null&&t(v,se),d=i(Nl,d,ge),Oe===null?le=Nl:Oe.sibling=Nl,Oe=Nl,se=it}if(Ue.done)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;!Ue.done;ge++,Ue=S.next())Ue=G(v,Ue.value,w),Ue!==null&&(d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return je&&Ll(v,ge),le}for(se=n(se);!Ue.done;ge++,Ue=S.next())Ue=_(se,v,ge,Ue.value,w),Ue!==null&&(e&&Ue.alternate!==null&&se.delete(Ue.key===null?ge:Ue.key),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return e&&se.forEach(function(Dh){return t(v,Dh)}),je&&Ll(v,ge),le}function qe(v,d,S,w){if(typeof S=="object"&&S!==null&&S.type===ie&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case $:e:{for(var le=S.key;d!==null;){if(d.key===le){if(le=S.type,le===ie){if(d.tag===7){l(v,d.sibling),w=a(d,S.props.children),w.return=v,v=w;break e}}else if(d.elementType===le||typeof le=="object"&&le!==null&&le.$$typeof===Y&&Br(le)===d.type){l(v,d.sibling),w=a(d,S.props),xa(w,S),w.return=v,v=w;break e}l(v,d);break}else t(v,d);d=d.sibling}S.type===ie?(w=ql(S.props.children,v.mode,w,S.key),w.return=v,v=w):(w=oi(S.type,S.key,S.props,null,v.mode,w),xa(w,S),w.return=v,v=w)}return u(v);case ue:e:{for(le=S.key;d!==null;){if(d.key===le)if(d.tag===4&&d.stateNode.containerInfo===S.containerInfo&&d.stateNode.implementation===S.implementation){l(v,d.sibling),w=a(d,S.children||[]),w.return=v,v=w;break e}else{l(v,d);break}else t(v,d);d=d.sibling}w=Yu(S,v.mode,w),w.return=v,v=w}return u(v);case Y:return le=S._init,S=le(S._payload),qe(v,d,S,w)}if(we(S))return he(v,d,S,w);if(Te(S)){if(le=Te(S),typeof le!="function")throw Error(c(150));return S=le.call(S),fe(v,d,S,w)}if(typeof S.then=="function")return qe(v,d,Ti(S),w);if(S.$$typeof===V)return qe(v,d,di(v,S),w);Ci(v,S)}return typeof S=="string"&&S!==""||typeof S=="number"||typeof S=="bigint"?(S=""+S,d!==null&&d.tag===6?(l(v,d.sibling),w=a(d,S),w.return=v,v=w):(l(v,d),w=qu(S,v.mode,w),w.return=v,v=w),u(v)):l(v,d)}return function(v,d,S,w){try{Sa=0;var le=qe(v,d,S,w);return Tn=null,le}catch(se){if(se===fa||se===mi)throw se;var Oe=_t(29,se,null,v.mode);return Oe.lanes=w,Oe.return=v,Oe}finally{}}}var Cn=Pr(!0),qr=Pr(!1),Ht=R(null),Xt=null;function vl(e){var t=e.alternate;L(et,et.current&1),L(Ht,e),Xt===null&&(t===null||xn.current!==null||t.memoizedState!==null)&&(Xt=e)}function Yr(e){if(e.tag===22){if(L(et,et.current),L(Ht,e),Xt===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(Xt=e)}}else bl()}function bl(){L(et,et.current),L(Ht,Ht.current)}function tl(e){W(Ht),Xt===e&&(Xt=null),W(et)}var et=R(0);function Oi(e){for(var t=e;t!==null;){if(t.tag===13){var l=t.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||co(l)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function Ss(e,t,l,n){t=e.memoizedState,l=l(n,t),l=l==null?t:O({},t,l),e.memoizedState=l,e.lanes===0&&(e.updateQueue.baseState=l)}var xs={enqueueSetState:function(e,t,l){e=e._reactInternals;var n=Ot(),a=hl(n);a.payload=t,l!=null&&(a.callback=l),t=yl(e,a,n),t!==null&&(Mt(t,e,n),ga(t,e,n))},enqueueReplaceState:function(e,t,l){e=e._reactInternals;var n=Ot(),a=hl(n);a.tag=1,a.payload=t,l!=null&&(a.callback=l),t=yl(e,a,n),t!==null&&(Mt(t,e,n),ga(t,e,n))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var l=Ot(),n=hl(l);n.tag=2,t!=null&&(n.callback=t),t=yl(e,n,l),t!==null&&(Mt(t,e,l),ga(t,e,l))}};function Lr(e,t,l,n,a,i,u){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(n,i,u):t.prototype&&t.prototype.isPureReactComponent?!na(l,n)||!na(a,i):!0}function Xr(e,t,l,n){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(l,n),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(l,n),t.state!==e&&xs.enqueueReplaceState(t,t.state,null)}function Jl(e,t){var l=t;if("ref"in t){l={};for(var n in t)n!=="ref"&&(l[n]=t[n])}if(e=e.defaultProps){l===t&&(l=O({},l));for(var a in e)l[a]===void 0&&(l[a]=e[a])}return l}var Mi=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Qr(e){Mi(e)}function Vr(e){console.error(e)}function Kr(e){Mi(e)}function zi(e,t){try{var l=e.onUncaughtError;l(t.value,{componentStack:t.stack})}catch(n){setTimeout(function(){throw n})}}function Zr(e,t,l){try{var n=e.onCaughtError;n(l.value,{componentStack:l.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(a){setTimeout(function(){throw a})}}function Es(e,t,l){return l=hl(l),l.tag=3,l.payload={element:null},l.callback=function(){zi(e,t)},l}function kr(e){return e=hl(e),e.tag=3,e}function Jr(e,t,l,n){var a=l.type.getDerivedStateFromError;if(typeof a=="function"){var i=n.value;e.payload=function(){return a(i)},e.callback=function(){Zr(t,l,n)}}var u=l.stateNode;u!==null&&typeof u.componentDidCatch=="function"&&(e.callback=function(){Zr(t,l,n),typeof a!="function"&&(Tl===null?Tl=new Set([this]):Tl.add(this));var s=n.stack;this.componentDidCatch(n.value,{componentStack:s!==null?s:""})})}function Dm(e,t,l,n,a){if(l.flags|=32768,n!==null&&typeof n=="object"&&typeof n.then=="function"){if(t=l.alternate,t!==null&&oa(t,l,a,!0),l=Ht.current,l!==null){switch(l.tag){case 13:return Xt===null?Ks():l.alternate===null&&ke===0&&(ke=3),l.flags&=-257,l.flags|=65536,l.lanes=a,n===Wu?l.flags|=16384:(t=l.updateQueue,t===null?l.updateQueue=new Set([n]):t.add(n),ks(e,n,a)),!1;case 22:return l.flags|=65536,n===Wu?l.flags|=16384:(t=l.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([n])},l.updateQueue=t):(l=t.retryQueue,l===null?t.retryQueue=new Set([n]):l.add(n)),ks(e,n,a)),!1}throw Error(c(435,l.tag))}return ks(e,n,a),Ks(),!1}if(je)return t=Ht.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=a,n!==Qu&&(e=Error(c(422),{cause:n}),sa(Ut(e,l)))):(n!==Qu&&(t=Error(c(423),{cause:n}),sa(Ut(t,l))),e=e.current.alternate,e.flags|=65536,a&=-a,e.lanes|=a,n=Ut(n,l),a=Es(e.stateNode,n,a),es(e,a),ke!==4&&(ke=2)),!1;var i=Error(c(520),{cause:n});if(i=Ut(i,l),Ma===null?Ma=[i]:Ma.push(i),ke!==4&&(ke=2),t===null)return!0;n=Ut(n,l),l=t;do{switch(l.tag){case 3:return l.flags|=65536,e=a&-a,l.lanes|=e,e=Es(l.stateNode,n,e),es(l,e),!1;case 1:if(t=l.type,i=l.stateNode,(l.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(Tl===null||!Tl.has(i))))return l.flags|=65536,a&=-a,l.lanes|=a,a=kr(a),Jr(a,e,l,n),es(l,a),!1}l=l.return}while(l!==null);return!1}var $r=Error(c(461)),nt=!1;function ot(e,t,l,n){t.child=e===null?qr(t,null,l,n):Cn(t,e.child,l,n)}function Wr(e,t,l,n,a){l=l.render;var i=t.ref;if("ref"in n){var u={};for(var s in n)s!=="ref"&&(u[s]=n[s])}else u=n;return Kl(t),n=is(e,t,l,u,i,a),s=us(),e!==null&&!nt?(ss(e,t,a),ll(e,t,a)):(je&&s&&Lu(t),t.flags|=1,ot(e,t,n,a),t.child)}function Fr(e,t,l,n,a){if(e===null){var i=l.type;return typeof i=="function"&&!Pu(i)&&i.defaultProps===void 0&&l.compare===null?(t.tag=15,t.type=i,Ir(e,t,i,n,a)):(e=oi(l.type,null,n,t,t.mode,a),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!Ds(e,a)){var u=i.memoizedProps;if(l=l.compare,l=l!==null?l:na,l(u,n)&&e.ref===t.ref)return ll(e,t,a)}return t.flags|=1,e=Jt(i,n),e.ref=t.ref,e.return=t,t.child=e}function Ir(e,t,l,n,a){if(e!==null){var i=e.memoizedProps;if(na(i,n)&&e.ref===t.ref)if(nt=!1,t.pendingProps=n=i,Ds(e,a))(e.flags&131072)!==0&&(nt=!0);else return t.lanes=e.lanes,ll(e,t,a)}return _s(e,t,l,n,a)}function ef(e,t,l){var n=t.pendingProps,a=n.children,i=e!==null?e.memoizedState:null;if(n.mode==="hidden"){if((t.flags&128)!==0){if(n=i!==null?i.baseLanes|l:l,e!==null){for(a=t.child=e.child,i=0;a!==null;)i=i|a.lanes|a.childLanes,a=a.sibling;t.childLanes=i&~n}else t.childLanes=0,t.child=null;return tf(e,t,n,l)}if((l&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&gi(t,i!==null?i.cachePool:null),i!==null?Ic(t,i):ls(),Yr(t);else return t.lanes=t.childLanes=536870912,tf(e,t,i!==null?i.baseLanes|l:l,l)}else i!==null?(gi(t,i.cachePool),Ic(t,i),bl(),t.memoizedState=null):(e!==null&&gi(t,null),ls(),bl());return ot(e,t,a,l),t.child}function tf(e,t,l,n){var a=$u();return a=a===null?null:{parent:Ie._currentValue,pool:a},t.memoizedState={baseLanes:l,cachePool:a},e!==null&&gi(t,null),ls(),Yr(t),e!==null&&oa(e,t,n,!0),null}function Di(e,t){var l=t.ref;if(l===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(c(284));(e===null||e.ref!==l)&&(t.flags|=4194816)}}function _s(e,t,l,n,a){return Kl(t),l=is(e,t,l,n,void 0,a),n=us(),e!==null&&!nt?(ss(e,t,a),ll(e,t,a)):(je&&n&&Lu(t),t.flags|=1,ot(e,t,l,a),t.child)}function lf(e,t,l,n,a,i){return Kl(t),t.updateQueue=null,l=tr(t,n,l,a),er(e),n=us(),e!==null&&!nt?(ss(e,t,i),ll(e,t,i)):(je&&n&&Lu(t),t.flags|=1,ot(e,t,l,i),t.child)}function nf(e,t,l,n,a){if(Kl(t),t.stateNode===null){var i=yn,u=l.contextType;typeof u=="object"&&u!==null&&(i=dt(u)),i=new l(n,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=xs,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=n,i.state=t.memoizedState,i.refs={},Fu(t),u=l.contextType,i.context=typeof u=="object"&&u!==null?dt(u):yn,i.state=t.memoizedState,u=l.getDerivedStateFromProps,typeof u=="function"&&(Ss(t,l,u,n),i.state=t.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(u=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),u!==i.state&&xs.enqueueReplaceState(i,i.state,null),ha(t,n,i,a),ma(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!0}else if(e===null){i=t.stateNode;var s=t.memoizedProps,r=Jl(l,s);i.props=r;var x=i.context,U=l.contextType;u=yn,typeof U=="object"&&U!==null&&(u=dt(U));var G=l.getDerivedStateFromProps;U=typeof G=="function"||typeof i.getSnapshotBeforeUpdate=="function",s=t.pendingProps!==s,U||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s||x!==u)&&Xr(t,i,n,u),ml=!1;var E=t.memoizedState;i.state=E,ha(t,n,i,a),ma(),x=t.memoizedState,s||E!==x||ml?(typeof G=="function"&&(Ss(t,l,G,n),x=t.memoizedState),(r=ml||Lr(t,l,r,n,E,x,u))?(U||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=x),i.props=n,i.state=x,i.context=u,n=r):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,Iu(e,t),u=t.memoizedProps,U=Jl(l,u),i.props=U,G=t.pendingProps,E=i.context,x=l.contextType,r=yn,typeof x=="object"&&x!==null&&(r=dt(x)),s=l.getDerivedStateFromProps,(x=typeof s=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==G||E!==r)&&Xr(t,i,n,r),ml=!1,E=t.memoizedState,i.state=E,ha(t,n,i,a),ma();var _=t.memoizedState;u!==G||E!==_||ml||e!==null&&e.dependencies!==null&&fi(e.dependencies)?(typeof s=="function"&&(Ss(t,l,s,n),_=t.memoizedState),(U=ml||Lr(t,l,U,n,E,_,r)||e!==null&&e.dependencies!==null&&fi(e.dependencies))?(x||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(n,_,r),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(n,_,r)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),t.memoizedProps=n,t.memoizedState=_),i.props=n,i.state=_,i.context=r,n=U):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),n=!1)}return i=n,Di(e,t),n=(t.flags&128)!==0,i||n?(i=t.stateNode,l=n&&typeof l.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&n?(t.child=Cn(t,e.child,null,a),t.child=Cn(t,null,l,a)):ot(e,t,l,a),t.memoizedState=i.state,e=t.child):e=ll(e,t,a),e}function af(e,t,l,n){return ua(),t.flags|=256,ot(e,t,l,n),t.child}var As={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ts(e){return{baseLanes:e,cachePool:Vc()}}function Cs(e,t,l){return e=e!==null?e.childLanes&~l:0,t&&(e|=Gt),e}function uf(e,t,l){var n=t.pendingProps,a=!1,i=(t.flags&128)!==0,u;if((u=i)||(u=e!==null&&e.memoizedState===null?!1:(et.current&2)!==0),u&&(a=!0,t.flags&=-129),u=(t.flags&32)!==0,t.flags&=-33,e===null){if(je){if(a?vl(t):bl(),je){var s=Ze,r;if(r=s){e:{for(r=s,s=Lt;r.nodeType!==8;){if(!s){s=null;break e}if(r=Yt(r.nextSibling),r===null){s=null;break e}}s=r}s!==null?(t.memoizedState={dehydrated:s,treeContext:Yl!==null?{id:$t,overflow:Wt}:null,retryLane:536870912,hydrationErrors:null},r=_t(18,null,null,0),r.stateNode=s,r.return=t,t.child=r,mt=t,Ze=null,r=!0):r=!1}r||Ql(t)}if(s=t.memoizedState,s!==null&&(s=s.dehydrated,s!==null))return co(s)?t.lanes=32:t.lanes=536870912,null;tl(t)}return s=n.children,n=n.fallback,a?(bl(),a=t.mode,s=Ri({mode:"hidden",children:s},a),n=ql(n,a,l,null),s.return=t,n.return=t,s.sibling=n,t.child=s,a=t.child,a.memoizedState=Ts(l),a.childLanes=Cs(e,u,l),t.memoizedState=As,n):(vl(t),Os(t,s))}if(r=e.memoizedState,r!==null&&(s=r.dehydrated,s!==null)){if(i)t.flags&256?(vl(t),t.flags&=-257,t=Ms(e,t,l)):t.memoizedState!==null?(bl(),t.child=e.child,t.flags|=128,t=null):(bl(),a=n.fallback,s=t.mode,n=Ri({mode:"visible",children:n.children},s),a=ql(a,s,l,null),a.flags|=2,n.return=t,a.return=t,n.sibling=a,t.child=n,Cn(t,e.child,null,l),n=t.child,n.memoizedState=Ts(l),n.childLanes=Cs(e,u,l),t.memoizedState=As,t=a);else if(vl(t),co(s)){if(u=s.nextSibling&&s.nextSibling.dataset,u)var x=u.dgst;u=x,n=Error(c(419)),n.stack="",n.digest=u,sa({value:n,source:null,stack:null}),t=Ms(e,t,l)}else if(nt||oa(e,t,l,!1),u=(l&e.childLanes)!==0,nt||u){if(u=Xe,u!==null&&(n=l&-l,n=(n&42)!==0?1:ru(n),n=(n&(u.suspendedLanes|l))!==0?0:n,n!==0&&n!==r.retryLane))throw r.retryLane=n,hn(e,n),Mt(u,e,n),$r;s.data==="$?"||Ks(),t=Ms(e,t,l)}else s.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=r.treeContext,Ze=Yt(s.nextSibling),mt=t,je=!0,Xl=null,Lt=!1,e!==null&&(jt[wt++]=$t,jt[wt++]=Wt,jt[wt++]=Yl,$t=e.id,Wt=e.overflow,Yl=t),t=Os(t,n.children),t.flags|=4096);return t}return a?(bl(),a=n.fallback,s=t.mode,r=e.child,x=r.sibling,n=Jt(r,{mode:"hidden",children:n.children}),n.subtreeFlags=r.subtreeFlags&65011712,x!==null?a=Jt(x,a):(a=ql(a,s,l,null),a.flags|=2),a.return=t,n.return=t,n.sibling=a,t.child=n,n=a,a=t.child,s=e.child.memoizedState,s===null?s=Ts(l):(r=s.cachePool,r!==null?(x=Ie._currentValue,r=r.parent!==x?{parent:x,pool:x}:r):r=Vc(),s={baseLanes:s.baseLanes|l,cachePool:r}),a.memoizedState=s,a.childLanes=Cs(e,u,l),t.memoizedState=As,n):(vl(t),l=e.child,e=l.sibling,l=Jt(l,{mode:"visible",children:n.children}),l.return=t,l.sibling=null,e!==null&&(u=t.deletions,u===null?(t.deletions=[e],t.flags|=16):u.push(e)),t.child=l,t.memoizedState=null,l)}function Os(e,t){return t=Ri({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Ri(e,t){return e=_t(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function Ms(e,t,l){return Cn(t,e.child,null,l),e=Os(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function sf(e,t,l){e.lanes|=t;var n=e.alternate;n!==null&&(n.lanes|=t),Ku(e.return,t,l)}function zs(e,t,l,n,a){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:n,tail:l,tailMode:a}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=n,i.tail=l,i.tailMode=a)}function of(e,t,l){var n=t.pendingProps,a=n.revealOrder,i=n.tail;if(ot(e,t,n.children,l),n=et.current,(n&2)!==0)n=n&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&sf(e,l,t);else if(e.tag===19)sf(e,l,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}n&=1}switch(L(et,n),a){case"forwards":for(l=t.child,a=null;l!==null;)e=l.alternate,e!==null&&Oi(e)===null&&(a=l),l=l.sibling;l=a,l===null?(a=t.child,t.child=null):(a=l.sibling,l.sibling=null),zs(t,!1,a,l,i);break;case"backwards":for(l=null,a=t.child,t.child=null;a!==null;){if(e=a.alternate,e!==null&&Oi(e)===null){t.child=a;break}e=a.sibling,a.sibling=l,l=a,a=e}zs(t,!0,l,null,i);break;case"together":zs(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function ll(e,t,l){if(e!==null&&(t.dependencies=e.dependencies),Al|=t.lanes,(l&t.childLanes)===0)if(e!==null){if(oa(e,t,l,!1),(l&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,l=Jt(e,e.pendingProps),t.child=l,l.return=t;e.sibling!==null;)e=e.sibling,l=l.sibling=Jt(e,e.pendingProps),l.return=t;l.sibling=null}return t.child}function Ds(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&fi(e)))}function Rm(e,t,l){switch(t.tag){case 3:Ne(t,t.stateNode.containerInfo),gl(t,Ie,e.memoizedState.cache),ua();break;case 27:case 5:zt(t);break;case 4:Ne(t,t.stateNode.containerInfo);break;case 10:gl(t,t.type,t.memoizedProps.value);break;case 13:var n=t.memoizedState;if(n!==null)return n.dehydrated!==null?(vl(t),t.flags|=128,null):(l&t.child.childLanes)!==0?uf(e,t,l):(vl(t),e=ll(e,t,l),e!==null?e.sibling:null);vl(t);break;case 19:var a=(e.flags&128)!==0;if(n=(l&t.childLanes)!==0,n||(oa(e,t,l,!1),n=(l&t.childLanes)!==0),a){if(n)return of(e,t,l);t.flags|=128}if(a=t.memoizedState,a!==null&&(a.rendering=null,a.tail=null,a.lastEffect=null),L(et,et.current),n)break;return null;case 22:case 23:return t.lanes=0,ef(e,t,l);case 24:gl(t,Ie,e.memoizedState.cache)}return ll(e,t,l)}function cf(e,t,l){if(e!==null)if(e.memoizedProps!==t.pendingProps)nt=!0;else{if(!Ds(e,l)&&(t.flags&128)===0)return nt=!1,Rm(e,t,l);nt=(e.flags&131072)!==0}else nt=!1,je&&(t.flags&1048576)!==0&&Bc(t,ri,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var n=t.elementType,a=n._init;if(n=a(n._payload),t.type=n,typeof n=="function")Pu(n)?(e=Jl(n,e),t.tag=1,t=nf(null,t,n,e,l)):(t.tag=0,t=_s(null,t,n,e,l));else{if(n!=null){if(a=n.$$typeof,a===de){t.tag=11,t=Wr(null,t,n,e,l);break e}else if(a===J){t.tag=14,t=Fr(null,t,n,e,l);break e}}throw t=Ke(n)||n,Error(c(306,t,""))}}return t;case 0:return _s(e,t,t.type,t.pendingProps,l);case 1:return n=t.type,a=Jl(n,t.pendingProps),nf(e,t,n,a,l);case 3:e:{if(Ne(t,t.stateNode.containerInfo),e===null)throw Error(c(387));n=t.pendingProps;var i=t.memoizedState;a=i.element,Iu(e,t),ha(t,n,null,l);var u=t.memoizedState;if(n=u.cache,gl(t,Ie,n),n!==i.cache&&Zu(t,[Ie],l,!0),ma(),n=u.element,i.isDehydrated)if(i={element:n,isDehydrated:!1,cache:u.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=af(e,t,n,l);break e}else if(n!==a){a=Ut(Error(c(424)),t),sa(a),t=af(e,t,n,l);break e}else{switch(e=t.stateNode.containerInfo,e.nodeType){case 9:e=e.body;break;default:e=e.nodeName==="HTML"?e.ownerDocument.body:e}for(Ze=Yt(e.firstChild),mt=t,je=!0,Xl=null,Lt=!0,l=qr(t,null,n,l),t.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(ua(),n===a){t=ll(e,t,l);break e}ot(e,t,n,l)}t=t.child}return t;case 26:return Di(e,t),e===null?(l=gd(t.type,null,t.pendingProps,null))?t.memoizedState=l:je||(l=t.type,e=t.pendingProps,n=Vi(re.current).createElement(l),n[ft]=t,n[ht]=e,rt(n,l,e),lt(n),t.stateNode=n):t.memoizedState=gd(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return zt(t),e===null&&je&&(n=t.stateNode=rd(t.type,t.pendingProps,re.current),mt=t,Lt=!0,a=Ze,Ml(t.type)?(ro=a,Ze=Yt(n.firstChild)):Ze=a),ot(e,t,t.pendingProps.children,l),Di(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&je&&((a=n=Ze)&&(n=ih(n,t.type,t.pendingProps,Lt),n!==null?(t.stateNode=n,mt=t,Ze=Yt(n.firstChild),Lt=!1,a=!0):a=!1),a||Ql(t)),zt(t),a=t.type,i=t.pendingProps,u=e!==null?e.memoizedProps:null,n=i.children,uo(a,i)?n=null:u!==null&&uo(a,u)&&(t.flags|=32),t.memoizedState!==null&&(a=is(e,t,_m,null,null,l),Ga._currentValue=a),Di(e,t),ot(e,t,n,l),t.child;case 6:return e===null&&je&&((e=l=Ze)&&(l=uh(l,t.pendingProps,Lt),l!==null?(t.stateNode=l,mt=t,Ze=null,e=!0):e=!1),e||Ql(t)),null;case 13:return uf(e,t,l);case 4:return Ne(t,t.stateNode.containerInfo),n=t.pendingProps,e===null?t.child=Cn(t,null,n,l):ot(e,t,n,l),t.child;case 11:return Wr(e,t,t.type,t.pendingProps,l);case 7:return ot(e,t,t.pendingProps,l),t.child;case 8:return ot(e,t,t.pendingProps.children,l),t.child;case 12:return ot(e,t,t.pendingProps.children,l),t.child;case 10:return n=t.pendingProps,gl(t,t.type,n.value),ot(e,t,n.children,l),t.child;case 9:return a=t.type._context,n=t.pendingProps.children,Kl(t),a=dt(a),n=n(a),t.flags|=1,ot(e,t,n,l),t.child;case 14:return Fr(e,t,t.type,t.pendingProps,l);case 15:return Ir(e,t,t.type,t.pendingProps,l);case 19:return of(e,t,l);case 31:return n=t.pendingProps,l=t.mode,n={mode:n.mode,children:n.children},e===null?(l=Ri(n,l),l.ref=t.ref,t.child=l,l.return=t,t=l):(l=Jt(e.child,n),l.ref=t.ref,t.child=l,l.return=t,t=l),t;case 22:return ef(e,t,l);case 24:return Kl(t),n=dt(Ie),e===null?(a=$u(),a===null&&(a=Xe,i=ku(),a.pooledCache=i,i.refCount++,i!==null&&(a.pooledCacheLanes|=l),a=i),t.memoizedState={parent:n,cache:a},Fu(t),gl(t,Ie,a)):((e.lanes&l)!==0&&(Iu(e,t),ha(t,null,null,l),ma()),a=e.memoizedState,i=t.memoizedState,a.parent!==n?(a={parent:n,cache:n},t.memoizedState=a,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=a),gl(t,Ie,n)):(n=i.cache,gl(t,Ie,n),n!==a.cache&&Zu(t,[Ie],l,!0))),ot(e,t,t.pendingProps.children,l),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function nl(e){e.flags|=4}function rf(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!vd(t)){if(t=Ht.current,t!==null&&((Re&4194048)===Re?Xt!==null:(Re&62914560)!==Re&&(Re&536870912)===0||t!==Xt))throw da=Wu,Kc;e.flags|=8192}}function Ui(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?Yo():536870912,e.lanes|=t,Dn|=t)}function Ea(e,t){if(!je)switch(e.tailMode){case"hidden":t=e.tail;for(var l=null;t!==null;)t.alternate!==null&&(l=t),t=t.sibling;l===null?e.tail=null:l.sibling=null;break;case"collapsed":l=e.tail;for(var n=null;l!==null;)l.alternate!==null&&(n=l),l=l.sibling;n===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:n.sibling=null}}function Ve(e){var t=e.alternate!==null&&e.alternate.child===e.child,l=0,n=0;if(t)for(var a=e.child;a!==null;)l|=a.lanes|a.childLanes,n|=a.subtreeFlags&65011712,n|=a.flags&65011712,a.return=e,a=a.sibling;else for(a=e.child;a!==null;)l|=a.lanes|a.childLanes,n|=a.subtreeFlags,n|=a.flags,a.return=e,a=a.sibling;return e.subtreeFlags|=n,e.childLanes=l,t}function Um(e,t,l){var n=t.pendingProps;switch(Xu(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ve(t),null;case 1:return Ve(t),null;case 3:return l=t.stateNode,n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),It(Ie),st(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(e===null||e.child===null)&&(ia(t)?nl(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,Yc())),Ve(t),null;case 26:return l=t.memoizedState,e===null?(nl(t),l!==null?(Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217)):l?l!==e.memoizedState?(nl(t),Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217):(e.memoizedProps!==n&&nl(t),Ve(t),t.flags&=-16777217),null;case 27:M(t),l=re.current;var a=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==n&&nl(t);else{if(!n){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}e=ee.current,ia(t)?Pc(t):(e=rd(a,n,l),t.stateNode=e,nl(t))}return Ve(t),null;case 5:if(M(t),l=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==n&&nl(t);else{if(!n){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}if(e=ee.current,ia(t))Pc(t);else{switch(a=Vi(re.current),e){case 1:e=a.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:e=a.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":e=a.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":e=a.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":e=a.createElement("div"),e.innerHTML=" + diff --git a/chrome-extension/src/background/ai-api-client.ts b/chrome-extension/src/background/ai-api-client.ts index 6ab040f7..98c1442f 100644 --- a/chrome-extension/src/background/ai-api-client.ts +++ b/chrome-extension/src/background/ai-api-client.ts @@ -26,7 +26,7 @@ export interface AiModelResponse { } // Доступные модели -export type ModelAlias = 'gemini-flash' | 'gemini-pro' | 'gpt-3.5-turbo' | 'gpt-4'; +export type ModelAlias = 'gemini-flash-lite' | 'gemini-pro' | 'gpt-3.5-turbo' | 'gpt-4'; // Конфигурация модели interface ModelConfig { @@ -251,7 +251,7 @@ function handleAiError(error: any, context: any): never { // Поддерживаемые модели и их конфигурации с типизированными индексами const MODEL_CONFIGS: Record = { - 'gemini-flash': { + 'gemini-flash-lite': { provider: 'google', //model_name: 'gemini-2.5-flash-lite:generateContent', model_name: 'gemini-flash-lite-latest:generateContent', diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index 53b80863..5a4c2234 100644 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -239,7 +239,7 @@ const sendHtmlDirectly = async ( // Получить API ключ для передачи в offscreen let geminiApiKey: string | undefined; try { - geminiApiKey = await getApiKeyForModel('gemini-flash') || undefined; + geminiApiKey = await getApiKeyForModel('gemini-flash-lite') || undefined; console.log('[background][DIRECT_TRANSMISSION] ✅ API key retrieved for workflow'); } catch (keyError) { console.warn('[background][DIRECT_TRANSMISSION] ⚠️ Failed to get API key:', keyError); @@ -850,7 +850,7 @@ async function processRecoveredAssembledTransfer(msg: any, transfer: any): Promi // Получить API ключ для Gemini и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeMessage.geminiApiKey = geminiApiKey; console.log('[RECOVERY_PROCESSING] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { @@ -1323,7 +1323,7 @@ async function executeWorkflowInOffscreen( if (!geminiApiKey) { try { console.log('[WORKFLOW_EXECUTION] 🔑 Getting Gemini API key...'); - geminiApiKey = await getApiKeyForModel('gemini-flash'); + geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); console.log('[WORKFLOW_EXECUTION] ✅ Gemini API key retrieved successfully'); } catch (keyError) { console.error('[WORKFLOW_EXECUTION] ❌ Failed to get Gemini API key:', keyError); @@ -2032,7 +2032,7 @@ chrome.runtime.onMessage.addListener( // Получить API ключ и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeWorkflowMessage.geminiApiKey = geminiApiKey; console.log('[background][HTML_ASSEMBLED] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { @@ -2900,7 +2900,7 @@ async function handleMessage(message: any, sender: any): Promise { // Получить API ключ и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeWorkflowMessage.geminiApiKey = geminiApiKey; console.log('[background][PORT][HTML_ASSEMBLED] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index ef202082..803d94a8 100644 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.820", + "version": "1.0.825", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/chrome-extension/test-chunk-transmission.html b/chrome-extension/test-chunk-transmission.html deleted file mode 100644 index b0c8f6ee..00000000 --- a/chrome-extension/test-chunk-transmission.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - Chunk Transmission Race Condition Test - - - -
-

🔧 Race Condition Protection Test Suite

- -
-
-

Transmission Status

- 0 / 40 -
-
-
-
-

Ready to start testing

-
- -
-
- 40 - Expected Chunks -
-
- 0 KB - Transferred Size -
-
- 0 KB/s - Transfer Speed -
-
- 0s - Elapsed Time -
-
- -
- - - - -
- -
-

📋 Test Results & Diagnostics

-
-
-
- - - - \ No newline at end of file diff --git a/chrome-extension/test-communication.js b/chrome-extension/test-communication.js deleted file mode 100644 index 0c070d5c..00000000 --- a/chrome-extension/test-communication.js +++ /dev/null @@ -1,38 +0,0 @@ -// Тестовый скрипт для проверки messaging системы -// Запуск: node test-communication.js - -const testMessagingSystem = async () => { - console.log('🧪 Тестирование messaging системы после исправлений...\n'); - - // Тест 1: Проверка PING/PONG механизма - console.log('📡 Тест 1: Проверка механизма PING/PONG'); - console.log('✅ PING/PONG механизм реализован в background.js и SidePanel.tsx\n'); - - // Тест 2: Проверка увеличенных таймаутов - console.log('⏰ Тест 2: Проверка увеличенных таймаутов'); - console.log('✅ Таймауты увеличены: 3000ms → 5000ms → 8000ms\n'); - - // Тест 3: Проверка синхронной обработки в background - console.log('🔄 Тест 3: Проверка синхронной обработки'); - console.log('✅ Все асинхронные обработчики заменены на синхронные\n'); - - // Тест 4: Проверка heartbeat механизма - console.log('💓 Тест 4: Проверка heartbeat механизма'); - console.log('✅ Heartbeat реализован с интервалом 10 секунд\n'); - - // Тест 5: Проверка retry логики с проверкой соединения - console.log('🔁 Тест 5: Проверка retry логики'); - console.log('✅ Retry логика включает проверку соединения перед каждым запросом\n'); - - console.log('🎉 Все тесты пройдены! Система готова к использованию.'); - console.log('\n📋 Ключевые исправления:'); - console.log('1. ✅ Увеличены таймауты в SidePanel.tsx'); - console.log('2. ✅ Исправлена асинхронная обработка в background.js'); - console.log('3. ✅ Добавлен PING/PONG механизм'); - console.log('4. ✅ Реализован heartbeat механизм'); - console.log('5. ✅ Улучшена retry логика с проверкой соединения'); - - console.log('\n🚀 Система должна теперь корректно обрабатывать запросы и избегать undefined ответов.'); -}; - -testMessagingSystem().catch(console.error); \ No newline at end of file diff --git a/chrome-extension/test-direct-page.html b/chrome-extension/test-direct-page.html deleted file mode 100644 index b89ccb27..00000000 --- a/chrome-extension/test-direct-page.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - Pyodide Direct Test - - - -
-

🧪 Прямой тест Pyodide endpoint

- -
-

Что это тестирует:

-
    -
  • Отправку сообщения TEST_PYODIDE_DIRECT в background script
  • -
  • Проверку версии Chrome (≥109 или <109)
  • -
  • Исполнение Python кода через Pyodide в offscreen document
  • -
  • Получение результата выполнения
  • -
-
- -

Python код для тестирования:

- - - - - - -
-
- - - - \ No newline at end of file diff --git a/chrome-extension/test-large-html.html b/chrome-extension/test-large-html.html deleted file mode 100644 index 23adcc5b..00000000 --- a/chrome-extension/test-large-html.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - Large HTML Test - 33+ Chunks - - - - -
-

Large HTML Test Document

-

- This is a test document designed to create more than 33 chunks of 32KB each to thoroughly test our race condition protection in the HTML chunk transfer protocol. - The document contains repetitive content to achieve the desired size and trigger multiple chunk transmissions. - This should help us validate that our enhanced chunk manager with multi-layered storage protection works correctly. -

-
- - - - - -
-

Technical Test Sections

-
- -
-

Race Condition Test Section 1

-

The race condition protection mechanism we've implemented includes:

-
    -
  • Multi-layered storage system (active | completed | global references | global scope)
  • -
  • Global reference storage for immediate access
  • -
  • Global scope backup storage as last resort
  • -
  • Safe transfer lookup with fallback mechanisms
  • -
  • Enhanced cleanup logic with staggered retention periods
  • -
-
-

This section contains detailed technical information about our chunk transfer system improvements.

-

The system now maintains transfer states in 4 different storage layers to prevent "Transfer not found" errors.

-

Active transfers are automatically moved to completed backup storage when assembly completes.

-

Global references ensure immediate access even during cleanup operations.

-

The global scope provides ultimate fallback for critical operations.

-
-
- -
-

Race Condition Test Section 2

-

Background script improvements include:

-
    -
  • Enhanced ChunkManager with race condition protection
  • -
  • Multi-level transfer lookup mechanism
  • -
  • Safe transfer state validation
  • -
  • Comprehensive diagnostic logging
  • -
  • Graceful degradation on transfer cleanup
  • -
-
-

The background script now monitors transfer scope state every 10 seconds to detect potential race conditions.

-

Detailed diagnostic information is logged for each transfer operation.

-

Transfer timeout warnings are shown 5 seconds before cleanup to allow debugging.

-

The system prevents premature cleanup by using staggered retention times for different storage layers.

-

Empty HTML fallback is provided when all other recovery mechanisms fail.

-
-
- - - -
-
- - - -

[CONTENT CONTINUES TO REACH 33+ CHUNKS...]

- - - \ No newline at end of file diff --git a/chrome-extension/test-pyodide-direct.js b/chrome-extension/test-pyodide-direct.js deleted file mode 100644 index a8b53477..00000000 --- a/chrome-extension/test-pyodide-direct.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Test script for direct Pyodide endpoint - * Run this in browser console to test the PYODIDE_DIRECT_TEST functionality - */ - -// Test function -async function testDirectPyodide() { - console.log('🚀 Testing TEST_PYODIDE_DIRECT endpoint...'); - - try { - const testMessage = { - type: 'TEST_PYODIDE_DIRECT', - pythonCode: 'print("Hello World from Pyodide!")\nimport time\nresult = f"Current time: {time.time()}"\nprint(result)\nresult', - timestamp: Date.now() - }; - - console.log('📤 Sending test message:', testMessage); - - // Send message to background script - const response = await chrome.runtime.sendMessage(testMessage); - - console.log('📥 Received response:', response); - - if (response.success) { - console.log('✅ Test successful!'); - console.log('📊 Result:', response.result); - console.log('🌐 Chrome version:', response.chromeVersion); - console.log('⏱️ Execution time:', response.timestamp - testMessage.timestamp, 'ms'); - } else { - console.log('❌ Test failed!'); - console.log('🛠️ Error:', response.error); - - if (response.chromeVersion && parseInt(response.chromeVersion) < 109) { - console.log('ℹ️ Chrome version < 109 detected - this is expected behavior'); - } - } - - } catch (error) { - console.error('💥 Communication error:', error); - console.log('Make sure extension is loaded and background script is running'); - } -} - -// Alternative test with simple Python code -async function testSimplePyodide() { - console.log('🐍 Testing with simple Python code...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'TEST_PYODIDE_DIRECT', - pythonCode: '1 + 2 + 3', - timestamp: Date.now() - }); - - console.log('📥 Simple test response:', response); - - if (response.success) { - console.log('✅ Simple test successful, result:', response.result); - } else { - console.log('❌ Simple test failed:', response.error); - } - - } catch (error) { - console.error('💥 Simple test error:', error); - } -} - -// Run tests -if (typeof chrome !== 'undefined' && chrome.runtime) { - console.log('🔧 Chrome extension runtime detected, running tests...'); - - // Test 1: Simple test - setTimeout(() => { - testSimplePyodide(); - }, 1000); - - // Test 2: Full test - setTimeout(() => { - testDirectPyodide(); - }, 2000); - -} else { - console.log('🔶 Chrome extension not detected'); - console.log('💡 To test:'); - console.log('1. Load extension in Chrome'); - console.log('2. Open DevTools console'); - console.log('3. Copy and run this file'); -} - -// Export for manual testing -window.testDirectPyodide = testDirectPyodide; -window.testSimplePyodide = testSimplePyodide; - -console.log('🎯 Available test functions:'); -console.log('- testDirectPyodide()'); -console.log('- testSimplePyodide()'); \ No newline at end of file diff --git a/docs/api-documentation.md b/docs/api-documentation.md index b0228fdb..4f2c5269 100644 --- a/docs/api-documentation.md +++ b/docs/api-documentation.md @@ -236,10 +236,10 @@ interface WorkflowStep { ### Model Mapping ```javascript const aiModelMapping = { - "basic_analysis": "gemini-flash", + "basic_analysis": "gemini-flash-lite", "detailed_comparison": "gemini-pro", "deep_analysis": "gemini-pro", - "scraping_fallback": "gemini-flash" + "scraping_fallback": "gemini-flash-lite" }; ``` @@ -249,7 +249,7 @@ const aiProviders = { google: { apiUrl: "https://generativelanguage.googleapis.com", models: { - "gemini-flash": "models/gemini-1.5-flash", + "gemini-flash-lite": "models/gemini-1.5-flash", "gemini-pro": "models/gemini-1.5-pro" }, rateLimits: { @@ -326,7 +326,7 @@ const aiProviders = { }, "ai_providers": { "google": { - "fallback_chain": ["gemini-flash", "gemini-pro"], + "fallback_chain": ["gemini-flash-lite", "gemini-pro"], "rate_limits": { "requests_per_minute": 60, "requests_per_hour": 1000 diff --git a/docs/architecture.md b/docs/architecture.md index 2c9cd995..17548717 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -355,7 +355,7 @@ class FastDOMParser: ```javascript const aiProviders = { google: { - fallbackChain: ["gemini-flash", "gemini-pro"], + fallbackChain: ["gemini-flash-lite", "gemini-pro"], rateLimits: { requestsPerMinute: 60, burstLimit: 20 } }, openai: { @@ -367,7 +367,7 @@ const aiProviders = { **Fallback стратегия**: 1. Основная модель текущего провайдера -2. Быстрая модель провайдера (gemini-flash, gpt-3.5-turbo) +2. Быстрая модель провайдера (gemini-flash-lite, gpt-3.5-turbo) 3. Продвинутая модель провайдера (gemini-pro, gpt-4) 4. Оффлайн режим с кешем diff --git a/docs/integration-guide.md b/docs/integration-guide.md index 03cb62fa..36855c6d 100644 --- a/docs/integration-guide.md +++ b/docs/integration-guide.md @@ -89,7 +89,7 @@ ozon-analyzer/ const GEMINI_CONFIG = { apiKey: process.env.GEMINI_API_KEY, models: { - 'gemini-flash': 'models/gemini-1.5-flash', + 'gemini-flash-lite': 'models/gemini-1.5-flash', 'gemini-pro': 'models/gemini-1.5-pro' }, retryPolicy: { diff --git a/memory-bank/development/ozon-analyzer-testing.md b/memory-bank/development/ozon-analyzer-testing.md index 91a0ee7e..21b80b91 100644 --- a/memory-bank/development/ozon-analyzer-testing.md +++ b/memory-bank/development/ozon-analyzer-testing.md @@ -341,7 +341,7 @@ Comprehensive testing results and development insights gathered during the adapt ✅ GPT-3.5-turbo: Deprecated (maintenance mode) 🌟 **Google Gemini Models** - ✅ Gemini Flash: Available (speed optimized) + ✅ Gemini Flash: Available (speed basic_analysis) ✅ Gemini Pro: Available (balanced performance) ✅ Gemini Ultra: Expensive (limited use) @@ -442,7 +442,7 @@ Comprehensive testing results and development insights gathered during the adapt | Category | Target | Achieved | Status | |----------|--------|----------|--------| | **Functionality** | 100% | 100% | ✅ **COMPLETE** | -| **Performance** | <30s | 24.7s | ✅ **OPTIMIZED** | +| **Performance** | <30s | 24.7s | ✅ **basic_analysis** | | **Reliability** | 99%+ | 99.5% | ✅ **STABLE** | | **Security** | Zero Risk | Zero Risk | ✅ **SECURE** | | **Usability** | 90%+ | 92% | ✅ **EXCELLENT** | diff --git a/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md b/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md index 889d25e5..b7de418b 100644 --- a/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md +++ b/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md @@ -394,7 +394,7 @@ class MessagingMonitor { |--------|------------------|---------------| | **Architecture** | Mixed, inconsistent | Clean, unified | | **Testing** | False confidence | Real reliability | -| **Performance** | Memory leaks | Optimized usage | +| **Performance** | Memory leaks | basic_analysis usage | | **Maintainability** | Hard to debug | Easy to understand | | **User Experience** | Unstable chat | Reliable messaging | diff --git a/memory-bank/ui/ozon-analyzer-ui-integration.md b/memory-bank/ui/ozon-analyzer-ui-integration.md index 3fa0333f..3e3e108b 100644 --- a/memory-bank/ui/ozon-analyzer-ui-integration.md +++ b/memory-bank/ui/ozon-analyzer-ui-integration.md @@ -338,7 +338,7 @@ const getFeatureFlags = (capabilities: UICapabilities) => ({ #### **Mobile Optimization** ```typescript -// Touch-optimized interactions +// Touch-basic_analysis interactions const mobileInteractions = { tapToExpand: true, // Expand sections on tap swipeToNavigate: true, // Swipe between results @@ -792,7 +792,7 @@ The Ozon Analyzer UI integration establishes comprehensive patterns for plugin u 3. **Accessible Design**: WCAG 2.1 AA compliance with screen reader support 4. **Responsive Layout**: Mobile-first design with adaptive breakpoints 5. **Error Resilience**: User-friendly error recovery and fallback patterns -6. **Performance Optimized**: Virtual scrolling, lazy loading, and efficient rendering +6. **Performance basic_analysis**: Virtual scrolling, lazy loading, and efficient rendering 7. **Consistent Branding**: Cohesive design language and component library These patterns provide a foundation for future plugin UIs and ensure excellent user experiences across devices and accessibility needs. diff --git a/package.json b/package.json index d9fd2d45..15989789 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1345", + "version": "1.0.1350", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 4fa18f53..70d11345 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 0169ec00..b813123c 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1350", + "version": "0.5.1355", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index acf2a839..e5e3be15 100644 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 4942adad..6539645b 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 872cf090..2501cf92 100644 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 3e891945..06ec0b14 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 6f1f2ea4..806ea319 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index ecc00f0b..10b2d537 100644 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 90736c69..16a47690 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 55f7dffe..ef0e7386 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 18111b4c..c73abfe3 100644 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1371", + "version": "0.5.1376", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 8f1906eb..7961a2a7 100644 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index c00ac1d9..42d1a23f 100644 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 29a3430a..77f8e358 100644 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 91296683..9f2e3e47 100644 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 5da5b4e3..1b573a67 100644 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index b2fd6e70..4232ca90 100644 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 58a0e9a7..a8b28de1 100644 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/PluginDetails.tsx b/pages/options/src/components/PluginDetails.tsx index c879f9fd..5311908e 100644 --- a/pages/options/src/components/PluginDetails.tsx +++ b/pages/options/src/components/PluginDetails.tsx @@ -17,8 +17,8 @@ interface LanguagePrompts { } interface PromptsStructure { - optimized: LanguagePrompts; - deep: LanguagePrompts; + basic_analysis: LanguagePrompts; + deep_analysis: LanguagePrompts; } const cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' '); @@ -52,7 +52,7 @@ interface PromptsEditorProps { } const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: PromptsEditorProps) => { - const [promptType, setPromptType] = useState<'optimized' | 'deep'>('optimized'); + const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis'); const [language, setLanguage] = useState<'ru' | 'en'>('ru'); const [customPrompt, setCustomPrompt] = useState(''); @@ -132,7 +132,7 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts @@ -359,10 +359,10 @@ const PluginDetails = (props: PluginDetailsProps) => { if (stored[key]) { const prompts = stored[key] as PromptsStructure; console.log(' Структура промптов:'); - console.log(` - optimized.ru: ${prompts.optimized?.ru ? '✓' : '✗'} (${prompts.optimized?.ru?.length || 0} символов)`); - console.log(` - optimized.en: ${prompts.optimized?.en ? '✓' : '✗'} (${prompts.optimized?.en?.length || 0} символов)`); - console.log(` - deep.ru: ${prompts.deep?.ru ? '✓' : '✗'} (${prompts.deep?.ru?.length || 0} символов)`); - console.log(` - deep.en: ${prompts.deep?.en ? '✓' : '✗'} (${prompts.deep?.en?.length || 0} символов)`); + console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`); + console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`); + console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`); + console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`); } } catch (error) { console.error('Ошибка диагностики промптов:', error); @@ -380,22 +380,22 @@ const PluginDetails = (props: PluginDetailsProps) => { if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) { const promptsConfig = defaultValue as any; const result: PromptsStructure = { - optimized: { ru: {}, en: {} }, - deep: { ru: {}, en: {} } + basic_analysis: { ru: {}, en: {} }, + deep_analysis: { ru: {}, en: {} } }; // Извлекаем default значения из структуры manifest - if (promptsConfig.optimized?.ru?.default) { - result.optimized.ru = promptsConfig.optimized.ru.default; + if (promptsConfig.basic_analysis?.ru?.default) { + result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default; } - if (promptsConfig.optimized?.en?.default) { - result.optimized.en = promptsConfig.optimized.en.default; + if (promptsConfig.basic_analysis?.en?.default) { + result.basic_analysis.en = promptsConfig.basic_analysis.en.default; } - if (promptsConfig.deep?.ru?.default) { - result.deep.ru = promptsConfig.deep.ru.default; + if (promptsConfig.deep_analysis?.ru?.default) { + result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default; } - if (promptsConfig.deep?.en?.default) { - result.deep.en = promptsConfig.deep.en.default; + if (promptsConfig.deep_analysis?.en?.default) { + result.deep_analysis.en = promptsConfig.deep_analysis.en.default; } return result; diff --git a/pages/options/src/hooks/useAIKeys.ts b/pages/options/src/hooks/useAIKeys.ts index 4d5109ab..73825aac 100644 --- a/pages/options/src/hooks/useAIKeys.ts +++ b/pages/options/src/hooks/useAIKeys.ts @@ -15,8 +15,8 @@ export const useAIKeys = () => { const { t } = useTranslations('ru'); const [aiKeys, setAiKeys] = React.useState([ { - id: 'gemini-flash', - name: 'Google Gemini (Flash) - Базовый анализ', + id: 'gemini-flash-lite', + name: 'Google Gemini Flash Lite - Базовый анализ', key: '', status: 'not_configured', isFixed: true, @@ -55,7 +55,7 @@ export const useAIKeys = () => { console.log('[useAIKeys] Starting to load AI keys...'); // Загружаем зашифрованные ключи - const fixedKeyIds = ['gemini-flash', 'gemini-pro']; + const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro']; const fixedKeysPromises = fixedKeyIds.map(async (keyId) => { const decryptedKey = await APIKeyManager.getDecryptedKey(keyId); console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty'); diff --git a/pages/options/src/locales/en.json b/pages/options/src/locales/en.json index 4fa9d73d..88dc5a23 100644 --- a/pages/options/src/locales/en.json +++ b/pages/options/src/locales/en.json @@ -24,8 +24,8 @@ "prompts": { "settings": "Prompt Settings", "type": "Prompt Type:", - "optimized": "Optimized", - "deep": "Deep", + "basic_analysis": "Basic analysis", + "deep_analysis": "Deep analysis", "language": "Language:", "russian": "Russian", "english": "English", diff --git a/pages/options/src/locales/ru.json b/pages/options/src/locales/ru.json index 1eb6f503..ccaa7ec6 100644 --- a/pages/options/src/locales/ru.json +++ b/pages/options/src/locales/ru.json @@ -37,8 +37,8 @@ "prompts": { "settings": "Настройки промптов", "type": "Тип промпта:", - "optimized": "Оптимизированный", - "deep": "Глубокий", + "basic_analysis": "Базовый", + "deep_analysis": "Глубокий", "language": "Язык:", "russian": "Русский", "english": "Английский", diff --git a/pages/options/src/utils/mcpIntegration.ts b/pages/options/src/utils/mcpIntegration.ts index 958d8675..6cddec48 100644 --- a/pages/options/src/utils/mcpIntegration.ts +++ b/pages/options/src/utils/mcpIntegration.ts @@ -97,9 +97,9 @@ export class MCPService { */ private static getDefaultKeyForService(service: string): string | null { const serviceKeyMap: Record = { - 'gemini-flash': 'gemini-flash', + 'gemini-flash-lite': 'gemini-flash-lite', 'gemini-pro': 'gemini-pro', - 'google-gemini': 'gemini-flash', + 'google-gemini': 'gemini-flash-lite', 'anthropic': 'claude', // Для будущих реализаций 'openai': 'gpt', // Для будущих реализаций }; @@ -185,8 +185,8 @@ export class AIServiceManager { * Выполняет запрос к Google Gemini через MCP */ static async queryGemini(prompt: string, useFlash: boolean = true): Promise { - const service = useFlash ? 'gemini-flash' : 'gemini-pro'; - const keyId = useFlash ? 'gemini-flash' : 'gemini-pro'; + const service = useFlash ? 'gemini-flash-lite' : 'gemini-pro'; + const keyId = useFlash ? 'gemini-flash-lite' : 'gemini-pro'; return await MCPService.executeRequest({ service, @@ -228,7 +228,7 @@ export class AIServiceManager { const models: string[] = []; if (services.includes('google-gemini')) { - models.push('gemini-pro', 'gemini-flash'); + models.push('gemini-pro', 'gemini-flash-lite'); } if (services.includes('anthropic')) { models.push('claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'); diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index de3294a8..27ac3e8d 100644 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1363", + "version": "0.5.1368", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 5e422b11..8c880030 100644 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1368", + "version": "0.27.1373", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index d0f9e382..92c25d0e 100644 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1370", + "version": "0.27.1375", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/src/background/offscreen.ts b/src/background/offscreen.ts index 15381466..d4500c4a 100644 --- a/src/background/offscreen.ts +++ b/src/background/offscreen.ts @@ -512,8 +512,8 @@ except Exception as e: console.log('[BRIDGE DIAGNOSTIC] Тип промптов:', typeof pluginSettings.prompts); if (typeof pluginSettings.prompts === 'object') { console.log('[BRIDGE DIAGNOSTIC] Ключи промптов:', Object.keys(pluginSettings.prompts)); - console.log('[BRIDGE DIAGNOSTIC] optimized промпты:', pluginSettings.prompts.optimized); - console.log('[BRIDGE DIAGNOSTIC] deep промпты:', pluginSettings.prompts.deep); + console.log('[BRIDGE DIAGNOSTIC] basic_analysis промпты:', pluginSettings.prompts.basic_analysis); + console.log('[BRIDGE DIAGNOSTIC] deep_analysis промпты:', pluginSettings.prompts.deep_analysis); } } else { console.log('[BRIDGE DIAGNOSTIC] ❌ Промпты НЕ НАЙДЕНЫ в pluginSettings'); @@ -890,6 +890,7 @@ interface ExecuteWorkflowMessage { useChunks: boolean; pageHtml: string; assembledHtml?: string; // New field for pre-assembled HTML from chunks + pluginSettings?: Record; // Plugin settings for workflow execution }; } diff --git a/temp_before.json b/temp_before.json deleted file mode 100644 index 7c14bc33..00000000 --- a/temp_before.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "name": "Ozon Analyzer", - "version": "1.1.0", - "description": "Анализатор товаров Ozon с проверкой соответствия описания и состава", - "author": "Igor Lebedev", - "main_server": "mcp_server.py", - "host_permissions": [ - "*://*.ozon.ru/*", - "http://localhost/*", - "http://127.0.0.1/*" - ], - "icon": "icon.svg", - "permissions": [ - "activeTab", - "scripting" - ], - "ai_models": { - "basic_analysis": "gemini-flash", - "compliance_check": "gemini-flash", - "detailed_comparison": "gemini-pro", - "deep_analysis": "gemini-pro", - "scraping_fallback": "gemini-flash" - }, - "options": { - "enable_deep_analysis": { - "type": "boolean", - "default": true, - "label": { - "ru": "Включить глубокий анализ", - "en": "Enable deep analysis" - }, - "description": { - "ru": "Запускать дополнительный анализ при низкой оценке соответствия", - "en": "Run additional analysis when compliance score is low" - } - }, - "auto_request_deep_analysis": { - "type": "boolean", - "default": true, - "label": { - "ru": "Автоматически запрашивать глубокий анализ", - "en": "Automatically request deep analysis" - } - }, - "search_for_analogs": { - "type": "boolean", - "default": false, - "label": { - "ru": "Поиск аналогов", - "en": "Search for analogs" - }, - "description": { - "ru": "Включить поиск аналогов", - "en": "Enable search for analogs" - } - }, - "response_language": { - "type": "select", - "default": "ru", - "values": [ - "ru", - "en", - "auto" - ], - "labels": { - "ru": { - "ru": "Русский", - "en": "Russian" - }, - "en": { - "ru": "Английский", - "en": "English" - }, - "auto": { - "ru": "Автоопределение", - "en": "Auto-detect" - } - }, - "label": { - "ru": "Язык ответа", - "en": "Response language" - } - }, - "analysis_threshold": { - "type": "number", - "default": 7, - "min": 1, - "max": 10, - "step": 1, - "label": { - "ru": "Порог для глубокого анализа", - "en": "Deep analysis threshold" - } - }, - "prompts": { - "optimized": { - "ru": { - "type": "text", -} diff --git a/temp_mcp_bridge.ts b/temp_mcp_bridge.ts deleted file mode 100644 index 14c05c44..00000000 --- a/temp_mcp_bridge.ts +++ /dev/null @@ -1,25 +0,0 @@ -export async function runPythonTool(pluginId: string, toolName: string, toolInput: any, context?: any): Promise { - // Загружаем manifest.json для плагина - const manifest = await getPluginManifest(pluginId); - - initializeCommunication(); - const pyodideWorker = getWorker(); - const callId = `py_tool_run_${Date.now()}_${Math.random()}`; - - const pyScriptUrl = chrome.runtime.getURL(`/plugins/${pluginId}/mcp_server.py`); - const response = await fetch(pyScriptUrl); - if (!response.ok) throw new Error(`Python script для плагина ${pluginId} не найден`); - const pythonCode = await response.text(); - - return new Promise((resolve, reject) => { - promises.set(callId, { resolve, reject }); - pyodideWorker.postMessage({ - type: 'run_python_tool', - callId, - pythonCode, - toolName, - toolInput, - manifest - }); - }); -} diff --git a/test-chat-system.js b/test-chat-system.js deleted file mode 100644 index 2ffb901e..00000000 --- a/test-chat-system.js +++ /dev/null @@ -1,141 +0,0 @@ -/* eslint-disable no-undef */ -// Скрипт для тестирования системы чатов плагинов -// Запускать в консоли DevTools - -console.log('🧪 Тестирование системы чатов плагинов...'); - -// Функция для создания тестового чата -async function createTestChat() { - try { - console.log('📝 Создание тестового чата...'); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'test-chat-plugin', - pageKey: 'https://example.com/test', - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение - const message = { - role: 'user', - content: 'Привет! Это тестовое сообщение для отладки.', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'test-chat-plugin', - pageKey: 'https://example.com/test', - message: message, - }); - - console.log('✅ Сообщение сохранено:', message); - - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId: 'test-chat-plugin', - pageKey: 'https://example.com/test', - draftText: 'Это тестовый черновик сообщения...', - }); - - console.log('✅ Черновик сохранен'); - - return true; - } catch (error) { - console.error('❌ Ошибка создания чата:', error); - return false; - } -} - -// Функция для получения списка чатов -async function listChats() { - try { - console.log('📋 Получение списка чатов...'); - - const chats = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHATS', - pluginId: null, - }); - - console.log('✅ Чаты найдены:', chats); - return chats; - } catch (error) { - console.error('❌ Ошибка получения чатов:', error); - return []; - } -} - -// Функция для получения списка черновиков -async function listDrafts() { - try { - console.log('📋 Получение списка черновиков...'); - - const drafts = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHAT_DRAFTS', - pluginId: null, - }); - - console.log('✅ Черновики найдены:', drafts); - return drafts; - } catch (error) { - console.error('❌ Ошибка получения черновиков:', error); - return []; - } -} - -// Функция для отправки тестового лога -async function sendTestLog() { - try { - console.log('📝 Отправка тестового лога...'); - - await chrome.runtime.sendMessage({ - type: 'LOG_EVENT', - pluginId: 'test-chat-plugin', - pageKey: 'https://example.com/test', - level: 'info', - stepId: 'test-step', - message: 'Тестовое сообщение лога для отладки', - logData: { test: true, timestamp: Date.now() }, - }); - - console.log('✅ Лог отправлен'); - } catch (error) { - console.error('❌ Ошибка отправки лога:', error); - } -} - -// Запуск всех тестов -async function runAllTests() { - console.log('🚀 Запуск всех тестов системы чатов...'); - - await createTestChat(); - await sendTestLog(); - await listChats(); - await listDrafts(); - - console.log('✅ Все тесты завершены!'); - console.log('💡 Теперь откройте DevTools и перейдите на вкладку "Чаты плагинов" для просмотра результатов'); -} - -// Экспортируем функции для использования в консоли -window.testChatSystem = { - createTestChat, - listChats, - listDrafts, - sendTestLog, - runAllTests, -}; - -console.log('🎯 Функции тестирования доступны:'); -console.log('- testChatSystem.createTestChat() - создать тестовый чат'); -console.log('- testChatSystem.listChats() - получить список чатов'); -console.log('- testChatSystem.listDrafts() - получить список черновиков'); -console.log('- testChatSystem.sendTestLog() - отправить тестовый лог'); -console.log('- testChatSystem.runAllTests() - запустить все тесты'); - -// Автоматически запускаем тесты -runAllTests(); diff --git a/test-diagnostic.html b/test-diagnostic.html deleted file mode 100644 index 4c534020..00000000 --- a/test-diagnostic.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Chrome Extension Diagnostic Test - - - -

Chrome Extension Diagnostic Test

-

This page is used to test the chrome extension's storage and messaging functionality.

- -
-

Extension Status

-
Checking...
- -
- -
-

Storage Diagnostic

- -
-
- -
-

Chat Operations

- - - -
-
- -
-

Log Output

-
- -
- - - - \ No newline at end of file diff --git a/test-load-prompts.html b/test-load-prompts.html deleted file mode 100644 index bd2c6e44..00000000 --- a/test-load-prompts.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - Тест loadDefaultPrompts() - - - -

🧪 Тест функции loadDefaultPrompts()

-
- - - - \ No newline at end of file diff --git a/test-options-browser.html b/test-options-browser.html deleted file mode 100644 index d8bd4fee..00000000 --- a/test-options-browser.html +++ /dev/null @@ -1,440 +0,0 @@ - - - - - - Тест интерфейса редактирования промптов - Ozon Analyzer - - - -

🧪 Тестирование интерфейса редактирования промптов

-

Ozon Analyzer Plugin - комплексное тестирование функциональности

- - - -
-
-

📁 Тест 1: Загрузка интерфейса options

- ОЖИДАНИЕ -
-
-

Проверка корректной загрузки страницы настройки промптов

-
-
-
- -
-
-

📋 Тест 2: Отображение вкладок типов промптов

- ОЖИДАНИЕ -
-
-

Проверка наличия вкладок "Оптимизированный анализ" и "Глубокий анализ"

-
-
-
- -
-
-

🌐 Тест 3: Отображение вкладок языков

- ОЖИДАНИЕ -
-
-

Проверка наличия вкладок "Русский" и "English"

-
-
-
- -
-
-

📝 Тест 4: Функциональность копирования

- ОЖИДАНИЕ -
-
-

Проверка работы кнопки копирования текста из оригинального поля в редактируемое

-
-
- - -
-
- - -
-
-
- - -
- -
- - -
-
- -
-
-
-
- -
-
-

💾 Тест 5: Сохранение в chrome.storage

- ОЖИДАНИЕ -
-
-

Проверка сохранения промптов в chrome.storage.sync

-
-
-
- -
-
-

🐍 Тест 6: Интеграция с Python backend

- ОЖИДАНИЕ -
-
-

Проверка загрузки промптов в mcp_server.py

-
-
-
- -
-
-

🚨 Тест 7: Обработка ошибок

- ОЖИДАНИЕ -
-
-

Проверка обработки ошибок и fallback логики

-
-
-
- -
-
-
-
- 0/7 тестов завершено -
- - - - \ No newline at end of file diff --git a/test-options-interface.js b/test-options-interface.js deleted file mode 100644 index cda529d8..00000000 --- a/test-options-interface.js +++ /dev/null @@ -1,164 +0,0 @@ -// Тест для loadDefaultPrompts() функции -// Этот файл проверяет, что функция правильно загружает промпты из manifest.json - -console.log('🧪 Запуск теста loadDefaultPrompts()'); - -// Имитация chrome.runtime.getURL для тестирования -if (typeof chrome === 'undefined') { - window.chrome = { - runtime: { - getURL: function(path) { - // В тестовом окружении возвращаем путь к локальному файлу - return path; - } - } - }; -} - -// Имитация fetch для тестирования -const originalFetch = window.fetch; -let testManifest = { - "options": { - "prompts": { - "optimized": { - "ru": { - "type": "text", - "default": "Test Russian optimized prompt", - "label": { "ru": "Оптимизированный промпт (русский)" } - }, - "en": { - "type": "text", - "default": "Test English optimized prompt", - "label": { "ru": "Оптимизированный промпт (английский)" } - } - }, - "deep": { - "ru": { - "type": "text", - "default": "Test Russian deep analysis prompt", - "label": { "ru": "Промпт глубокого анализа (русский)" } - }, - "en": { - "type": "text", - "default": "Test English deep analysis prompt", - "label": { "ru": "Промпт глубокого анализа (английский)" } - } - } - } - } -}; - -// Mock fetch для возврата тестового manifest -window.fetch = function(url) { - console.log('📡 Mock fetch called with URL:', url); - return Promise.resolve({ - ok: true, - json: function() { - return Promise.resolve(testManifest); - } - }); -}; - -// Глобальные переменные для теста -let defaultPrompts = {}; - -// Функция из options/index.html -async function loadDefaultPrompts() { - console.log('[TEST][DEBUG] 🔍 Loading default prompts from manifest.json...'); - - try { - const manifestUrl = chrome.runtime.getURL('plugins/ozon-analyzer/manifest.json'); - console.log('[TEST][DEBUG] 📂 Manifest URL:', manifestUrl); - - const response = await fetch(manifestUrl); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const manifest = await response.json(); - console.log('[TEST][DEBUG] 📋 Manifest loaded successfully'); - - if (manifest.options && manifest.options.prompts) { - defaultPrompts = manifest.options.prompts; - console.log('[TEST][DEBUG] 🤖 Prompts loaded:', Object.keys(defaultPrompts)); - - console.log('✅ Test passed: Prompts loaded successfully'); - return true; - } else { - throw new Error('Раздел prompts не найден в manifest.json'); - } - - } catch (error) { - console.error('[TEST][DEBUG] ❌ Error loading prompts:', error); - - // Fallback на встроенные значения промптов - defaultPrompts = { - optimized: { - ru: { - type: "text", - default: "Ты - токсиколог и химик-косметолог...", - label: { ru: "Оптимизированный промпт (русский)" } - }, - en: { - type: "text", - default: "You are a toxicologist and cosmetic chemist...", - label: { ru: "Оптимизированный промпт (английский)" } - } - }, - deep: { - ru: { - type: "text", - default: "Длинный текст промпта глубокого анализа...", - label: { ru: "Промпт глубокого анализа (русский)" } - }, - en: { - type: "text", - default: "Long English deep analysis prompt text...", - label: { ru: "Промпт глубокого анализа (английский)" } - } - } - }; - - console.log('✅ Test passed: Fallback prompts loaded'); - return true; - } -} - -// Запуск теста -async function runTest() { - console.log('🚀 Starting loadDefaultPrompts() test...'); - - const success = await loadDefaultPrompts(); - - if (success) { - console.log('🎉 Test completed successfully!'); - console.log('📊 Loaded prompts:', Object.keys(defaultPrompts)); - - // Проверка структуры промптов - if (defaultPrompts.optimized && defaultPrompts.optimized.ru && defaultPrompts.optimized.en) { - console.log('✅ Optimized prompts structure is correct'); - } else { - console.error('❌ Optimized prompts structure is incorrect'); - } - - if (defaultPrompts.deep && defaultPrompts.deep.ru && defaultPrompts.deep.en) { - console.log('✅ Deep analysis prompts structure is correct'); - } else { - console.error('❌ Deep analysis prompts structure is incorrect'); - } - - } else { - console.error('❌ Test failed!'); - } - - // Восстановление оригинального fetch - window.fetch = originalFetch; -} - -// Запуск теста при загрузке -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', runTest); -} else { - runTest(); -} \ No newline at end of file diff --git a/test-ozon-chat.js b/test-ozon-chat.js deleted file mode 100644 index 7d925379..00000000 --- a/test-ozon-chat.js +++ /dev/null @@ -1,226 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для создания чатов на странице Ozon -// Запускать в консоли DevTools - -console.log('🛒 Тестирование чатов на странице Ozon...'); - -// Получаем текущий URL -const currentUrl = window.location.href; -console.log('📍 Текущий URL:', currentUrl); - -// Функция для создания чата для ozon-analyzer -async function createOzonChat() { - try { - console.log('📝 Создание чата для ozon-analyzer...'); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение пользователя - const userMessage = { - role: 'user', - content: 'Проанализируй этот товар и расскажи о его характеристиках', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: userMessage, - }); - - console.log('✅ Сообщение пользователя сохранено:', userMessage); - - // Сохраняем ответ плагина - const pluginMessage = { - role: 'plugin', - content: - 'Анализирую товар "Костюм спортивный North Dot"... Найдена информация о цене, характеристиках и отзывах.', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: pluginMessage, - }); - - console.log('✅ Ответ плагина сохранен:', pluginMessage); - - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - draftText: 'Хотите узнать больше о доставке или отзывах?', - }); - - console.log('✅ Черновик сохранен'); - - return true; - } catch (error) { - console.error('❌ Ошибка создания чата:', error); - return false; - } -} - -// Функция для создания чата для test-chat-plugin -async function createTestPluginChat() { - try { - console.log('📝 Создание чата для test-chat-plugin...'); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение - const message = { - role: 'user', - content: 'Это тестовое сообщение с Ozon', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - message: message, - }); - - console.log('✅ Тестовое сообщение сохранено:', message); - - return true; - } catch (error) { - console.error('❌ Ошибка создания тестового чата:', error); - return false; - } -} - -// Функция для отправки тестовых логов -async function sendTestLogs() { - try { - console.log('📝 Отправка тестовых логов...'); - - const logs = [ - { - pluginId: 'ozon-analyzer', - level: 'info', - stepId: 'page-load', - message: 'Страница товара загружена', - logData: { url: currentUrl, title: document.title }, - }, - { - pluginId: 'ozon-analyzer', - level: 'success', - stepId: 'product-found', - message: 'Товар найден: Костюм спортивный North Dot', - logData: { productId: '1438414833' }, - }, - { - pluginId: 'test-chat-plugin', - level: 'debug', - stepId: 'test-debug', - message: 'Тестовый отладочный лог', - logData: { test: true, timestamp: Date.now() }, - }, - ]; - - for (const log of logs) { - await chrome.runtime.sendMessage({ - type: 'LOG_EVENT', - pluginId: log.pluginId, - pageKey: currentUrl, - level: log.level, - stepId: log.stepId, - message: log.message, - logData: log.logData, - }); - - console.log(`✅ Лог отправлен: ${log.pluginId} - ${log.message}`); - } - } catch (error) { - console.error('❌ Ошибка отправки логов:', error); - } -} - -// Функция для получения всех данных -async function getAllData() { - try { - console.log('📋 Получение всех данных...'); - - // Получаем чаты - const chats = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHATS', - pluginId: null, - }); - - console.log('✅ Чаты найдены:', chats); - - // Получаем черновики - const drafts = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHAT_DRAFTS', - pluginId: null, - }); - - console.log('✅ Черновики найдены:', drafts); - - // Получаем логи - const logs = await chrome.runtime.sendMessage({ - type: 'LIST_ALL_PLUGIN_LOGS', - }); - - console.log('✅ Логи найдены:', logs); - - return { chats, drafts, logs }; - } catch (error) { - console.error('❌ Ошибка получения данных:', error); - return { chats: [], drafts: [], logs: {} }; - } -} - -// Запуск всех тестов -async function runOzonTests() { - console.log('🚀 Запуск тестов для Ozon...'); - - await createOzonChat(); - await createTestPluginChat(); - await sendTestLogs(); - await getAllData(); - - console.log('✅ Все тесты завершены!'); - console.log('💡 Теперь откройте DevTools и перейдите на вкладки:'); - console.log(' - "Чаты плагинов" - для просмотра чатов и черновиков'); - console.log(' - "Логи" - для просмотра логов плагинов'); -} - -// Экспортируем функции для использования в консоли -window.ozonTestSystem = { - createOzonChat, - createTestPluginChat, - sendTestLogs, - getAllData, - runOzonTests, -}; - -console.log('🎯 Функции тестирования Ozon доступны:'); -console.log('- ozonTestSystem.createOzonChat() - создать чат для ozon-analyzer'); -console.log('- ozonTestSystem.createTestPluginChat() - создать чат для test-chat-plugin'); -console.log('- ozonTestSystem.sendTestLogs() - отправить тестовые логи'); -console.log('- ozonTestSystem.getAllData() - получить все данные'); -console.log('- ozonTestSystem.runOzonTests() - запустить все тесты'); - -// Автоматически запускаем тесты -runOzonTests(); diff --git a/test-pyodide-message.js b/test-pyodide-message.js deleted file mode 100644 index 44526fd5..00000000 --- a/test-pyodide-message.js +++ /dev/null @@ -1,248 +0,0 @@ -// Тестовый скрипт для проверки PYODIDE_MESSAGE после внедрения Варианта 2 -// Запуск: node test-pyodide-message.js - -console.log('🧪 Тестирование PYODIDE_MESSAGE верификации после Варианта 2...'); - -// Имитация chrome.runtime API для тестирования -const mockChrome = { - runtime: { - sendMessage: (message, callback) => { - console.log('📤 Отправка PYODIDE_MESSAGE:', { - type: message.type, - pluginId: message.pluginId, - pageKey: message.pageKey?.substring(0, 50) + '...', - messageLength: message.message?.length || 0, - timestamp: message.timestamp - }); - - // Имитация ответа background script - setTimeout(() => { - if (message.type === 'PYODIDE_MESSAGE') { - // Имитируем успешную обработку и верификацию - callback({ - success: true, - messageId: `pyodide_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - type: 'PYODIDE_MESSAGE_RESPONSE' - }); - } else { - callback({ error: 'Неизвестный тип сообщения' }); - } - }, 100); // Имитация задержки background - }, - - lastError: null, - - onMessage: { - addListener: (listener) => { - console.log('📡 Добавлен слушатель сообщений'); - }, - removeListener: (listener) => { - console.log('🔌 Удален слушатель сообщений'); - } - } - } -}; - -// Глобальный объект chrome для тестирования -global.chrome = mockChrome; - -// Имитация Promise-based подхода (как в исправленном коде) -function sendPyodideMessage(pluginId, pageKey, message, timestamp = Date.now()) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error('PYODIDE_MESSAGE timeout after 10000ms')); - }, 10000); - - chrome.runtime.sendMessage({ - type: 'PYODIDE_MESSAGE', - pluginId, - pageKey, - message, - timestamp - }, (response) => { - clearTimeout(timeoutId); - - if (chrome.runtime.lastError) { - console.error('❌ sendPyodideMessage error:', chrome.runtime.lastError); - reject(new Error(chrome.runtime.lastError.message)); - return; - } - - if (response === undefined) { - console.warn('⚠️ sendPyodideMessage: received undefined response'); - reject(new Error('Background script returned undefined')); - return; - } - - resolve(response); - }); - }); -} - -// Тест 1: Проверка корректной структуры PYODIDE_MESSAGE (Вариант 2) -async function testCorrectPyodideMessage() { - console.log('\n📋 Тест 1: Корректная структура PYODIDE_MESSAGE (Вариант 2)'); - - try { - const pluginId = 'ozon-analyzer'; - const pageKey = 'https://www.ozon.ru/product/test-product-123'; - const message = 'Анализ товара завершен. Найдена информация о цене и характеристиках.'; - const timestamp = Date.now(); - - const response = await sendPyodideMessage(pluginId, pageKey, message, timestamp); - - console.log('✅ PYODIDE_MESSAGE успешно отправлен и обработан:', response); - console.log(' - messageId:', response.messageId); - console.log(' - success:', response.success); - - // Проверяем, что messageId имеет правильный формат - if (response.messageId && response.messageId.startsWith('pyodide_')) { - console.log(' ✅ messageId имеет правильный формат (начинается с pyodide_)'); - return true; - } else { - console.error(' ❌ messageId имеет неправильный формат'); - return false; - } - - } catch (error) { - console.error('❌ PYODIDE_MESSAGE ошибка:', error.message); - return false; - } -} - -// Тест 2: Проверка обработки объекта в message -async function testObjectMessage() { - console.log('\n📦 Тест 2: Обработка объекта в message поле'); - - try { - const pluginId = 'ozon-analyzer'; - const pageKey = 'https://www.ozon.ru/product/test-product-456'; - const message = { - type: 'analysis_result', - product: { - name: 'Тестовый товар', - price: 1500, - rating: 4.5 - }, - analysis: { - competitors: 12, - trend: 'up' - } - }; - const timestamp = Date.now(); - - const response = await sendPyodideMessage(pluginId, pageKey, message, timestamp); - - console.log('✅ Объект в message успешно обработан:', response); - console.log(' - messageId:', response.messageId); - console.log(' - success:', response.success); - - return true; - } catch (error) { - console.error('❌ Обработка объекта в message ошибка:', error.message); - return false; - } -} - -// Тест 3: Проверка отсутствия обязательных полей -async function testMissingFields() { - console.log('\n❌ Тест 3: Отсутствие обязательных полей'); - - try { - const response = await sendPyodideMessage('', '', ''); - - console.log('❌ Ожидалась ошибка из-за отсутствия полей, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('Missing required fields')) { - console.log('✅ Корректная обработка отсутствия обязательных полей:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } -} - -// Тест 4: Проверка timeout -async function testTimeout() { - console.log('\n⏱️ Тест 4: Проверка timeout'); - - // Изменяем mock для имитации долгого ответа - const originalSendMessage = mockChrome.runtime.sendMessage; - mockChrome.runtime.sendMessage = (message, callback) => { - // Имитация очень долгого ответа (больше timeout) - setTimeout(() => { - callback({ success: true, messageId: 'test-id' }); - }, 15000); // 15 секунд - больше чем наш timeout 10 секунд - }; - - try { - const response = await sendPyodideMessage('test-plugin', 'test-page', 'test message'); - - console.log('❌ Ожидался timeout, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('timeout')) { - console.log('✅ Timeout сработал корректно:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } finally { - // Восстанавливаем оригинальную функцию - mockChrome.runtime.sendMessage = originalSendMessage; - } -} - -// Основная функция тестирования -async function runPyodideTests() { - console.log('🚀 Запуск тестов PYODIDE_MESSAGE после Варианта 2...\n'); - - const tests = [ - testCorrectPyodideMessage, - testObjectMessage, - testMissingFields, - testTimeout - ]; - - let passed = 0; - let failed = 0; - - for (const test of tests) { - try { - const result = await test(); - if (result) { - passed++; - } else { - failed++; - } - } catch (error) { - console.error('💥 Критическая ошибка в тесте:', error); - failed++; - } - } - - console.log('\n📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ PYODIDE_MESSAGE:'); - console.log(`✅ Пройдено: ${passed}`); - console.log(`❌ Провалено: ${failed}`); - console.log(`📈 Всего тестов: ${passed + failed}`); - - if (failed === 0) { - console.log('\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! PYODIDE_MESSAGE работает корректно после Варианта 2.'); - console.log('🔧 Верификация сообщений исправлена - поле id присваивается, role = "plugin".'); - console.log('📝 Рекомендуется провести интеграционное тестирование в браузере.'); - } else { - console.log('\n⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ. Требуется дополнительная отладка.'); - } - - return { passed, failed, total: passed + failed }; -} - -// Запуск тестов -if (typeof module !== 'undefined' && module.exports) { - module.exports = { runPyodideTests }; -} else { - runPyodideTests().catch(console.error); -} \ No newline at end of file diff --git a/test-recovery-analysis.cjs b/test-recovery-analysis.cjs deleted file mode 100644 index 81296d46..00000000 --- a/test-recovery-analysis.cjs +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env node - -/** - * Анализатор recovery механизма для тестирования исправлений - * Проверяет код background.ts на правильность реализации recovery - */ - -const fs = require('fs'); -const path = require('path'); - -console.log('🧪 АНАЛИЗАТОР RECOVERY МЕХАНИЗМА'); -console.log('================================'); - -// Читаем код background.ts -const backgroundCode = fs.readFileSync('src/background/background.ts', 'utf8'); - -console.log('✅ Код background.ts прочитан успешно'); - -// Проверки recovery механизма -const checks = [ - { - name: '✅ Сохранение метаданных перед завершением transfer', - pattern: /saveTransferMetadataBeforeCompletion/, - description: 'Функция должна сохранять pluginId и pageKey перед завершением transfer' - }, - { - name: '✅ Восстановление pluginId из chrome.storage', - pattern: /pluginId.*savedMetadata.*pluginId/, - description: 'Recovery должен восстанавливать pluginId из сохранённых метаданных' - }, - { - name: '✅ Восстановление pageKey из chrome.storage', - pattern: /pageKey.*savedMetadata.*pageKey/, - description: 'Recovery должен восстанавливать pageKey из сохранённых метаданных' - }, - { - name: '✅ Multi-layer recovery стратегия', - pattern: /Strategy [1-5]/, - description: 'Должно быть 5 стратегий recovery (emergency_backup, global_scope, chrome_storage, offscreen_requery, partial_recovery)' - }, - { - name: '✅ TransferPersistenceManager', - pattern: /class TransferPersistenceManager/, - description: 'Должен быть класс для сохранения/загрузки метаданных transfer в chrome.storage' - }, - { - name: '✅ TransferRecoveryManager', - pattern: /class TransferRecoveryManager/, - description: 'Должен быть класс для выполнения recovery операций' - }, - { - name: '✅ Multi-layer storage verification', - pattern: /verifyMultiLayerStorage/, - description: 'Должна быть функция проверки сохранения transfer во всех storage слоях' - }, - { - name: '✅ Recovery из emergency backup', - pattern: /recoverFromEmergencyBackup/, - description: 'Должна быть стратегия recovery из emergency backup' - }, - { - name: '✅ Recovery из global scope', - pattern: /recoverFromGlobalScope/, - description: 'Должна быть стратегия recovery из global scope' - }, - { - name: '✅ Recovery из chrome storage', - pattern: /recoverFromChromeStorage/, - description: 'Должна быть стратегия recovery из chrome.storage' - }, - { - name: '✅ Обработка HTML_ASSEMBLED с recovery', - pattern: /HTML_ASSEMBLED.*recovery/, - description: 'Обработка HTML_ASSEMBLED должна включать recovery логику' - } -]; - -console.log('\n📋 ПРОВЕРКА ВАЖНЫХ КОМПОНЕНТОВ RECOVERY:'); -console.log('====================================='); - -let passedChecks = 0; -let failedChecks = 0; - -checks.forEach(check => { - const found = check.pattern.test(backgroundCode); - if (found) { - console.log(`✅ ${check.name}`); - console.log(` ${check.description}`); - passedChecks++; - } else { - console.log(`❌ ${check.name}`); - console.log(` ${check.description}`); - failedChecks++; - } - console.log(''); -}); - -console.log(`📊 РЕЗУЛЬТАТЫ АНАЛИЗА:`); -console.log(`✅ Пройдено проверок: ${passedChecks}`); -console.log(`❌ Провалено проверок: ${failedChecks}`); -console.log(`📈 Общий результат: ${passedChecks}/${passedChecks + failedChecks} (${Math.round(passedChecks / (passedChecks + failedChecks) * 100)}%)`); - -// Анализ ключевых строк кода -console.log('\n🔍 АНАЛИЗ КЛЮЧЕВЫХ СТРОК КОДА:'); -console.log('==============================='); - -// Ищем критические строки для recovery -const criticalPatterns = [ - { - name: 'Строка сохранения pluginId в метаданные', - pattern: /pluginId.*pendingWorkflow\.pluginId/ - }, - { - name: 'Строка сохранения pageKey в метаданные', - pattern: /pageKey.*transfer_/ - }, - { - name: 'Строка восстановления pluginId', - pattern: /pluginId.*savedMetadata.*pluginId/ - }, - { - name: 'Строка использования recovered pluginId', - pattern: /workflowPluginId.*recoveredMetadata\.pluginId/ - } -]; - -criticalPatterns.forEach(pattern => { - const match = backgroundCode.match(pattern.pattern); - if (match) { - console.log(`✅ Найдена: ${pattern.name}`); - console.log(` Совпадение: ${match[0].substring(0, 100)}...`); - } else { - console.log(`❌ НЕ НАЙДЕНА: ${pattern.name}`); - } -}); - -// Проверка на наличие проблемных паттернов -console.log('\n🚨 ПРОВЕРКА НА ПРОБЛЕМНЫЕ ПАТТЕРНЫ:'); -console.log('====================================='); - -const problemPatterns = [ - { - name: 'Отсутствие проверки на undefined pluginId', - pattern: /pluginId.*undefined/ - }, - { - name: 'Отсутствие проверки на undefined pageKey', - pattern: /pageKey.*undefined/ - } -]; - -problemPatterns.forEach(pattern => { - const match = backgroundCode.match(pattern.pattern); - if (match) { - console.log(`⚠️ ВОЗМОЖНАЯ ПРОБЛЕМА: ${pattern.name}`); - console.log(` Найдено: ${match[0]}`); - } else { - console.log(`✅ OK: ${pattern.name}`); - } -}); - -// Итоговые рекомендации -console.log('\n📋 РЕКОМЕНДАЦИИ ПО ТЕСТИРОВАНИЮ:'); -console.log('================================'); - -if (passedChecks >= 8) { - console.log('✅ Recovery механизм выглядит полноценно реализованным'); - console.log('Рекомендуется провести интеграционное тестирование с реальным браузером'); -} else { - console.log('⚠️ Recovery механизм требует доработки'); - console.log('Необходимо проверить отсутствующие компоненты'); -} - -console.log('\n🎯 СЛЕДУЮЩИЕ ШАГИ ТЕСТИРОВАНИЯ:'); -console.log('1. Запустить расширение в браузере'); -console.log('2. Открыть страницу с большим HTML (1MB+)'); -console.log('3. Запустить RUN_WORKFLOW для ozon-analyzer плагина'); -console.log('4. Мониторить логи на предмет:'); -console.log(' - "🔍 Starting enhanced multi-layer transfer check"'); -console.log(' - "✅ PluginId restored: ozon-analyzer"'); -console.log(' - "✅ PageKey restored: [URL]"'); -console.log(' - "🎉 Transfer recovery successful"'); -console.log('5. Убедиться в отсутствии ошибок:'); -console.log(' - "Cannot determine pluginId for recovered transfer"'); -console.log(' - "Failed to process recovered transfer: Error: Missing pluginId"'); - -console.log('\n🏁 АНАЛИЗ ЗАВЕРШЕН'); \ No newline at end of file diff --git a/test-recovery-large-html.html b/test-recovery-large-html.html deleted file mode 100644 index 67e3e5cf..00000000 --- a/test-recovery-large-html.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - Test Page for Recovery Mechanism - Large HTML Content - - - -

Test Product Page - Large HTML Content (~1MB)

-
-

Premium Wireless Headphones Pro Max Ultra

-
₽ 45,990
-

This is a premium wireless headphones with advanced noise cancellation technology, premium build quality, and exceptional sound performance. Perfect for audiophiles and professionals alike.

-
- -
-

Technical Specifications

-
Driver Size: 40mm dynamic drivers
-
Frequency Response: 20Hz - 20kHz
-
Battery Life: Up to 30 hours
-
Charging Time: 2 hours (USB-C)
-
Connectivity: Bluetooth 5.2, USB-C, 3.5mm jack
-
Weight: 320g
-
Dimensions: 200x180x80mm
-
Color Options: Black, White, Silver, Blue
-
Warranty: 2 years manufacturer warranty
-
- -
-
Product Image 1
-
Product Image 2
-
Product Image 3
-
Product Image 4
-
Product Image 5
-
Product Image 6
-
- -
-

Shipping Information

-

Free Shipping: Available for orders over ₽3,000

-

Delivery Time: 1-3 business days in Moscow, 3-7 days for regions

-

Pickup Points: Available at 2,500+ locations across Russia

-

International Shipping: Available to 50+ countries

-
- -
-

Customer Reviews (1,247 reviews)

- -
-

Excellent Sound Quality - 5 stars

-

I've been using these headphones for 2 months now and I'm extremely satisfied. The sound quality is outstanding with deep bass and clear highs. The noise cancellation works perfectly for my daily commute. Battery life is as advertised - I get about 28-30 hours on a single charge.

-

Pros: Great sound, comfortable fit, good battery life, ANC works well

-

Cons: A bit heavy for extended wear, expensive

-

By Alex M. on 2024-01-15

-
- -
-

Perfect for Work and Travel - 4 stars

-

These headphones are my go-to for work calls and travel. The microphone quality is excellent and people can hear me clearly even in noisy environments. The build quality feels premium and they've held up well to daily use.

-

The only reason I'm giving 4 stars instead of 5 is the weight - they can feel a bit heavy after wearing for several hours straight. But overall, very happy with the purchase.

-

Pros: Excellent mic, good ANC, premium build

-

Cons: Heavy weight

-

By Sarah K. on 2024-01-10

-
- - - - - -
-

Great Value for Money - 5 stars

-

After comparing many options, I chose these headphones and I'm glad I did. The audio quality rivals much more expensive brands. The noise cancellation is effective without being overbearing. Perfect for my daily work routine.

-

By Mike R. on 2024-01-08

-
- -
-

Comfortable for Long Sessions - 4 stars

-

I use these for gaming and they work great. The sound is immersive and the mic quality is clear. Only complaint is they're a bit bulky for travel, but that's expected for this level of performance.

-

By Jason T. on 2024-01-05

-
-
- -
-

Technical Details

-

Detailed technical specifications and performance metrics for the Premium Wireless Headphones Pro Max Ultra. This section contains comprehensive information about audio performance, connectivity options, and compatibility with various devices.

- - -
-

Audio Performance

-
    -
  • Frequency Response: 20 Hz - 20 kHz (±3dB)
  • -
  • Impedance: 32 ohms
  • -
  • Sensitivity: 98 dB SPL/mW
  • -
  • Total Harmonic Distortion: <0.1% at 1kHz
  • -
  • Signal-to-Noise Ratio: >95 dB
  • -
-
- -
-

Connectivity & Compatibility

-

Compatible with all Bluetooth-enabled devices including smartphones, tablets, laptops, and gaming consoles. Supports Bluetooth 5.2 with aptX HD, aptX Adaptive, AAC, and SBC codecs for high-quality audio streaming.

-
-
- - - - - \ No newline at end of file diff --git a/test-recovery-workflow.js b/test-recovery-workflow.js deleted file mode 100644 index cb8ddfff..00000000 --- a/test-recovery-workflow.js +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для тестирования recovery механизма -// Запускать в консоли DevTools расширения (не в консоли браузера!) - -console.log('🧪 Тестирование RECOVERY МЕХАНИЗМА для ozon-analyzer плагина...'); - -// Функция для запуска RUN_WORKFLOW теста -async function runRecoveryTest() { - try { - console.log('🚀 Запуск тестового сценария recovery механизма...'); - console.log('📋 План тестирования:'); - console.log('1. Открыть тестовую страницу с большим HTML'); - console.log('2. Запустить RUN_WORKFLOW для ozon-analyzer'); - console.log('3. Мониторить процесс передачи данных в чанках'); - console.log('4. Ждать обработки HTML_ASSEMBLED'); - console.log('5. Проверить восстановление pluginId и pageKey'); - - // Открываем тестовую страницу с большим HTML - const testPageUrl = chrome.runtime.getURL('test-recovery-large-html.html'); - console.log('📄 Открываем тестовую страницу:', testPageUrl); - - // Создаем новую вкладку с тестовой страницей - const tab = await chrome.tabs.create({ - url: testPageUrl, - active: true - }); - - console.log('✅ Тестовая страница открыта, tab ID:', tab.id); - - // Ждем загрузки страницы - await new Promise(resolve => setTimeout(resolve, 3000)); - - // Теперь переключаемся на эту вкладку и запускаем RUN_WORKFLOW - await chrome.tabs.update(tab.id, { active: true }); - - console.log('🔄 Запуск RUN_WORKFLOW для ozon-analyzer...'); - - // Запускаем RUN_WORKFLOW - const workflowResult = await chrome.runtime.sendMessage({ - type: 'RUN_WORKFLOW', - pluginId: 'ozon-analyzer', - requestId: `test_recovery_${Date.now()}` - }); - - console.log('✅ RUN_WORKFLOW завершен:', workflowResult); - - // Мониторим процесс - console.log('📊 Мониторинг процесса передачи данных...'); - console.log('🔍 Ищем логи:'); - console.log('- "🔍 Starting enhanced multi-layer transfer check"'); - console.log('- "🔄 Found potential global storage keys"'); - console.log('- "✅ Transfer metadata recovered from chrome.storage"'); - console.log('- "✅ PluginId restored: ozon-analyzer"'); - console.log('- "✅ PageKey restored: ..."'); - console.log('- "✅ Recovery transfer completion flag set"'); - console.log('- "🎉 Transfer recovery successful"'); - - // Ждем завершения процесса - await new Promise(resolve => setTimeout(resolve, 10000)); - - console.log('🏁 Тест завершен. Проверьте логи выше на наличие ошибок recovery.'); - - return { - success: workflowResult?.success || false, - requestId: workflowResult?.requestId, - tabId: tab.id, - testPageUrl - }; - - } catch (error) { - console.error('❌ Ошибка во время тестирования recovery:', error); - - // Проверяем на специфические ошибки recovery - if (error.message.includes('Cannot determine pluginId for recovered transfer')) { - console.error('🚨 ОБНАРУЖЕНА ОШИБКА RECOVERY: "Cannot determine pluginId for recovered transfer"'); - console.error('Это означает проблему с восстановлением pluginId из метаданных'); - } - - if (error.message.includes('Failed to process recovered transfer')) { - console.error('🚨 ОБНАРУЖЕНА ОШИБКА RECOVERY: "Failed to process recovered transfer"'); - console.error('Это означает проблему с обработкой восстановленного transfer'); - } - - return { - success: false, - error: error.message - }; - } -} - -// Функция для проверки статуса transfer'а -async function checkTransferStatus(transferId) { - try { - console.log('🔍 Проверка статуса transfer:', transferId); - - const status = await chrome.runtime.sendMessage({ - type: 'CHECK_TRANSFER_STATUS', - transferId - }); - - console.log('📊 Статус transfer:', status); - return status; - } catch (error) { - console.error('❌ Ошибка проверки статуса transfer:', error); - return null; - } -} - -// Функция для тестирования с разными размерами HTML -async function runMultipleRecoveryTests() { - console.log('🔬 Запуск серии тестов recovery с разными размерами HTML...'); - - const testSizes = [ - { name: 'Small HTML', size: 50000 }, - { name: 'Medium HTML', size: 500000 }, - { name: 'Large HTML', size: 1000000 }, - { name: 'Extra Large HTML', size: 2000000 } - ]; - - const results = []; - - for (const testCase of testSizes) { - console.log(`\n🧪 Тестирование: ${testCase.name} (${testCase.size} chars)`); - - try { - const result = await runRecoveryTest(); - results.push({ - testCase: testCase.name, - size: testCase.size, - success: result.success, - error: result.error - }); - - console.log(`✅ Тест ${testCase.name}: ${result.success ? 'ПРОЙДЕН' : 'ПРОВАЛЕН'}`); - - // Пауза между тестами - await new Promise(resolve => setTimeout(resolve, 5000)); - - } catch (error) { - console.error(`❌ Тест ${testCase.name} завершился с ошибкой:`, error); - results.push({ - testCase: testCase.name, - size: testCase.size, - success: false, - error: error.message - }); - } - } - - console.log('\n📋 РЕЗУЛЬТАТЫ СЕРИИ ТЕСТОВ:'); - results.forEach(result => { - console.log(`${result.testCase}: ${result.success ? '✅' : '❌'} ${result.error || 'OK'}`); - }); - - return results; -} - -// Экспортируем функции для использования в консоли -window.recoveryTestSystem = { - runRecoveryTest, - checkTransferStatus, - runMultipleRecoveryTests, - getCurrentUrl: async () => { - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - return tab.url; - } -}; - -console.log('🎯 Функции тестирования recovery доступны:'); -console.log('- recoveryTestSystem.runRecoveryTest() - запуск основного теста recovery'); -console.log('- recoveryTestSystem.checkTransferStatus(transferId) - проверка статуса transfer'); -console.log('- recoveryTestSystem.runMultipleRecoveryTests() - серия тестов с разными размерами'); -console.log('- recoveryTestSystem.getCurrentUrl() - получение текущего URL'); - -// Автоматический запуск основного теста -console.log('🚀 Автоматический запуск теста recovery через 3 секунды...'); -setTimeout(() => { - runRecoveryTest().then(result => { - console.log('🏁 Автоматический тест завершен:', result); - }).catch(error => { - console.error('💥 Автоматический тест завершился с ошибкой:', error); - }); -}, 3000); \ No newline at end of file diff --git a/test-scripts/test-prompt-system.js b/test-scripts/test-prompt-system.js index bee29cd9..fff9668f 100644 --- a/test-scripts/test-prompt-system.js +++ b/test-scripts/test-prompt-system.js @@ -75,9 +75,9 @@ class PromptSystemTester { try { const customPromptsSettings = { prompts: { - optimized: { - ru: 'КАСТОМНЫЙ ПРОМПТ ДЛЯ ТЕСТИРОВАНИЯ - OPTIMIZED RU', - en: 'CUSTOM PROMPT FOR TESTING - OPTIMIZED EN' + basic_analysis: { + ru: 'КАСТОМНЫЙ ПРОМПТ ДЛЯ ТЕСТИРОВАНИЯ - basic_analysis RU', + en: 'CUSTOM PROMPT FOR TESTING - basic_analysis EN' } } }; @@ -85,7 +85,7 @@ class PromptSystemTester { const testResult = await this.executePythonCode(` try: prompts = get_user_prompts(${JSON.stringify(customPromptsSettings)}) - optimized_ru = prompts.get('optimized', {}).get('ru', '') + optimized_ru = prompts.get('basic_analysis', {}).get('ru', '') custom_found = 'КАСТОМНЫЙ ПРОМПТ ДЛЯ ТЕСТИРОВАНИЯ' in optimized_ru return { @@ -119,7 +119,7 @@ class PromptSystemTester { try: empty_settings = {} prompts = get_user_prompts(empty_settings) - optimized_ru = prompts.get('optimized', {}).get('ru', '') + optimized_ru = prompts.get('basic_analysis', {}).get('ru', '') manifest_used = len(optimized_ru) > 100 return { @@ -153,14 +153,14 @@ class PromptSystemTester { try: plugin_settings = { 'prompts': { - 'optimized': { + 'basic_analysis': { 'ru': 'ТЕСТОВЫЙ ПРОМПТ ДЛЯ ПРОВЕРКИ AI: Проанализируй состав: {composition}' } } } prompts = get_user_prompts(plugin_settings) - optimized_ru = prompts.get('optimized', {}).get('ru', '') + optimized_ru = prompts.get('basic_analysis', {}).get('ru', '') has_placeholder = '{composition}' in optimized_ru sufficient_length = len(optimized_ru) > 50 diff --git a/test_custom_prompts_scenario.py b/test_custom_prompts_scenario.py deleted file mode 100644 index 5d1e9a36..00000000 --- a/test_custom_prompts_scenario.py +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 2: Проверка кастомных промптов с переопределением - -Цель: Проверить, что кастомные промпты из пользовательских настроек -корректно переопределяют оригинальные промпты из manifest.json -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - # Имитируем наличие manifest.json в globals - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопированная из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ 2) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - else: - manifest_prompts = {} - - # Используем кастомные промпты из plugin_settings - if plugin_settings and isinstance(plugin_settings, dict): - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" Кастомные промпты найдены: {custom_prompts_raw}") - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка кастомного промпта {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback на manifest.json - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - else: - console_log("ℹ️ Пользовательские настройки не переданы или некорректны - используем manifest") - - # Fallback на manifest.json - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт из manifest: {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_2(): - """Выполнение тестового сценария 2.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 2: Проверка кастомных промптов с переопределением") - print("=" * 80) - - # Тест 1: Проверка кастомных промптов с переопределением - print("\n📋 ТЕСТ 1: Кастомные промпты должны переопределять оригинальные") - print("-" * 60) - - # Кастомные промпты для тестирования - custom_prompts = { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ОПТИМИЗИРОВАННЫЙ ПРОМПТ НА РУССКОМ ЯЗЫКЕ для анализа косметики', - 'en': 'CUSTOM OPTIMIZED PROMPT IN ENGLISH for cosmetic analysis' - }, - 'deep': { - 'ru': 'КАСТОМНЫЙ ГЛУБОКИЙ ПРОМПТ НА РУССКОМ ЯЗЫКЕ для детального анализа', - 'en': 'CUSTOM DEEP PROMPT IN ENGLISH for detailed analysis' - } - } - - # Пользовательские настройки с кастомными промптами - plugin_settings = { - 'response_language': 'ru', - 'prompts': custom_prompts - } - - try: - prompts = get_user_prompts(plugin_settings) - - # Проверяем что кастомные промпты загружены - custom_loaded = 0 - total_custom = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - expected_value = custom_prompts[prompt_type][lang] - - if prompt_value == expected_value: - custom_loaded += 1 - print(f" ✅ {prompt_type}.{lang}: кастомный промпт загружен корректно") - else: - print(f" ❌ {prompt_type}.{lang}: кастомный промпт НЕ загружен (ожидали: '{expected_value[:50]}...', получили: '{prompt_value[:50]}...')") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 1: {custom_loaded}/{total_custom} кастомных промптов загружено") - - if custom_loaded == total_custom: - print("✅ ТЕСТ 1 ПРОЙДЕН: Кастомные промпты корректно переопределяют оригинальные") - else: - print("❌ ТЕСТ 1 НЕ ПРОЙДЕН: Кастомные промпты не переопределяют оригинальные") - - except Exception as e: - print(f"❌ ТЕСТ 1 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 2: Частичное переопределение промптов - print("\n📋 ТЕСТ 2: Частичное переопределение промптов") - print("-" * 60) - - # Только часть промптов кастомные - partial_custom_prompts = { - 'optimized': { - 'ru': 'ЧАСТИЧНО КАСТОМНЫЙ ПРОМПТ ТОЛЬКО ДЛЯ РУССКОГО' - }, - 'deep': { - 'en': 'PARTIAL CUSTOM PROMPT ONLY FOR ENGLISH' - } - } - - partial_plugin_settings = { - 'response_language': 'ru', - 'prompts': partial_custom_prompts - } - - try: - prompts = get_user_prompts(partial_plugin_settings) - - # Проверяем частичное переопределение - partial_checks = [ - ('optimized.ru', partial_custom_prompts['optimized']['ru']), - ('deep.en', partial_custom_prompts['deep']['en']) - ] - - partial_correct = 0 - - for prompt_key, expected_value in partial_checks: - prompt_type, lang = prompt_key.split('.') - actual_value = prompts[prompt_type][lang] - - if actual_value == expected_value: - partial_correct += 1 - print(f" ✅ {prompt_key}: кастомный промпт загружен") - else: - print(f" ❌ {prompt_key}: кастомный промпт НЕ загружен") - - # Проверяем что остальные промпты взяты из manifest - other_prompts_count = 0 - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_key = f"{prompt_type}.{lang}" - if (prompt_type, lang) not in [('optimized', 'ru'), ('deep', 'en')]: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - other_prompts_count += 1 - print(f" ✅ {prompt_key}: промпт из manifest загружен") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 2: {partial_correct}/2 кастомных + {other_prompts_count}/2 из manifest") - - if partial_correct == 2 and other_prompts_count == 2: - print("✅ ТЕСТ 2 ПРОЙДЕН: Частичное переопределение работает корректно") - else: - print("❌ ТЕСТ 2 НЕ ПРОЙДЕН: Частичное переопределение не работает") - - except Exception as e: - print(f"❌ ТЕСТ 2 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 3: Пустые или некорректные кастомные промпты - print("\n📋 ТЕСТ 3: Обработка пустых или некорректных кастомных промптов") - print("-" * 60) - - # Кастомные промпты с пустыми значениями - empty_custom_prompts = { - 'optimized': { - 'ru': '', # Пустой промпт - 'en': ' ' # Только пробелы - }, - 'deep': { - 'ru': None, # None значение - 'en': 'ВАЛИДНЫЙ КАСТОМНЫЙ ПРОМПТ' # Валидный промпт - } - } - - empty_plugin_settings = { - 'response_language': 'ru', - 'prompts': empty_custom_prompts - } - - try: - prompts = get_user_prompts(empty_plugin_settings) - - # Проверяем обработку пустых значений - empty_checks = [ - ('optimized.ru', False), # Должен быть взят из manifest - ('optimized.en', False), # Должен быть взят из manifest - ('deep.ru', False), # Должен быть взят из manifest - ('deep.en', True) # Должен быть кастомный - ] - - empty_correct = 0 - - for prompt_key, should_be_custom in empty_checks: - prompt_type, lang = prompt_key.split('.') - actual_value = prompts[prompt_type][lang] - expected_custom = empty_custom_prompts[prompt_type][lang] - - if should_be_custom: - # Должен быть кастомный промпт - if actual_value == expected_custom: - empty_correct += 1 - print(f" ✅ {prompt_key}: кастомный промпт сохранен") - else: - print(f" ❌ {prompt_key}: кастомный промпт потерян") - else: - # Должен быть взят из manifest - if actual_value != expected_custom and len(actual_value.strip()) > 0: - empty_correct += 1 - print(f" ✅ {prompt_key}: взят из manifest при пустом кастомном") - else: - print(f" ❌ {prompt_key}: не взят из manifest при пустом кастомном") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 3: {empty_correct}/4 проверок корректно обработано") - - if empty_correct >= 3: # Допускаем 1 ошибку для некритических сценариев - print("✅ ТЕСТ 3 ПРОЙДЕН: Пустые промпты обрабатываются корректно") - else: - print("❌ ТЕСТ 3 НЕ ПРОЙДЕН: Пустые промпты обрабатываются некорректно") - - except Exception as e: - print(f"❌ ТЕСТ 3 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - print("\n" + "=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 2") - print("=" * 80) - -if __name__ == "__main__": - run_test_scenario_2() \ No newline at end of file diff --git a/test_fallback_damaged_manifest.py b/test_fallback_damaged_manifest.py deleted file mode 100644 index a810eae1..00000000 --- a/test_fallback_damaged_manifest.py +++ /dev/null @@ -1,458 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 3: Тестирование fallback при повреждении manifest.json - -Цель: Проверить, что функция get_user_prompts() корректно обрабатывает -поврежденный или отсутствующий manifest.json и использует встроенные промпты -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals с возможностью повреждения.""" - if name == 'manifest': - # Имитируем различные сценарии повреждения manifest.json - damage_scenario = os.environ.get('DAMAGE_SCENARIO', 'normal') - - if damage_scenario == 'normal': - # Нормальный manifest.json - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif damage_scenario == 'missing': - # Manifest отсутствует - console_log("ИМИТАЦИЯ: manifest отсутствует в globals") - return default - - elif damage_scenario == 'empty': - # Manifest пустой - console_log("ИМИТАЦИЯ: manifest пустой") - return {} - - elif damage_scenario == 'invalid_json': - # Manifest содержит некорректный JSON - console_log("ИМИТАЦИЯ: manifest содержит некорректный JSON") - return "{ invalid json" - - elif damage_scenario == 'missing_options': - # Manifest без секции options - console_log("ИМИТАЦИЯ: manifest без секции options") - return {"name": "Ozon Analyzer", "version": "1.0.0"} - - elif damage_scenario == 'missing_prompts': - # Manifest без секции prompts в options - console_log("ИМИТАЦИЯ: manifest без секции prompts") - return { - "name": "Ozon Analyzer", - "version": "1.0.0", - "options": { - "response_language": "ru" - } - } - - elif damage_scenario == 'corrupted_prompts': - # Manifest с поврежденной секцией prompts - console_log("ИМИТАЦИЯ: manifest с поврежденной секцией prompts") - return { - "name": "Ozon Analyzer", - "version": "1.0.0", - "options": { - "prompts": { - "optimized": { - "ru": None, # Поврежденное значение - "en": "" # Пустое значение - } - } - } - } - - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """ - Встроенные промпты по умолчанию (скопировано из mcp_server.py). - """ - builtin_prompts = { - 'optimized': { - 'ru': """Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки. - -ДАННЫЕ: -Описание: {description} -Состав: {composition} - -ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА: - -1. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ: -- Термины типа "3D/4D/5D", "революционный", "инновационный" - ТРЕБУЮТ доказательств -- Для каждого заявления ("лифтинг", "против морщин"): - * Найди КОНКРЕТНЫЙ активный компонент - * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг) - * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте - -2. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1): -- Проверь КАЖДЫЙ компонент на: - * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, и т.д.) - * Парабены (особенно butyl-, propyl-) - * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone) - * Потенциальные эндокринные дизрапторы -- Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10 - -3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ: -- Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален -- Collagen/Elastin: молекулы НЕ проникают, работают только как пленка -- Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины - -4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ: -- Современная косметика = без парабенов, с новыми консервантами -- Устаревшие формулы → снижение оценки на 2-3 балла - -5. ШКАЛА ОЦЕНКИ (СТРОГАЯ): -- 9-10: Идеальный состав, доказанные активы в высоких концентрациях, без токсичных компонентов -- 7-8: Хороший состав, минимум проблемных компонентов -- 5-6: Средний продукт, есть проблемные компоненты ИЛИ активы в низких дозах -- 3-4: Устаревшая формула, много токсичных компонентов, маркетинговые заявления не подтверждены -- 1-2: Опасный или полностью бесполезный продукт - -КРИТИЧЕСКИ ВАЖНО: -- Будь СКЕПТИЧЕН к маркетингу -- НЕ завышай оценку из вежливости -- Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10 -- Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку - -ФОРМАТ ОТВЕТА - ТОЛЬКО JSON: -{{ -"score": число_от_1_до_10, -"reasoning": "ДЕТАЛЬНЫЙ анализ: - 1. Проверка маркетинга: [разбери каждое заявление] - 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты] - 3. Реальная эффективность активов: [концентрации vs заявления] - 4. Сравнение с современными стандартами: [почему устарел/актуален] - 5. Итоговый вердикт: [честное заключение]", -"confidence": число_от_0_до_1, -"red_flags": ["список всех токсичных/проблемных компонентов"], -"marketing_lies": ["список не подтвержденных маркетинговых заявлений"] -}} - -ЯЗЫК: Русский, технический стиль с примерами.""", - 'en': """You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation. - -DATA: -Description: {description} -Composition: {composition} - -MANDATORY ANALYSIS PROTOCOL: - -1. TOXICOLOGICAL SCREENING (highest priority): -- Screen EVERY ingredient for: - * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea) - * Parabens (particularly butylparaben, propylparaben - EU restricted) - * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone) - * Known/suspected endocrine disruptors -- HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum - -2. MARKETING CLAIMS VERIFICATION: -- Buzzwords like "3D/4D/5D technology", "revolutionary", "clinical breakthrough" - DEMAND evidence -- For each claim ("lifting", "anti-wrinkle", "firming"): - * Identify the SPECIFIC active ingredient responsible - * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose) - * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product - -3. REALISTIC EFFICACY ASSESSMENT: -- Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect -- Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers -- Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep wrinkles - -4. MODERN FORMULATION STANDARDS COMPARISON: -- 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails -- Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction - -5. EVIDENCE-BASED SCORING RUBRIC (strict grading): -- 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile -- 7-8: Well-formulated, minor concerns only, actives present at reasonable levels -- 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims) -- 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing -- 1-2: Potentially harmful or fraudulent product - -CRITICAL ASSESSMENT RULES: -- Maintain scientific skepticism toward all marketing language -- Apply evidence-based standards, NOT brand reputation -- Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10 -- Claims unsupported by adequate active concentrations = reduce score proportionally -- Default to LOWER score when ingredient concentrations are ambiguous - -OUTPUT FORMAT - VALID JSON ONLY: -{{ -"score": integer_1_to_10, -"reasoning": "COMPREHENSIVE ANALYSIS: - 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks] - 2. Marketing Claims Audit: [fact-check each claim against ingredient reality] - 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence] - 4. Formulation Modernity Assessment: [evaluate against current industry standards] - 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]", -"confidence": float_0_to_1, -"red_flags": ["comprehensive list of problematic/toxic/outdated ingredients"], -"marketing_lies": ["specific unsubstantiated or misleading marketing claims"] -}} - -RESPONSE LANGUAGE: English, using precise technical terminology.""" - }, - 'deep': { - 'ru': """Проведи глубокий анализ товара с медицинской и научной точки зрения. -Описание: {description} -Состав: {composition} -Проанализируй: -1. Научную обоснованность заявленных свойств. -2. Потенциальные побочные эффекты и противопоказания. -3. Эффективность по сравнению с аналогами. -Верни детальный максимально подробный обоснованный анализ в структурированном виде (используй Markdown).""", - 'en': """Conduct a deep analysis of the product from a medical and scientific perspective. -Description: {description} -Composition: {composition} -Analyze: -1. Scientific validity of the claimed properties. -2. Potential side effects and contraindications. -3. Effectiveness compared to analogs. -Return a detailed, maximally comprehensive, reasoned analysis in a structured form (use Markdown).""" - } - } - - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов с fallback логикой (скопировано из mcp_server.py). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ 3) =====") - - try: - # Диагностика доступа к manifest - console_log("🔍 ДИАГНОСТИКА ДОСТУПА К MANIFEST:") - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - else: - console_log(" ❌ manifest НЕ НАЙДЕН в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - - # Структура промптов - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log(" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_3(): - """Выполнение тестового сценария 3.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 3: Тестирование fallback при повреждении manifest.json") - print("=" * 80) - - # Сценарии повреждения manifest.json - damage_scenarios = [ - ('normal', 'Нормальный manifest.json'), - ('missing', 'Отсутствующий manifest'), - ('empty', 'Пустой manifest'), - ('invalid_json', 'Некорректный JSON в manifest'), - ('missing_options', 'Отсутствует секция options'), - ('missing_prompts', 'Отсутствует секция prompts'), - ('corrupted_prompts', 'Поврежденная секция prompts') - ] - - results = {} - - for scenario, description in damage_scenarios: - print(f"\n📋 ТЕСТ: {description}") - print("-" * 60) - - # Устанавливаем переменную окружения для сценария повреждения - os.environ['DAMAGE_SCENARIO'] = scenario - - try: - prompts = get_user_prompts() - - # Проверяем что промпты загружены - loaded_count = 0 - builtin_count = 0 - total_count = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_count += 1 - # Проверяем является ли промпт встроенным (fallback) - builtin_prompt = _get_builtin_default_prompt(prompt_type, lang) - if prompt_value == builtin_prompt: - builtin_count += 1 - - print(f" Загружено промптов: {loaded_count}/{total_count}") - print(f" Из них встроенных (fallback): {builtin_count}") - - if scenario == 'normal': - # Для нормального сценария должны быть загружены все промпты из manifest - if loaded_count == total_count: - print("✅ НОРМАЛЬНЫЙ СЦЕНАРИЙ ПРОЙДЕН: Все промпты загружены из manifest") - results[scenario] = "PASSED" - else: - print("❌ НОРМАЛЬНЫЙ СЦЕНАРИЙ НЕ ПРОЙДЕН: Не все промпты загружены") - results[scenario] = "FAILED" - else: - # Для поврежденных сценариев должен сработать fallback - if builtin_count > 0: - print(f"✅ FALLBACK СЦЕНАРИЙ ПРОЙДЕН: Использовано {builtin_count} встроенных промптов") - results[scenario] = "PASSED" - else: - print("❌ FALLBACK СЦЕНАРИЙ НЕ ПРОЙДЕН: Fallback не сработал") - results[scenario] = "FAILED" - - except Exception as e: - print(f"❌ ТЕСТ НЕ ПРОЙДЕН: Критическая ошибка: {e}") - results[scenario] = "ERROR" - - # Итоговый отчет - print("\n" + "=" * 80) - print("📊 ИТОГОВЫЙ ОТЧЕТ ПО ТЕСТИРОВАНИЮ FALLBACK") - print("=" * 80) - - passed_count = sum(1 for result in results.values() if result == "PASSED") - total_count = len(results) - - print(f"Пройдено сценариев: {passed_count}/{total_count}") - - for scenario, result in results.items(): - status_icon = "✅" if result == "PASSED" else "❌" if result == "FAILED" else "⚠️" - description = next(desc for sc, desc in damage_scenarios if sc == scenario) - print(f" {status_icon} {description}: {result}") - - if passed_count == total_count: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ: Fallback логика работает корректно") - elif passed_count >= total_count * 0.8: # 80% успеха - print(f"\n⚠️ ЧАСТИЧНЫЙ УСПЕХ: {passed_count}/{total_count} тестов пройдено") - print("Рекомендуется проверить сценарии, которые не прошли") - else: - print(f"\n❌ НЕУДАЧА: Только {passed_count}/{total_count} тестов пройдено") - print("Необходимо исправить fallback логику") - - print("=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 3") - print("=" * 80) - - # Очищаем переменную окружения - if 'DAMAGE_SCENARIO' in os.environ: - del os.environ['DAMAGE_SCENARIO'] - -if __name__ == "__main__": - run_test_scenario_3() \ No newline at end of file diff --git a/test_fixes.sh b/test_fixes.sh deleted file mode 100755 index ec5c469c..00000000 --- a/test_fixes.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -echo "=== ТЕСТИРОВАНИЕ ИСПРАВЛЕНИЙ ===" -echo - -# Тест 1: Проверка сборки проекта -echo "Тест 1: Проверка сборки проекта" -if [ -d "dist" ] && [ -f "dist/manifest.json" ] && [ -f "dist/background.js" ]; then - echo "✅ Сборка проекта успешна - директория dist существует" -else - echo "❌ Сборка проекта не удалась - директория dist отсутствует" - exit 1 -fi -echo - -# Тест 2: Проверка manifest.json -echo "Тест 2: Проверка manifest.json" -if [ -f "dist/manifest.json" ]; then - # Проверка валидности JSON с помощью node - if node -e "try { JSON.parse(require('fs').readFileSync('dist/manifest.json', 'utf8')); console.log('valid'); } catch(e) { console.log('invalid'); }" | grep -q "valid"; then - echo "✅ manifest.json валиден" - else - echo "❌ manifest.json невалиден" - fi - - # Проверка наличия "*.html" в web_accessible_resources - if grep -q '"\*\.html"' dist/manifest.json; then - echo "✅ '*.html' найден в web_accessible_resources" - else - echo "❌ '*.html' отсутствует в web_accessible_resources" - fi - - # Проверка наличия offscreen_document - if grep -q '"offscreen_document"' dist/manifest.json && grep -q '"offscreen.html"' dist/manifest.json; then - echo "✅ offscreen_document корректно настроен" - else - echo "❌ offscreen_document не найден или некорректен" - fi -else - echo "❌ manifest.json не найден" -fi -echo - -# Тест 3: Проверка background.js -echo "Тест 3: Проверка background.js" -if [ -f "dist/background.js" ]; then - echo "✅ background.js найден" - - # Проверка наличия импортов (упрощенная проверка) - if grep -q "import" dist/background.js; then - echo "✅ Импорты найдены в background.js" - else - echo "❌ Импорты не найдены в background.js" - fi - - # Проверка наличия try/catch блоков - if grep -q "try {" dist/background.js && grep -q "catch" dist/background.js; then - echo "✅ Try/catch блоки найдены в background.js" - else - echo "❌ Try/catch блоки не найдены в background.js" - fi - - # Проверка наличия ensureOffscreenDocument - if grep -q "ensureOffscreenDocument" dist/background.js; then - echo "✅ ensureOffscreenDocument найден в background.js" - else - echo "❌ ensureOffscreenDocument не найден в background.js" - fi -else - echo "❌ background.js не найден" -fi -echo - -# Тест 4: Проверка offscreen файлов -echo "Тест 4: Проверка offscreen файлов" -if [ -f "chrome-extension/public/offscreen.html" ]; then - echo "✅ offscreen.html найден" - - if grep -q "offscreen-init.js" chrome-extension/public/offscreen.html; then - echo "✅ offscreen-init.js подключен в offscreen.html" - else - echo "❌ offscreen-init.js не подключен в offscreen.html" - fi -else - echo "❌ offscreen.html не найден" -fi - -if [ -f "chrome-extension/public/offscreen-init.js" ]; then - echo "✅ offscreen-init.js найден" - - if grep -q "pyodide/pyodide.js" chrome-extension/public/offscreen-init.js && grep -q "offscreen.js" chrome-extension/public/offscreen-init.js; then - echo "✅ Пути к pyodide.js и offscreen.js корректны" - else - echo "❌ Пути к pyodide.js или offscreen.js некорректны" - fi -else - echo "❌ offscreen-init.js не найден" -fi - -if [ -f "chrome-extension/public/offscreen.js" ]; then - echo "✅ offscreen.js найден" -else - echo "❌ offscreen.js не найден" -fi -echo - -# Тест 5: Проверка heartbeat механизма -echo "Тест 5: Проверка heartbeat механизма" -if grep -q "heartbeat\|connection.monitoring\|retry.logic" dist/background.js 2>/dev/null; then - echo "✅ Heartbeat механизм найден в коде" -else - echo "⚠️ Heartbeat механизм не найден (может быть в другом формате)" -fi -echo - -echo "=== ТЕСТИРОВАНИЕ ЗАВЕРШЕНО ===" \ No newline at end of file diff --git a/test_get_user_prompts.py b/test_get_user_prompts.py deleted file mode 100644 index 644d793d..00000000 --- a/test_get_user_prompts.py +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий для проверки корректной работы функции get_user_prompts. - -Этот тест проверяет: -1. Корректный доступ к кастомным промптам из структуры plugin_settings -2. Правильное извлечение дефолтных промптов из манифеста -3. Логирование диагностической информации -4. Обработку ошибок и fallback сценарии -""" - -import asyncio -import json -import sys -import os -from typing import Dict, Any, Optional - -# Добавляем текущую директорию в путь для импорта -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -# Мокаем функции JavaScript bridge для тестирования -class MockJSBridge: - """Мок JavaScript bridge для тестирования.""" - - def __init__(self): - self.logged_messages = [] - - def consoleLog(self, message: str): - """Мокируем console.log.""" - self.logged_messages.append(message) - print(f"[MOCK_CONSOLE] {message}") - - def sendMessageToChat(self, message: Dict[str, Any]): - """Мокируем отправку сообщения в чат.""" - self.logged_messages.append(f"CHAT: {message}") - print(f"[MOCK_CHAT] {message}") - -# Глобальный мок JS bridge -mock_js = MockJSBridge() - -def console_log(message: str, force: bool = False): - """Мокированная функция логирования.""" - mock_js.consoleLog(f"[TEST_LOG] {message}") - -def chat_message(message: str, message_type: str = None): - """Мокированная функция отправки сообщения в чат.""" - mock_js.sendMessageToChat({"content": message}) - -# Мокируем Pyodide globals -class MockGlobals: - """Мок Pyodide globals для тестирования.""" - - def __init__(self, data: Dict[str, Any]): - self.data = data - - def __contains__(self, key: str) -> bool: - return key in self.data - - def __getitem__(self, key: str) -> Any: - return self.data[key] - - def keys(self): - return self.data.keys() - -# Импортируем функцию get_user_prompts из основного модуля -# Поскольку мы не можем напрямую импортировать из-за зависимостей, -# создадим локальную копию функции для тестирования - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасная функция получения значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Мокированная функция получения переменной из Pyodide globals.""" - try: - if name in globals(): - value = globals()[name] - return value - else: - return default - except Exception as e: - return default - -def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - """ - Локальная копия функции get_user_prompts для тестирования. - - Загружает промпты из настроек пользователя или использует значения по умолчанию из manifest.json. - """ - try: - console_log("🔍 НАЧАЛО ТЕСТИРОВАНИЯ get_user_prompts") - console_log(f"📋 Входящие plugin_settings: {plugin_settings}") - console_log(f"📋 Тип plugin_settings: {type(plugin_settings)}") - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - - console_log(f"📋 Извлеченные кастомные промпты: {custom_prompts_raw}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - manifest_prompts = safe_dict_get(manifest, 'options.prompts', {}) - - console_log(f"📋 Manifest промпты: {manifest_prompts}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - - custom_value = safe_dict_get(prompt_type_data, lang, '') - - console_log(f"🔍 Проверка промпта {prompt_type}.{lang}:") - console_log(f" - Кастомное значение: '{custom_value}'") - console_log(f" - Длина кастомного значения: {len(custom_value) if custom_value else 0}") - console_log(f" - Тип кастомного значения: {type(custom_value)}") - console_log(f" - Проверка на непустую строку: {isinstance(custom_value, str) and len(custom_value.strip()) > 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - # Fallback на manifest.json - lang_data = safe_dict_get(manifest_prompts, f'{prompt_type}.{lang}', {}) - - manifest_value = safe_dict_get(lang_data, 'default', '') - prompts[prompt_type][lang] = manifest_value - console_log(f"ℹ️ Используем промпт по умолчанию: {prompt_type}.{lang}") - - console_log(f"🔍 Диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Final prompts structure: {prompts}") - console_log(f"📋 Загружено промптов: {len([p for pt in prompts.values() for p in pt.values() if p])} кастомных") - return prompts - - except Exception as e: - console_log(f"❌ Ошибка загрузки промптов: {str(e)}") - console_log(f"🔍 Диагностика ошибки:") - console_log(f" Plugin settings: {plugin_settings}") - console_log(f" Plugin settings type: {type(plugin_settings)}") - # Критический fallback - возвращаем пустые промпты - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -class TestGetUserPrompts: - """Класс для тестирования функции get_user_prompts.""" - - def __init__(self): - self.test_results = [] - self.manifest = { - 'options': { - 'prompts': { - 'optimized.ru': {'default': 'Оптимизированный промпт по умолчанию (RU)'}, - 'optimized.en': {'default': 'Default optimized prompt (EN)'}, - 'deep.ru': {'default': 'Глубокий промпт по умолчанию (RU)'}, - 'deep.en': {'default': 'Default deep prompt (EN)'} - } - } - } - - def run_test(self, test_name: str, plugin_settings: Dict[str, Any], expected_custom_count: int): - """Запуск одного теста.""" - print(f"\n🧪 ТЕСТ: {test_name}") - print("=" * 50) - - # Устанавливаем глобальные переменные для теста - global manifest - manifest = self.manifest - - # Очищаем логи перед тестом - mock_js.logged_messages = [] - - # Выполняем тестируемую функцию - result = get_user_prompts(plugin_settings) - - # Анализируем результаты - custom_prompts_count = len([p for pt in result.values() for p in pt.values() if p]) - - print(f"📊 Результат теста:") - print(f" - Количество кастомных промптов: {custom_prompts_count}") - print(f" - Ожидаемое количество: {expected_custom_count}") - print(f" - Структура результата: {result}") - - # Проверяем логи - print(f"📋 Логи теста ({len(mock_js.logged_messages)} сообщений):") - for i, log in enumerate(mock_js.logged_messages[-10:], 1): # Последние 10 логов - print(f" {i}. {log}") - - # Определяем успешность теста - success = custom_prompts_count == expected_custom_count - self.test_results.append({ - 'test_name': test_name, - 'success': success, - 'expected': expected_custom_count, - 'actual': custom_prompts_count, - 'result': result, - 'logs': mock_js.logged_messages.copy() - }) - - print(f"✅ ТЕСТ {'ПРОЙДЕН' if success else 'ПРОВАЛЕН'}") - return success - - def run_all_tests(self): - """Запуск всех тестов.""" - print("🚀 НАЧАЛО ТЕСТИРОВАНИЯ ФУНКЦИИ get_user_prompts") - print("=" * 60) - - # Тест 1: Корректные кастомные промпты - self.run_test( - "Корректные кастомные промпты", - { - 'prompts': { - 'optimized': { - 'ru': 'Кастомный оптимизированный промпт (RU)', - 'en': 'Custom optimized prompt (EN)' - }, - 'deep': { - 'ru': 'Кастомный глубокий промпт (RU)', - 'en': 'Custom deep prompt (EN)' - } - } - }, - 4 # Все 4 промпта кастомные - ) - - # Тест 2: Пустые промпты (fallback на manifest) - self.run_test( - "Пустые промпты (fallback на manifest)", - { - 'prompts': { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - }, - 0 # Нет кастомных промптов - ) - - # Тест 3: Отсутствующие промпты - self.run_test( - "Отсутствующие промпты", - {}, - 0 # Нет кастомных промптов - ) - - # Тест 4: Неправильная структура промптов - self.run_test( - "Неправильная структура промптов", - { - 'prompts': 'неправильная_структура' - }, - 0 # Нет кастомных промптов из-за неправильной структуры - ) - - # Тест 5: Частично заполненные промпты - self.run_test( - "Частично заполненные промпты", - { - 'prompts': { - 'optimized': { - 'ru': 'Только русский оптимизированный промпт' - }, - 'deep': { - 'en': 'Only English deep prompt' - } - } - }, - 2 # Только 2 кастомных промпта - ) - - # Тест 6: Промпты с пробелами (должны игнорироваться) - self.run_test( - "Промпты с пробелами", - { - 'prompts': { - 'optimized': { - 'ru': ' ', # Только пробелы - 'en': '\t\n' # Табуляция и перенос - } - } - }, - 0 # Промпты с пробелами игнорируются - ) - - # Тест 7: None как plugin_settings - self.run_test( - "None как plugin_settings", - None, - 0 # Нет кастомных промптов - ) - - self.print_summary() - - def print_summary(self): - """Вывод итогов тестирования.""" - print("\n" + "=" * 60) - print("📊 ИТОГИ ТЕСТИРОВАНИЯ") - print("=" * 60) - - total_tests = len(self.test_results) - passed_tests = len([t for t in self.test_results if t['success']]) - - print(f"Всего тестов: {total_tests}") - print(f"Пройденных тестов: {passed_tests}") - print(f"Проваленых тестов: {total_tests - passed_tests}") - print(f"Успешность: {passed_tests/total_tests*100:.1f}%") - - print("\nДетальный отчет:") - for i, test in enumerate(self.test_results, 1): - status = "✅ ПРОЙДЕН" if test['success'] else "❌ ПРОВАЛЕН" - print(f"{i}. {test['test_name']}: {status}") - print(f" Ожидаемо: {test['expected']}, Получено: {test['actual']}") - - if passed_tests == total_tests: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Функция работает корректно.") - else: - print(f"\n⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная отладка.") - -async def main(): - """Главная функция для запуска тестов.""" - print("🚀 Запуск тестового сценария для функции get_user_prompts") - - # Создаем и запускаем тесты - tester = TestGetUserPrompts() - tester.run_all_tests() - - # Ждем немного для завершения всех асинхронных операций - await asyncio.sleep(0.1) - - print("\n✅ Тестирование завершено!") - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/test_integration_full_cycle.py b/test_integration_full_cycle.py deleted file mode 100644 index de2a8d0c..00000000 --- a/test_integration_full_cycle.py +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 4: Полный цикл анализа с оригинальными промптами - -Цель: Протестировать полный цикл анализа товара Ozon с использованием -оригинальных промптов из manifest.json в реальном сценарии -""" - -import json -import sys -import os -import re - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def chat_message(message: str): - """Имитация отправки сообщения в чат.""" - print(f"[CHAT_MESSAGE] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif name == 'pluginSettings': - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'search_for_analogs': False - } - - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопировано из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ИНТЕГРАЦИОННЫЙ ТЕСТ) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - else: - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback на manifest.json (основной сценарий) - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -# Имитация класса FastDOMParser для тестирования -class MockFastDOMParser: - """Макет парсера HTML для тестирования.""" - - def __init__(self, html_content): - self.html = html_content - - def extract_product_info(self): - """Имитация извлечения информации о товаре.""" - return { - 'title': 'Тестовый крем для лица с коллагеном', - 'description': 'Инновационный крем для лица с активным коллагеном и гиалуроновой кислотой. Обеспечивает глубокое увлажнение и разглаживание морщин. Подходит для всех типов кожи.', - 'composition': 'Aqua, Glycerin, Collagen, Hyaluronic Acid, Parfum, DMDM Hydantoin, Propylparaben, Butylparaben, Phenoxyethanol', - 'categories': ['krasota-i-zdorove-6500'], - 'price': {'text': '1 299 ₽', 'amount': 1299, 'currency': 'RUB'}, - 'rating': {'text': '4.2', 'value': 4.2, 'max_value': 5.0}, - 'extraction_stats': { - 'total_fields': 6, - 'successful_fields': 6, - 'success_rate_percent': 100, - 'parsing_time_ms': 15.2 - } - } - - def is_product_in_target_category(self, categories): - """Проверка принадлежности к целевой категории.""" - return 'krasota-i-zdorove-6500' in categories - -# Имитация AI модели для тестирования -def mock_ai_call(model_alias: str, prompt: str): - """Имитация вызова AI модели.""" - console_log(f"🤖 ИМИТАЦИЯ ВЫЗОВА AI: {model_alias}") - console_log(f"📝 Длина промпта: {len(prompt)} символов") - - # Проверяем что промпт содержит ключевые элементы из manifest.json - prompt_lower = prompt.lower() - - # Для optimized промпта проверяем наличие ключевых элементов - if model_alias == 'compliance_check' and any(keyword in prompt_lower for keyword in ['токсиколог', 'косметолог', 'анализ']): - # Проверяем что в промпте есть данные о составе (DMDM Hydantoin, parabens и т.д.) - if any(ingredient in prompt for ingredient in ['DMDM Hydantoin', 'Propylparaben', 'Butylparaben']): - return json.dumps({ - 'score': 4, - 'reasoning': 'Анализ показал наличие потенциально проблемных консервантов (DMDM Hydantoin, Propylparaben, Butylparaben). Основные активные ингредиенты (Collagen, Hyaluronic Acid) присутствуют, но их концентрация не указана явно. Маркетинговые заявления о "глубоком увлажнении" и "разглаживании морщин" не полностью обоснованы составом.', - 'confidence': 0.8, - 'red_flags': ['DMDM Hydantoin', 'Propylparaben', 'Butylparaben'], - 'marketing_lies': ['Глубокое проникновение коллагена в кожу'] - }) - else: - return json.dumps({ - 'score': 5, - 'reasoning': 'Недостаточно данных для полного анализа состава', - 'confidence': 0.5, - 'red_flags': [], - 'marketing_lies': [] - }) - - # Для deep промпта - elif model_alias == 'deep_analysis' and 'глубокий анализ' in prompt_lower: - return "ДЕТАЛЬНЫЙ АНАЛИЗ ПРОДУКТА:\n\n1. НАУЧНАЯ ОБОСНОВАННОСТЬ:\nКоллаген в косметике не проникает в глубокие слои кожи из-за большого размера молекул (>500 Da). Гиалуроновая кислота работает только на поверхности.\n\n2. ПОБОЧНЫЕ ЭФФЕКТЫ:\nКонсерванты DMDM Hydantoin могут вызывать раздражение. Парабены накапливаются в организме.\n\n3. ЭФФЕКТИВНОСТЬ:\nЭффект увлажнения длится 4-6 часов, видимое разглаживание морщин отсутствует." - - return json.dumps({'score': 5, 'reasoning': 'Ошибка анализа'}) - -def analyze_ozon_product_integration_test(): - """ - Интеграционный тест полного цикла анализа с оригинальными промптами. - """ - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 4: Полный цикл анализа с оригинальными промптами") - print("=" * 80) - - # Шаг 1: Загрузка промптов из manifest.json - print("\n📋 ШАГ 1: Загрузка промптов из manifest.json") - print("-" * 60) - - try: - plugin_settings = get_pyodide_var('pluginSettings', {}) - prompts = get_user_prompts(plugin_settings) - - # Проверяем что промпты загружены - loaded_prompts = 0 - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_prompts += 1 - - print(f" Загружено промптов: {loaded_prompts}/4") - - if loaded_prompts < 2: - print("❌ НЕДОСТАТОЧНО ПРОМПТОВ ДЛЯ АНАЛИЗА") - return False - - print("✅ ПРОМПТЫ УСПЕШНО ЗАГРУЖЕНЫ") - - except Exception as e: - print(f"❌ ОШИБКА ЗАГРУЗКИ ПРОМПТОВ: {e}") - return False - - # Шаг 2: Имитация извлечения данных о товаре - print("\n📋 ШАГ 2: Извлечение данных о товаре") - print("-" * 60) - - try: - # Имитируем HTML страницу товара Ozon - mock_html = ''' - - - Крем для лица с коллагеном - Ozon - - - -
-

Крем для лица с коллагеном

-
-

Описание

-
-

Инновационный крем для лица с активным коллагеном и гиалуроновой кислотой. - Обеспечивает глубокое увлажнение и разглаживание морщин. Подходит для всех типов кожи.

-
-

Состав

-

Aqua, Glycerin, Collagen, Hyaluronic Acid, Parfum, DMDM Hydantoin, Propylparaben, Butylparaben, Phenoxyethanol

-
-
- - - - ''' - - parser = MockFastDOMParser(mock_html) - product_info = parser.extract_product_info() - - print(f" Название: {product_info['title']}") - print(f" Описание: {product_info['description'][:100]}...") - print(f" Состав: {product_info['composition']}") - print(f" Категория: {product_info['categories']}") - - # Проверяем категорию - if not parser.is_product_in_target_category(product_info['categories']): - print("❌ ТОВАР НЕ ПРИНАДЛЕЖИТ К ЦЕЛЕВОЙ КАТЕГОРИИ") - return False - - print("✅ ДАННЫЕ О ТОВАРЕ ИЗВЛЕЧЕНЫ КОРРЕКТНО") - - except Exception as e: - print(f"❌ ОШИБКА ИЗВЛЕЧЕНИЯ ДАННЫХ: {e}") - return False - - # Шаг 3: Тестирование промптов в AI анализе - print("\n📋 ШАГ 3: Тестирование промптов в AI анализе") - print("-" * 60) - - try: - # Тестируем optimized промпт (русский) - optimized_prompt_ru = prompts['optimized']['ru'] - if not optimized_prompt_ru or len(optimized_prompt_ru.strip()) == 0: - print("❌ ОПТИМИЗИРОВАННЫЙ ПРОМПТ (RU) НЕ ЗАГРУЖЕН") - return False - - # Вставляем тестовые данные в промпт - test_description = product_info['description'] - test_composition = product_info['composition'] - - # Подготавливаем промпт для тестирования - test_prompt = optimized_prompt_ru.replace('{description}', test_description).replace('{composition}', test_composition) - - print(f" Длина промпта: {len(test_prompt)} символов") - print(f" Промпт начинается с: {test_prompt[:100]}...") - - # Проверяем наличие ключевых элементов промпта - key_elements = [ - 'токсиколог', - 'косметолог', - 'анализ', - 'соответствия', - 'DMDM Hydantoin', - 'парабены', - 'концентрация' - ] - - found_elements = [] - for element in key_elements: - if element.lower() in test_prompt.lower(): - found_elements.append(element) - - print(f" Найдено ключевых элементов: {len(found_elements)}/{len(key_elements)}") - - if len(found_elements) >= 5: # Минимум 5 ключевых элементов - print("✅ ПРОМПТ СОДЕРЖИТ ВСЕ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - else: - print("❌ ПРОМПТ НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ПОДГОТОВКИ ПРОМПТА: {e}") - return False - - # Шаг 4: Имитация AI анализа - print("\n📋 ШАГ 4: Имитация AI анализа") - print("-" * 60) - - try: - # Имитируем вызов AI с промптом - ai_response = mock_ai_call('compliance_check', test_prompt) - - print(f" Длина ответа AI: {len(ai_response)} символов") - - # Проверяем структуру ответа - try: - parsed_response = json.loads(ai_response) - print(f" Структура ответа: {list(parsed_response.keys())}") - - if 'score' in parsed_response and 'reasoning' in parsed_response: - score = parsed_response['score'] - reasoning = parsed_response['reasoning'] - - print(f" Оценка соответствия: {score}/10") - print(f" Длина обоснования: {len(reasoning)} символов") - - # Проверяем что ответ учитывает данные товара - if 'DMDM Hydantoin' in reasoning or 'парабен' in reasoning.lower(): - print("✅ АНАЛИЗ УЧИТЫВАЕТ СОСТАВ ПРОДУКТА") - else: - print("❌ АНАЛИЗ НЕ УЧИТЫВАЕТ СОСТАВ ПРОДУКТА") - return False - - print("✅ AI АНАЛИЗ ВЫПОЛНЕН УСПЕШНО") - - else: - print("❌ ОТВЕТ AI НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ПОЛЯ") - return False - - except json.JSONDecodeError: - print("❌ ОТВЕТ AI НЕ ЯВЛЯЕТСЯ ВАЛИДНЫМ JSON") - return False - - except Exception as e: - print(f"❌ ОШИБКА AI АНАЛИЗА: {e}") - return False - - # Шаг 5: Проверка deep промпта - print("\n📋 ШАГ 5: Проверка deep промпта") - print("-" * 60) - - try: - deep_prompt_ru = prompts['deep']['ru'] - if not deep_prompt_ru or len(deep_prompt_ru.strip()) == 0: - print("❌ ГЛУБОКИЙ ПРОМПТ (RU) НЕ ЗАГРУЖЕН") - return False - - # Подготавливаем deep промпт - deep_test_prompt = deep_prompt_ru.replace('{description}', test_description).replace('{composition}', test_composition) - - print(f" Длина deep промпта: {len(deep_test_prompt)} символов") - print(f" Deep промпт начинается с: {deep_test_prompt[:100]}...") - - # Проверяем наличие ключевых элементов deep промпта - deep_key_elements = [ - 'критический анализ', - 'токсиколог', - 'косметолог', - 'маркетинговые уловки', - 'формальдегид-релизеры' - ] - - found_deep_elements = [] - for element in deep_key_elements: - if element.lower() in deep_test_prompt.lower(): - found_deep_elements.append(element) - - print(f" Найдено ключевых элементов deep промпта: {len(found_deep_elements)}/{len(deep_key_elements)}") - - if len(found_deep_elements) >= 3: - print("✅ DEEP ПРОМПТ СОДЕРЖИТ ВСЕ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - else: - print("❌ DEEP ПРОМПТ НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ПОДГОТОВКИ DEEP ПРОМПТА: {e}") - return False - - # Шаг 6: Финальная проверка интеграции - print("\n📋 ШАГ 6: Финальная проверка интеграции") - print("-" * 60) - - try: - # Имитируем полный цикл анализа - description = product_info['description'] - composition = product_info['composition'] - categories = product_info['categories'] - - # Проверяем что все данные корректны - checks = [ - ('Описание', len(description) > 50), - ('Состав', len(composition) > 20), - ('Категория', len(categories) > 0), - ('Цена', product_info['price']['amount'] > 0), - ('Рейтинг', product_info['rating']['value'] > 0) - ] - - passed_checks = 0 - for check_name, check_result in checks: - if check_result: - passed_checks += 1 - print(f" ✅ {check_name}: корректно") - else: - print(f" ❌ {check_name}: некорректно") - - print(f"\n📊 ПРОЙДЕНО ПРОВЕРОК: {passed_checks}/{len(checks)}") - - if passed_checks == len(checks): - print("✅ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") - else: - print("❌ НЕ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ФИНАЛЬНОЙ ПРОВЕРКИ: {e}") - return False - - print("\n" + "=" * 80) - print("🎉 ИНТЕГРАЦИОННЫЙ ТЕСТ ПРОЙДЕН УСПЕШНО!") - print("=" * 80) - print("📋 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ:") - print(" ✅ Промпты корректно загружаются из manifest.json") - print(" ✅ Оригинальные промпты содержат все необходимые элементы анализа") - print(" ✅ Данные о товаре извлекаются корректно") - print(" ✅ AI анализ выполняется с использованием оригинальных промптов") - print(" ✅ Полный цикл анализа работает от начала до конца") - print("=" * 80) - - return True - -if __name__ == "__main__": - success = analyze_ozon_product_integration_test() - if not success: - print("❌ ИНТЕГРАЦИОННЫЙ ТЕСТ НЕ ПРОЙДЕН") - sys.exit(1) - else: - print("✅ ИНТЕГРАЦИОННЫЙ ТЕСТ ПРОЙДЕН") - sys.exit(0) \ No newline at end of file diff --git a/test_integration_prompts.py b/test_integration_prompts.py deleted file mode 100644 index 46375ae7..00000000 --- a/test_integration_prompts.py +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ МЕХАНИЗМА ПРОМПТОВ -Тестирует полный цикл анализа товара Ozon с исправленными промптами -""" - -import json -import sys -import os -from typing import Dict, Any - -def simulate_ozon_product_data(): - """Симулирует реальные данные товара Ozon для тестирования""" - return { - 'title': 'Крем для лица с коллагеном и гиалуроновой кислотой', - 'description': '''Революционный anti-age крем с 3D лифтинг эффектом! - Инновационная формула с коллагеном и гиалуроновой кислотой обеспечивает: - • Мгновенное разглаживание морщин на 90% - • Глубокое увлажнение на 24 часа - • Лифтинг эффект уже через 7 дней использования - • Восстановление упругости кожи - Подходит для всех типов кожи, включая чувствительную.''', - 'composition': '''Aqua, Glycerin, Collagen, Sodium Hyaluronate, Palmitoyl Tripeptide-38, - Dimethicone, Parfum, Phenoxyethanol, Methylparaben, Propylparaben, - Butylparaben, Ethylparaben, DMDM Hydantoin, Quaternium-15, Octinoxate, - Oxybenzone, Triethanolamine, Carbomer, Allantoin''', - 'categories': ['krasota-i-zdorove-6500', 'uhod-za-kozhey-1234'], - 'price': {'text': '1 299 ₽', 'amount': 1299.0, 'currency': 'RUB'}, - 'rating': {'text': '4.2', 'value': 4.2, 'max_value': 5.0} - } - -def simulate_plugin_settings_with_prompts(): - """Симулирует настройки плагина с кастомными промптами""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': True, - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ОПТИМИЗИРОВАННЫЙ ПРОМПТ: Проанализируй косметический продукт на соответствие описания и состава. Будь максимально критичным к маркетинговым заявлениям. Описание: {description} Состав: {composition}', - 'en': 'CUSTOM OPTIMIZED PROMPT: Analyze cosmetic product for description and composition compliance. Be extremely critical of marketing claims. Description: {description} Composition: {composition}' - }, - 'deep': { - 'ru': 'КАСТОМНЫЙ ГЛУБОКИЙ ПРОМПТ: Проведи детальный медицинский анализ косметического продукта. Оцени безопасность состава и эффективность заявленных свойств. Описание: {description} Состав: {composition}', - 'en': 'CUSTOM DEEP PROMPT: Conduct detailed medical analysis of cosmetic product. Evaluate composition safety and claimed properties effectiveness. Description: {description} Composition: {composition}' - } - } - } - -def simulate_plugin_settings_empty(): - """Симулирует настройки плагина без кастомных промптов""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': True - # prompts отсутствуют - должен использовать из manifest - } - -def simulate_plugin_settings_partial(): - """Симулирует настройки плагина с частично заполненными промптами""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': False, - 'prompts': { - 'optimized': { - 'ru': 'ЧАСТИЧНЫЙ КАСТОМНЫЙ ПРОМПТ ТОЛЬКО РУССКИЙ' - # en отсутствует - должен использовать из manifest - } - # deep отсутствует - должен использовать из manifest - } - } - -def load_manifest(): - """Загружает manifest.json для тестирования""" - manifest_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer/manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return None - -def test_integration_scenario_1_full_custom_prompts(): - """Интеграционный тест 1: Полностью заполненные кастомные промпты""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 1: Полностью заполненные кастомные промпты") - print("=" * 70) - - # Загружаем данные товара и настройки - product_data = simulate_ozon_product_data() - plugin_settings = simulate_plugin_settings_with_prompts() - - print("📋 Тестовые данные:") - print(f" Товар: {product_data['title']}") - print(f" Настройки: {len(plugin_settings.get('prompts', {}))} типов промптов") - - # Симулируем процесс анализа - description = product_data['description'] - composition = product_data['composition'] - categories = product_data['categories'] - - print(f"📝 Описание: {description[:100]}...") - print(f"🧪 Состав: {composition[:100]}...") - print(f"🏷️ Категории: {categories}") - - # Проверяем что категории корректные для анализа - if not categories or 'krasota-i-zdorove-6500' not in categories: - print("❌ Товар не принадлежит к категории косметики") - return False - - # Симулируем логику анализа - print("\n🔍 Симуляция анализа с кастомными промптами:") - - # Проверяем какие промпты будут использоваться - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - custom_prompt = plugin_settings.get('prompts', {}).get(prompt_type, {}).get(lang, '') - if custom_prompt and len(custom_prompt.strip()) > 0: - print(f" ✅ {prompt_type}.{lang}: Кастомный промпт (длина: {len(custom_prompt)})") - else: - print(f" ❌ {prompt_type}.{lang}: Кастомный промпт отсутствует") - - # Симулируем результат анализа - analysis_result = { - 'score': 4, - 'reasoning': f"Анализ с использованием кастомных промптов. Маркетинговые заявления 'революционный' и '3D лифтинг' не подтверждены активами в достаточной концентрации. Найдены проблемные консерванты (парабены, формальдегид-релизеры).", - 'confidence': 0.85, - 'red_flags': ['Methylparaben', 'Propylparaben', 'DMDM Hydantoin', 'Octinoxate'], - 'marketing_lies': ['90% разглаживание морщин', 'эффект через 7 дней'] - } - - print("\n📊 Результат анализа:") - print(f" Оценка: {analysis_result['score']}/10") - print(f" Уверенность: {analysis_result['confidence']}") - print(f" Красные флаги: {len(analysis_result['red_flags'])} найдено") - print(f" Маркетинговые преувеличения: {len(analysis_result['marketing_lies'])} найдено") - - # Проверяем критичность обнаруженных проблем - if analysis_result['score'] < 7: - deep_analysis_offered = True - print(f" 🔍 Глубокий анализ: ПРЕДЛОЖЕН (оценка {analysis_result['score']} < 7)") - else: - deep_analysis_offered = False - print(f" ✅ Глубокий анализ: НЕ ТРЕБУЕТСЯ (оценка {analysis_result['score']} >= 7)") - - return True - -def test_integration_scenario_2_empty_custom_prompts(): - """Интеграционный тест 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 70) - - # Загружаем данные товара и настройки - product_data = simulate_ozon_product_data() - plugin_settings = simulate_plugin_settings_empty() - - print("📋 Тестовые данные:") - print(f" Товар: {product_data['title']}") - print(f" Настройки: промпты отсутствуют (должны использоваться из manifest)") - - # Загружаем manifest для проверки fallback - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return False - - manifest_prompts = manifest.get('options', {}).get('prompts', {}) - print(f"📋 Доступные промпты в manifest: {list(manifest_prompts.keys())}") - - # Проверяем что промпты из manifest корректные - success = True - for prompt_type in ['optimized', 'deep']: - if prompt_type in manifest_prompts: - print(f" ✅ {prompt_type}: Найден в manifest") - for lang in ['ru', 'en']: - lang_data = manifest_prompts[prompt_type].get(lang, {}) - default_prompt = lang_data.get('default', '') - if default_prompt and len(default_prompt) > 100: - print(f" ✅ {lang}: Промпт найден (длина: {len(default_prompt)})") - else: - print(f" ❌ {lang}: Промпт отсутствует или слишком короткий") - success = False - else: - print(f" ❌ {prompt_type}: Не найден в manifest") - success = False - - if success: - print("✅ Fallback на manifest работает корректно") - else: - print("❌ Fallback на manifest не работает") - - return success - -def test_integration_scenario_3_problematic_product(): - """Интеграционный тест 3: Проблемный товар с низкой оценкой""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 3: Проблемный товар с низкой оценкой") - print("=" * 70) - - # Создаем проблемный товар с подозрительными ингредиентами - problematic_product = { - 'title': 'Чудо-крем с революционной формулой 5D', - 'description': '''НЕВЕРОЯТНЫЙ ПРОРЫВ В КОСМЕТОЛОГИИ! - Революционная 5D формула с наночастицами обеспечивает: - • Полное исчезновение морщин за 3 дня - • Увеличение упругости кожи на 500% - • Эффект ботокса без инъекций - • Вечная молодость уже после первого применения - • Клинически протестировано в секретных лабораториях''', - 'composition': '''Aqua, Parfum, Methylparaben, Propylparaben, Butylparaben, - Ethylparaben, DMDM Hydantoin, Quaternium-15, Octinoxate, Oxybenzone, - Triethanolamine, Sodium Lauryl Sulfate, Artificial Colors, Alcohol Denat''', - 'categories': ['krasota-i-zdorove-6500'], - 'price': {'text': '5 999 ₽', 'amount': 5999.0, 'currency': 'RUB'}, - 'rating': {'text': '2.1', 'value': 2.1, 'max_value': 5.0} - } - - plugin_settings = simulate_plugin_settings_with_prompts() - - print("📋 Тестовый товар:") - print(f" Название: {problematic_product['title']}") - print(f" Цена: {problematic_product['price']['text']}") - print(f" Рейтинг: {problematic_product['rating']['text']}") - - # Анализируем состав на проблемные ингредиенты - composition_lower = problematic_product['composition'].lower() - - red_flags = [] - if 'paraben' in composition_lower: - red_flags.append('Парабены (потенциальные эндокринные дизрапторы)') - if 'dmdm' in composition_lower: - red_flags.append('DMDM Hydantoin (формальдегид-релизер)') - if 'octinoxate' in composition_lower or 'oxybenzone' in composition_lower: - red_flags.append('Устаревшие УФ-фильтры (Octinoxate, Oxybenzone)') - if 'sodium lauryl sulfate' in composition_lower: - red_flags.append('Sodium Lauryl Sulfate (агрессивный ПАВ)') - - print(f"🚨 Обнаружено красных флагов: {len(red_flags)}") - for flag in red_flags: - print(f" ❌ {flag}") - - # Анализируем маркетинговые заявления - description_lower = problematic_product['description'].lower() - marketing_lies = [] - - if 'революцион' in description_lower or 'революц' in description_lower: - marketing_lies.append('"Революционная формула" - требует доказательств') - if '5d' in description_lower: - marketing_lies.append('"5D формула" - маркетинговый термин без научного обоснования') - if '500%' in description_lower: - marketing_lies.append('"500% увеличение упругости" - завышенные обещания') - if 'вечная молодость' in description_lower: - marketing_lies.append('"Вечная молодость" - невозможно выполнить обещание') - - print(f"🎭 Маркетинговые преувеличения: {len(marketing_lies)}") - for lie in marketing_lies: - print(f" ⚠️ {lie}") - - # Вычисляем ожидаемую оценку - score_reduction = len(red_flags) + len(marketing_lies) - expected_score = max(1, 10 - score_reduction) - - print(f"📊 Ожидаемая оценка: {expected_score}/10 (снижение на {score_reduction} баллов)") - - if expected_score <= 5: - print("🔍 Глубокий анализ: НЕОБХОДИМ (оценка слишком низкая)") - else: - print("✅ Глубокий анализ: Не требуется") - - return True - -def test_integration_scenario_4_manifest_structure(): - """Интеграционный тест 4: Проверка структуры manifest.json""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 4: Проверка структуры manifest.json") - print("=" * 70) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return False - - print("📋 Анализ структуры manifest.json:") - - # Проверяем основные поля - required_fields = ['name', 'version', 'options'] - for field in required_fields: - if field in manifest: - print(f" ✅ {field}: {manifest[field]}") - else: - print(f" ❌ {field}: ОТСУТСТВУЕТ") - - # Проверяем структуру промптов - options = manifest.get('options', {}) - if not options: - print(" ❌ options: ОТСУТСТВУЕТ") - return False - - prompts = options.get('prompts', {}) - if not prompts: - print(" ❌ prompts: ОТСУТСТВУЕТ") - return False - - print(f" ✅ prompts: Найдены типы - {list(prompts.keys())}") - - # Проверяем каждый тип промпта - for prompt_type in ['optimized', 'deep']: - if prompt_type not in prompts: - print(f" ❌ {prompt_type}: Не найден") - continue - - print(f" ✅ {prompt_type}:") - type_prompts = prompts[prompt_type] - - for lang in ['ru', 'en']: - if lang not in type_prompts: - print(f" ❌ {lang}: Не найден") - continue - - lang_data = type_prompts[lang] - print(f" ✅ {lang}:") - - # Проверяем обязательные поля - for field in ['type', 'default']: - if field in lang_data: - value = lang_data[field] - if field == 'default' and isinstance(value, str): - length = len(value) - print(f" ✅ {field}: {length} символов") - if length < 100: - print(f" ⚠️ Внимание: очень короткий промпт ({length} символов)") - else: - print(f" ✅ {field}: {value}") - else: - print(f" ❌ {field}: ОТСУТСТВУЕТ") - - # Проверяем настройки AI моделей - ai_models = manifest.get('ai_models', {}) - if ai_models: - print(f" ✅ ai_models: {len(ai_models)} моделей") - for model_name, model_alias in ai_models.items(): - print(f" ✅ {model_name}: {model_alias}") - else: - print(" ❌ ai_models: ОТСУТСТВУЮТ") - - return True - -def run_integration_tests(): - """Запуск всех интеграционных тестов""" - print("🔍 НАЧИНАЕМ ИНТЕГРАЦИОННОЕ ТЕСТИРОВАНИЕ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 90) - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ИНТЕГРАЦИОННЫХ ТЕСТОВ") - print("=" * 50) - - results['integration_1'] = test_integration_scenario_1_full_custom_prompts() - results['integration_2'] = test_integration_scenario_2_empty_custom_prompts() - results['integration_3'] = test_integration_scenario_3_problematic_product() - results['integration_4'] = test_integration_scenario_4_manifest_structure() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ") - print("=" * 60) - - passed = 0 - total = 0 - - for test_name, result in results.items(): - total += 1 - if result: - passed += 1 - print(f"✅ {test_name}: ПРОЙДЕН") - else: - print(f"❌ {test_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} интеграционных тестов пройдено") - - if passed == total: - print("🎉 ВСЕ ИНТЕГРАЦИОННЫЕ ТЕСТЫ ПРОЙДЕНЫ!") - print("✅ Механизм промптов полностью функционален") - print("✅ Fallback логика работает корректно") - print("✅ Структура manifest.json корректна") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ!") - print("Требуется дополнительная диагностика и исправление проблем") - - return results - -if __name__ == "__main__": - run_integration_tests() \ No newline at end of file diff --git a/test_json_parsing.py b/test_json_parsing.py deleted file mode 100644 index 46c547e9..00000000 --- a/test_json_parsing.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -""" -Тестовый скрипт для проверки парсинга JSON ответов Gemini API -""" - -import json -import re - -def test_gemini_response_parsing(): - """Тестирование обработки ответа Gemini с markdown обёрткой.""" - - # Тестовые данные из логов - gemini_response = '''```json -{ - "score": 9, - "reasoning": "Описание товара полностью соответствует предоставленным структурированным данным. Анализ показал высокий уровень соответствия между заявленными свойствами продукта и его фактическим составом. Отмечается наличие ключевых активных ингредиентов, которые действительно способны оказывать заявленное воздействие на кожу.", - "confidence": 1 -} -```''' - - print("=== ТЕСТИРОВАНИЕ ОБРАБОТКИ ОТВЕТА GEMINI ===") - print(f"Исходный ответ ({len(gemini_response)} символов):") - print(repr(gemini_response)) - print() - - # Текущая логика обработки - print("1. Текущая логика обработки:") - cleaned_str = gemini_response.strip().replace('```json', '').replace('```', '') - print(f"После очистки ({len(cleaned_str)} символов):") - print(repr(cleaned_str)) - print() - - try: - parsed = json.loads(cleaned_str) - print("✅ JSON успешно распарсен:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга JSON: {je}") - print(f"Позиция ошибки: {je.pos}") - if je.pos is not None and je.pos < len(cleaned_str): - start = max(0, je.pos - 20) - end = min(len(cleaned_str), je.pos + 20) - print(f"Контекст ошибки: ...{cleaned_str[start:end]}...") - print() - - # Улучшенная логика обработки - print("2. Улучшенная логика обработки:") - - # Шаг 1: Более тщательная очистка markdown - improved_cleaned = gemini_response.strip() - - # Убираем ```json в начале - if improved_cleaned.startswith('```json'): - improved_cleaned = improved_cleaned[7:] # len('```json') - elif improved_cleaned.startswith('```'): - improved_cleaned = improved_cleaned[3:] # len('```') - - # Убираем ``` в конце - if improved_cleaned.endswith('```'): - improved_cleaned = improved_cleaned[:-3] - - # Финальная очистка пробелов - improved_cleaned = improved_cleaned.strip() - - print(f"После улучшенной очистки ({len(improved_cleaned)} символов):") - print(repr(improved_cleaned)) - print() - - try: - parsed = json.loads(improved_cleaned) - print("✅ JSON успешно распарсен после улучшения:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга даже после улучшения: {je}") - print() - - # Шаг 3: Использование регулярных выражений для извлечения JSON - print("3. Извлечение JSON с помощью regex:") - - # Паттерн для поиска JSON блока - json_pattern = r'```(?:json)?\s*\n?(\{.*?\})\s*\n?```' - match = re.search(json_pattern, gemini_response, re.DOTALL | re.IGNORECASE) - - if match: - extracted_json = match.group(1).strip() - print(f"Извлечено с помощью regex ({len(extracted_json)} символов):") - print(repr(extracted_json)) - print() - - try: - parsed = json.loads(extracted_json) - print("✅ JSON успешно распарсен с помощью regex:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга извлечённого JSON: {je}") - else: - print("❌ Не удалось извлечь JSON с помощью regex") - - return False - -def test_edge_cases(): - """Тестирование edge cases с экранированием кавычек.""" - - print("\n=== ТЕСТИРОВАНИЕ EDGE CASES ===") - - # Edge case 1: Экранированные кавычки (как при передаче JS->Python) - escaped_response = '"```json\\n{\\n \\"score\\": 9,\\n \\"reasoning\\": \\"test\\",\\n \\"confidence\\": 1\\n}\\n```"' - - print("Edge case 1: Экранированные кавычки") - print(f"Исходный ответ: {repr(escaped_response)}") - - try: - # Попытка парсинга как JSON строки - unescaped = json.loads(escaped_response) - print(f"После json.loads: {repr(unescaped)}") - - # Теперь применяем обычную очистку - cleaned = unescaped.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - - # Edge case 2: Двойная обёртка - double_wrapped = '```json\n```json\n{\n "score": 9,\n "reasoning": "test",\n "confidence": 1\n}\n```\n```' - - print("\nEdge case 2: Двойная обёртка") - print(f"Исходный ответ: {repr(double_wrapped)}") - - try: - cleaned = double_wrapped.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - - # Edge case 3: Неправильная структура markdown - malformed = '```json{\n "score": 9,\n "reasoning": "test",\n "confidence": 1\n}```' - - print("\nEdge case 3: Неправильная структура markdown") - print(f"Исходный ответ: {repr(malformed)}") - - try: - cleaned = malformed.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - -if __name__ == "__main__": - success = test_gemini_response_parsing() - test_edge_cases() - print(f"\n{'✅ ОСНОВНОЙ ТЕСТ ПРОШЁЛ' if success else '❌ ОСНОВНОЙ ТЕСТ ПРОВАЛИЛСЯ'}") \ No newline at end of file diff --git a/test_json_parsing_final.py b/test_json_parsing_final.py deleted file mode 100644 index 93fac4f9..00000000 --- a/test_json_parsing_final.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 -""" -Тестовый скрипт для проверки исправленной логики clean_ai_response -с реальными данными из логов Gemini API. -""" - -import json -import re - -def test_clean_ai_response_logic(ai_response: str) -> dict: - """ - Моделирует логику clean_ai_response из mcp_server.py - """ - print(f"\n=== ТЕСТИРОВАНИЕ ОТВЕТА ===") - print(f"Исходный ответ: {ai_response}") - - # Шаг 1: Улучшенная очистка markdown обёртки - original_length = len(ai_response) - - # Регулярное выражение для удаления markdown обёртки ```json...``` - markdown_pattern = re.compile(r'```\s*json\s*\n?(.*?)\n?\s*```', re.IGNORECASE | re.DOTALL) - cleaned_str = markdown_pattern.sub(r'\1', ai_response.strip()) - - # Дополнительная очистка на случай если остались одиночные ``` - cleaned_str = cleaned_str.replace('```', '').strip() - - print(f"После очистки: {cleaned_str}") - print(f"Удалено символов: {original_length - len(cleaned_str)}") - - # Шаг 1.5: Попытка парсинга очищенного ответа напрямую - try: - parsed = json.loads(cleaned_str) - print(f"✅ JSON успешно распарсен напрямую: {parsed}") - - # Валидация формата ответа - if isinstance(parsed, dict) and 'score' in parsed: - score = parsed.get('score') - reasoning = parsed.get('reasoning') - print(f"✅ Прямой парсинг успешен: score={score}") - return parsed - else: - print(f"⚠️ Парсинг вернул словарь без поля 'score': {parsed}") - except json.JSONDecodeError as je: - print(f"❌ Прямой JSON парсинг провалился: {str(je)}") - - # Шаг 2: Попытка парсинга исходного ответа напрямую - try: - parsed = json.loads(ai_response.strip()) - print(f"✅ Исходный ответ успешно распарсен: {parsed}") - if isinstance(parsed, dict) and 'score' in parsed: - return parsed - except json.JSONDecodeError: - print("❌ Парсинг исходного ответа провалился") - - # Шаг 3: Альтернативное извлечение JSON - print("🔄 ШАГ 3: Альтернативное извлечение JSON") - alternative_result = extract_json_from_ai_response(ai_response) - if alternative_result: - print(f"✅ Альтернативное извлечение успешно: {alternative_result}") - return alternative_result - - # Шаг 4: Попытка ремонта JSON - print("🔧 ШАГ 4: Попытка ремонта JSON") - repair_result = attempt_json_repair(cleaned_str) - if repair_result: - print(f"✅ Ремонт JSON успешен: {repair_result}") - return repair_result - - # Финальный fallback - print("❌ Все методы провалились, используем fallback") - fallback_result = { - "score": 5, - "reasoning": f"Не удалось распарсить JSON. Исходный ответ: {cleaned_str[:100]}..." - } - return fallback_result - -def extract_json_from_ai_response(ai_response: str) -> dict: - """Альтернативная функция извлечения JSON из ответа AI.""" - print(" Стратегия 2: Поиск между маркерами кода") - - # Улучшенные паттерны для поиска JSON в markdown блоках - code_block_patterns = [ - r'```\s*json\s*\n?\s*(\{.*?\})\s*\n?\s*```', # ```json\n{...}\n``` - r'```\s*(\{.*?\})\s*```', # ```\n{...}\n``` - r'```[^\n]*\s*\n?\s*(\{.*?\})\s*\n?\s*```', # ```language\n{...}\n``` - ] - - for pattern_idx, pattern in enumerate(code_block_patterns): - matches = re.findall(pattern, ai_response, re.IGNORECASE | re.DOTALL) - print(f" Стратегия 2.{pattern_idx + 1}: найдено {len(matches)} совпадений") - - for match_idx, match in enumerate(matches): - try: - print(f" Попытка парсинга совпадения {match_idx + 1}: {match[:50]}...") - parsed = json.loads(match) - if isinstance(parsed, dict) and 'score' in parsed: - score = parsed.get('score') - print(f" ✅ Найден валидный JSON: score={score}") - return parsed - except json.JSONDecodeError as e: - print(f" Парсинг совпадения {match_idx + 1} провалился: {e}") - continue - - print(" ❌ Ни одна стратегия не сработала") - return None - -def attempt_json_repair(broken_json: str) -> dict: - """Функция ремонта поврежденного JSON.""" - print(" Шаг 1: Очистка от Markdown") - - original_json = broken_json - cleaned = original_json - - # Проверяем, является ли JSON уже чистым - try: - parsed_clean = json.loads(cleaned.strip()) - if isinstance(parsed_clean, dict) and 'score' in parsed_clean: - print(" ✅ JSON уже чистый") - return parsed_clean - except json.JSONDecodeError: - pass - - # Убираем Markdown обертки - markdown_patterns = [ - r'```\s*json\s*\n?\s*', - r'```\s*', - r'\s*```', - ] - - for pattern in markdown_patterns: - cleaned = re.sub(pattern, '', cleaned, flags=re.IGNORECASE) - - cleaned = cleaned.strip() - - # Проверяем после очистки - try: - parsed = json.loads(cleaned) - if isinstance(parsed, dict) and 'score' in parsed: - print(" ✅ После очистки Markdown получился валидный JSON") - return parsed - except json.JSONDecodeError: - pass - - print(" ❌ Ремонт JSON не удался") - return None - -def main(): - """Основная функция тестирования.""" - print("🧪 ТЕСТИРОВАНИЕ ИСПРАВЛЕННОЙ ЛОГИКИ clean_ai_response") - print("=" * 60) - - # Тестовый кейс из логов - test_cases = [ - { - "name": "Gemini API с markdown обёрткой", - "input": """```json -{ - "score": 9, - "reasoning": "Описание товара и состав хорошо согласуются...", - "confidence": 0.95 -} -```""", - "expected_score": 9 - }, - { - "name": "Чистый JSON без markdown", - "input": """{ - "score": 7, - "reasoning": "Хорошее соответствие состава описанию" -}""", - "expected_score": 7 - }, - { - "name": "JSON с пробелами после json", - "input": """```json -{ - "score": 8, - "reasoning": "Тест с пробелами" -} -```""", - "expected_score": 8 - } - ] - - for test_case in test_cases: - print(f"\n🔍 ТЕСТ: {test_case['name']}") - print("-" * 40) - - try: - result = test_clean_ai_response_logic(test_case['input']) - actual_score = result.get('score', 'N/A') - expected_score = test_case['expected_score'] - - if actual_score == expected_score: - print(f"✅ ТЕСТ ПРОЙДЕН: score={actual_score} (ожидалось {expected_score})") - else: - print(f"❌ ТЕСТ ПРОВАЛЕН: score={actual_score} (ожидалось {expected_score})") - - print(f"Полный результат: {result}") - - except Exception as e: - print(f"❌ ОШИБКА ТЕСТИРОВАНИЯ: {e}") - - print("\n" + "=" * 60) - print("🏁 ТЕСТИРОВАНИЕ ЗАВЕРШЕНО") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_manifest_prompts_loading.py b/test_manifest_prompts_loading.py deleted file mode 100644 index 6bd51527..00000000 --- a/test_manifest_prompts_loading.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 1: Проверка корректности загрузки оригинальных промптов из manifest.json - -Цель: Проверить, что функция get_user_prompts() корректно извлекает промпты из manifest.json -и правильно структурирует их для использования в AI анализе. -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - # Имитируем наличие manifest.json в globals - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопированная из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ) =====") - - try: - # Диагностика доступа к manifest - console_log("🔍 ДИАГНОСТИКА ДОСТУПА К MANIFEST:") - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - else: - console_log(" ❌ manifest НЕ НАЙДЕН в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - - # Структура промптов - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log(" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback на manifest.json (основной сценарий для тестирования) - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_1(): - """Выполнение тестового сценария 1.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 1: Проверка загрузки оригинальных промптов из manifest.json") - print("=" * 80) - - # Тест 1: Загрузка промптов без пользовательских настроек - print("\n📋 ТЕСТ 1: Загрузка промптов без пользовательских настроек") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем что промпты загружены - loaded_count = 0 - total_count = 0 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - total_count += 1 - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_count += 1 - print(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - print(f" ❌ {prompt_type}.{lang}: НЕ загружен") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 1: {loaded_count}/{total_count} промптов загружено") - - if loaded_count >= 2: # Минимум 2 промпта должны быть загружены - print("✅ ТЕСТ 1 ПРОЙДЕН: Оригинальные промпты из manifest.json загружаются корректно") - else: - print("❌ ТЕСТ 1 НЕ ПРОЙДЕН: Недостаточно промптов загружено из manifest.json") - - except Exception as e: - print(f"❌ ТЕСТ 1 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 2: Проверка структуры промптов - print("\n📋 ТЕСТ 2: Проверка структуры промптов") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем структуру промптов - expected_structure = { - 'optimized': {'ru': str, 'en': str}, - 'deep': {'ru': str, 'en': str} - } - - structure_valid = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_type = expected_structure[prompt_type][lang] - actual_type = type(prompts[prompt_type][lang]) - - if actual_type == expected_type: - print(f" ✅ {prompt_type}.{lang}: тип данных корректный ({actual_type.__name__})") - else: - print(f" ❌ {prompt_type}.{lang}: неверный тип данных (ожидали {expected_type.__name__}, получили {actual_type.__name__})") - structure_valid = False - - if structure_valid: - print("✅ ТЕСТ 2 ПРОЙДЕН: Структура промптов корректная") - else: - print("❌ ТЕСТ 2 НЕ ПРОЙДЕН: Структура промптов некорректная") - - except Exception as e: - print(f"❌ ТЕСТ 2 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 3: Проверка содержания промптов - print("\n📋 ТЕСТ 3: Проверка содержания промптов") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем что промпты содержат ключевые слова - content_checks = { - 'optimized.ru': ['токсиколог', 'косметолог', 'анализ', 'соответствия'], - 'optimized.en': ['toxicologist', 'cosmetic chemist', 'analysis', 'compliance'], - 'deep.ru': ['глубокий анализ', 'медицинской', 'научной', 'точке зрения'], - 'deep.en': ['deep analysis', 'medical', 'scientific', 'perspective'] - } - - content_valid = True - - for prompt_key, keywords in content_checks.items(): - prompt_type, lang = prompt_key.split('.') - prompt_value = prompts[prompt_type][lang] - - found_keywords = [] - for keyword in keywords: - if keyword.lower() in prompt_value.lower(): - found_keywords.append(keyword) - - if len(found_keywords) >= 2: # Минимум 2 ключевых слова - print(f" ✅ {prompt_key}: найдено {len(found_keywords)}/{len(keywords)} ключевых слов") - else: - print(f" ❌ {prompt_key}: найдено только {len(found_keywords)}/{len(keywords)} ключевых слов") - content_valid = False - - if content_valid: - print("✅ ТЕСТ 3 ПРОЙДЕН: Содержание промптов корректное") - else: - print("❌ ТЕСТ 3 НЕ ПРОЙДЕН: Содержание промптов некорректное") - - except Exception as e: - print(f"❌ ТЕСТ 3 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - print("\n" + "=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 1") - print("=" * 80) - -if __name__ == "__main__": - run_test_scenario_1() \ No newline at end of file diff --git a/test_manifest_scenarios.py b/test_manifest_scenarios.py deleted file mode 100644 index ffb1bf2d..00000000 --- a/test_manifest_scenarios.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест для проверки исправления передачи manifest: протестировать сценарии -с manifest в plugin_settings и в globals -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -class MockPyodideContext: - """Мок Pyodide контекста для тестирования разных сценариев передачи manifest""" - - def __init__(self): - self.globals = {} - - def set_global(self, key: str, value: Any): - """Установить переменную в globals""" - self.globals[key] = value - print(f" 📥 Установлена глобальная переменная {key}: {type(value)}") - - def get_global(self, key: str, default=None): - """Получить переменную из globals""" - return self.globals.get(key, default) - - def get_plugin_settings_manifest_scenario(self): - """Сценарий 1: manifest в pluginSettings""" - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - - # PluginSettings содержит manifest как поле - plugin_settings = { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'manifest': manifest_data # manifest как часть pluginSettings - } - - self.set_global('pluginSettings', plugin_settings) - console_log("✅ Сценарий 1: manifest передан в pluginSettings.manifest") - return plugin_settings - - def get_globals_manifest_scenario(self): - """Сценарий 2: manifest в globals""" - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - - # PluginSettings без manifest - plugin_settings = { - 'response_language': 'ru', - 'enable_deep_analysis': True - } - - # Manifest передается отдельно в globals - self.set_global('pluginSettings', plugin_settings) - self.set_global('manifest', manifest_data) - console_log("✅ Сценарий 2: manifest передан в globals.manifest") - return plugin_settings - -def get_user_prompts_plugin_settings_scenario(plugin_settings): - """ - Функция загрузки промптов для сценария с manifest в pluginSettings - """ - console_log("🔍 ЗАГРУЗКА ПРОМПТОВ (сценарий: manifest в pluginSettings)") - - prompts = {'optimized': {'ru': '', 'en': ''}, 'deep': {'ru': '', 'en': ''}} - - # В этом сценарии manifest находится в pluginSettings.manifest - manifest = safe_dict_get(plugin_settings, 'manifest', {}) - console_log(f" manifest из pluginSettings: {manifest is not None}") - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {bool(manifest_prompts)}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = safe_dict_get( - manifest_prompts, - prompt_type, {} - ).get(lang, {}).get('default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ {prompt_type}.{lang}: загружен из pluginSettings.manifest") - else: - console_log(f"⚠️ {prompt_type}.{lang}: не найден в pluginSettings.manifest") - - return prompts - -def get_user_prompts_globals_scenario(plugin_settings, pyodide_context): - """ - Функция загрузки промптов для сценария с manifest в globals - """ - console_log("🔍 ЗАГРУЗКА ПРОМПТОВ (сценарий: manifest в globals)") - - prompts = {'optimized': {'ru': '', 'en': ''}, 'deep': {'ru': '', 'en': ''}} - - # В этом сценарии manifest находится в globals - manifest = pyodide_context.get_global('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {bool(manifest_prompts)}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = safe_dict_get( - manifest_prompts, - prompt_type, {} - ).get(lang, {}).get('default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ {prompt_type}.{lang}: загружен из globals.manifest") - else: - console_log(f"⚠️ {prompt_type}.{lang}: не найден в globals.manifest") - - return prompts - -def test_scenario(name: str, scenario_function, prompt_loader_function, pyodide_context): - """Тестирование конкретного сценария""" - print(f"\n{'='*80}") - print(f"🧪 ТЕСТИРОВАНИЕ СЦЕНАРИЯ: {name}") - print('='*80) - - # Настройка сценария - plugin_settings = scenario_function() - - # Загрузка промптов - if 'globals' in name.lower(): - prompts = prompt_loader_function(plugin_settings, pyodide_context) - else: - prompts = prompt_loader_function(plugin_settings) - - # Проверка результатов - success_count = 0 - expected_min_length = 100 # Минимальная длина промпта - - print("\n📊 РЕЗУЛЬТАТЫ ЗАГРУЗКИ ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - length = len(prompt_value) if prompt_value else 0 - - if length > expected_min_length: - print(f"✅ {prompt_type}.{lang}: {length} символов") - success_count += 1 - else: - print(f"❌ {prompt_type}.{lang}: {length} символов (слишком короткий)") - - total_prompts = 4 - success_rate = success_count / total_prompts - - if success_rate >= 0.75: # Минимум 3 из 4 промптов - print(f"🎉 СЦЕНАРИЙ ПРОЙДЕН: {success_count}/{total_prompts} промптов загружено") - return True - else: - print(f"❌ СЦЕНАРИЙ НЕ ПРОЙДЕН: {success_count}/{total_prompts} промптов загружено") - return False - -def main(): - """Основная функция тестирования""" - print("🚀 ЗАПУСК ТЕСТОВ ПЕРЕДАЧИ MANIFEST") - print("Проверяем сценарии: manifest в pluginSettings vs manifest в globals") - - # Создаем Pyodide контекст - pyodide_context = MockPyodideContext() - - # Сценарий 1: manifest в pluginSettings - scenario1_passed = test_scenario( - "Manifest в pluginSettings", - pyodide_context.get_plugin_settings_manifest_scenario, - get_user_prompts_plugin_settings_scenario, - pyodide_context - ) - - # Очищаем globals для следующего сценария - pyodide_context.globals.clear() - - # Сценарий 2: manifest в globals - scenario2_passed = test_scenario( - "Manifest в globals", - pyodide_context.get_globals_manifest_scenario, - get_user_prompts_globals_scenario, - pyodide_context - ) - - # Итоговый результат - print(f"\n{'='*80}") - print("📋 ИТОГОВЫЕ РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ") - print('='*80) - - print(f"Сценарий 1 (pluginSettings.manifest): {'✅ ПРОЙДЕН' if scenario1_passed else '❌ НЕ ПРОЙДЕН'}") - print(f"Сценарий 2 (globals.manifest): {'✅ ПРОЙДЕН' if scenario2_passed else '❌ НЕ ПРОЙДЕН'}") - - if scenario1_passed and scenario2_passed: - print("\n🎉 ВСЕ СЦЕНАРИИ ПРОЙДЕНЫ: Исправление передачи manifest работает корректно!") - print("✅ Manifest может передаваться как в pluginSettings, так и в globals") - print("✅ Промпты корректно загружаются в обоих сценариях") - return True - else: - print("\n💥 ОБНАРУЖЕНЫ ПРОБЛЕМЫ: Некоторые сценарии не работают") - if not scenario1_passed: - print("❌ Проблема со сценарием pluginSettings.manifest") - if not scenario2_passed: - print("❌ Проблема со сценарием globals.manifest") - return False - -if __name__ == "__main__": - success = main() - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_manifest_transmission.py b/test_manifest_transmission.py deleted file mode 100644 index 526bb5a8..00000000 --- a/test_manifest_transmission.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест механизма передачи manifest.json в Pyodide контекст - -Сценарий 1: Тестирование корректности передачи manifest.json в Pyodide контекст -- Проверка что переменная manifest доступна в Pyodide globals -- Проверка что структура manifest.options.prompts правильно доступна -- Проверка что промпты корректно извлекаются из manifest.json -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def test_manifest_transmission(): - """Тест передачи manifest.json в Pyodide контекст""" - print("🔍 ТЕСТ 1: Проверка корректности передачи manifest.json в Pyodide контекст") - print("=" * 70) - - # Загружаем manifest.json для сравнения - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - print(f"✅ Manifest.json загружен: {len(json.dumps(manifest_data))} символов") - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return False - - # Проверяем структуру промптов в manifest.json - manifest_prompts = manifest_data.get('options', {}).get('prompts', {}) - if not manifest_prompts: - print("❌ Структура промптов не найдена в manifest.json") - return False - - print("📋 Структура промптов в manifest.json:") - print(f" - optimized промпты: {list(manifest_prompts.get('optimized', {}).keys())}") - print(f" - deep промпты: {list(manifest_prompts.get('deep', {}).keys())}") - - # Проверяем наличие промптов по умолчанию - optimized_ru = manifest_prompts.get('optimized', {}).get('ru', {}).get('default', '') - optimized_en = manifest_prompts.get('optimized', {}).get('en', {}).get('default', '') - deep_ru = manifest_prompts.get('deep', {}).get('ru', {}).get('default', '') - deep_en = manifest_prompts.get('deep', {}).get('en', {}).get('default', '') - - print("📊 Длина промптов по умолчанию в manifest.json:") - print(f" - optimized.ru: {len(optimized_ru)} символов") - print(f" - optimized.en: {len(optimized_en)} символов") - print(f" - deep.ru: {len(deep_ru)} символов") - print(f" - deep.en: {len(deep_en)} символов") - - # Проверяем что все промпты присутствуют и не пустые - all_prompts_present = all([ - len(optimized_ru) > 100, - len(optimized_en) > 100, - len(deep_ru) > 100, - len(deep_en) > 100 - ]) - - if not all_prompts_present: - print("❌ Некоторые промпты отсутствуют или слишком короткие в manifest.json") - return False - - print("✅ Все промпты присутствуют в manifest.json") - - # Симулируем процесс передачи в Pyodide globals (как в offscreen.ts) - print("\n🔄 Симуляция передачи manifest в Pyodide globals...") - - # Симулируем Pyodide globals - class MockGlobals: - def __init__(self): - self.data = {} - - def set(self, key: str, value: Any): - self.data[key] = value - print(f" 📥 Установлен {key}: {type(value)} ({len(str(value))} символов)") - - def get(self, key: str, default=None): - return self.data.get(key, default) - - # Создаем mock Pyodide globals - globals_mock = MockGlobals() - - # Передаем manifest (как в offscreen.ts строки 518-519) - globals_mock.set('manifest', manifest_data) - - # Верифицируем передачу (как в offscreen.ts строки 525-534) - print("\n🔍 Верификация передачи manifest...") - - verify_manifest = globals_mock.get('manifest') - if not verify_manifest: - print("❌ Manifest НЕ НАЙДЕН в globals после передачи") - return False - - print(f"✅ Manifest подтвержден в globals: {type(verify_manifest)}") - - if isinstance(verify_manifest, dict): - verify_keys = list(verify_manifest.keys()) - print(f"✅ Ключи в globals: {verify_keys}") - - # Проверяем что промпты доступны через globals - globals_prompts = verify_manifest.get('options', {}).get('prompts', {}) - print(f"✅ Промпты доступны через globals: {type(globals_prompts)}") - - # Проверяем каждый промпт - test_cases = [ - ('optimized', 'ru', optimized_ru), - ('optimized', 'en', optimized_en), - ('deep', 'ru', deep_ru), - ('deep', 'en', deep_en) - ] - - all_match = True - for prompt_type, lang, expected in test_cases: - globals_value = globals_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - if globals_value == expected: - print(f"✅ {prompt_type}.{lang}: совпадает ({len(globals_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: НЕ СОВПАДАЕТ!") - print(f" Ожидалось: {len(expected)} символов") - print(f" Получено: {len(globals_value)} символов") - all_match = False - - if all_match: - print("\n🎉 ТЕСТ ПРОЙДЕН: Manifest успешно передан в Pyodide контекст!") - return True - else: - print("\n❌ ТЕСТ НЕ ПРОЙДЕН: Некоторые промпты не совпадают после передачи") - return False - - else: - print(f"❌ Manifest в globals не является словарем: {type(verify_manifest)}") - return False - -if __name__ == "__main__": - success = test_manifest_transmission() - if success: - print("\n🎯 РЕЗУЛЬТАТ: Исправление механизма передачи manifest.json работает корректно!") - else: - print("\n💥 РЕЗУЛЬТАТ: Обнаружены проблемы с передачей manifest.json!") - - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_manifest_transmission_integration.py b/test_manifest_transmission_integration.py deleted file mode 100644 index 8b164ad0..00000000 --- a/test_manifest_transmission_integration.py +++ /dev/null @@ -1,376 +0,0 @@ -#!/usr/bin/env python3 -""" -Интеграционный тест передачи manifest в Pyodide и использования промптов в mcp_server.py -""" - -import json -import sys -import os -from typing import Any - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def chat_message(message: str): - """Имитация отправки сообщения в чат.""" - print(f"[CHAT_MESSAGE] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif name == 'pluginSettings': - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'search_for_analogs': False - } - - return default - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts_test(plugin_settings=None): - """ - Тестовая версия функции get_user_prompts из mcp_server.py - """ - console_log(f"🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТ) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - - # Диагностика входных данных - console_log(f"🔍 Диагностика входных данных:") - console_log(f" plugin_settings type: {type(plugin_settings)}") - console_log(f" plugin_settings is None: {plugin_settings is None}") - console_log(f" plugin_settings keys: {list(plugin_settings.keys()) if isinstance(plugin_settings, dict) else 'не словарь'}") - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" custom_prompts_raw: {custom_prompts_raw}") - console_log(f" custom_prompts_raw type: {type(custom_prompts_raw)}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - console_log(f" manifest_prompts type: {type(manifest_prompts)}") - else: - console_log(f" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log(f"🔍 Итоговая диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log(f"🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - # Подсчет загруженных промптов - loaded_prompts = len([p for pt in prompts.values() for p in pt.values() if p and len(p.strip()) > 0]) - total_prompts = len([p for pt in prompts.values() for p in pt.values()]) - console_log(f"📋 Загружено промптов: {loaded_prompts}/{total_prompts} (кастомных: {loaded_prompts})") - - # ДИАГНОСТИКА ПРОБЛЕМЫ: Проверяем источник каждого промпта - console_log(f"🔍 ДИАГНОСТИКА ИСТОЧНИКОВ ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - # Определяем источник промпта - if custom_prompts_raw and custom_prompts_raw.get(prompt_type, {}).get(lang) == prompt_value: - source = "кастомный" - elif manifest_prompts and manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default') == prompt_value: - source = "manifest default" - else: - source = "встроенный fallback" - console_log(f" 📍 {prompt_type}.{lang}: источник = {source}") - else: - console_log(f" ❌ {prompt_type}.{lang}: источник = отсутствует") - - console_log(f"🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - console_log(f"🔍 Детальная диагностика ошибки:") - console_log(f" Exception type: {type(e).__name__}") - console_log(f" Exception args: {e.args}") - console_log(f" Plugin settings: {plugin_settings}") - console_log(f" Plugin settings type: {type(plugin_settings)}") - - # Трассировка стека для диагностики - import traceback - stack_trace = traceback.format_exc() - console_log(f" Stack trace: {stack_trace}") - - # Критический fallback - возвращаем пустые промпты - console_log(f"⚠️ Возвращаем критический fallback с пустыми промптами") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """ - Возвращает встроенные промпты по умолчанию. - """ - return "Встроенный промпт по умолчанию" - -def test_manifest_transmission_and_usage(): - """ - Тест передачи manifest и использования промптов в mcp_server.py - """ - print("=" * 80) - print("🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ ПЕРЕДАЧИ MANIFEST") - print("=" * 80) - - # Шаг 1: Загрузка manifest - print("\n📋 ШАГ 1: Загрузка manifest.json") - print("-" * 50) - - manifest = get_pyodide_var('manifest') - if not manifest: - print("❌ ОШИБКА: Manifest не загружен") - return False - - print(f"✅ Manifest загружен: {len(json.dumps(manifest))} символов") - print(f" Название: {manifest.get('name', 'N/A')}") - print(f" Версия: {manifest.get('version', 'N/A')}") - - # Шаг 2: Проверка структуры промптов - print("\n📋 ШАГ 2: Проверка структуры промптов в manifest") - print("-" * 50) - - prompts = manifest.get('options', {}).get('prompts', {}) - if not prompts: - print("❌ ОШИБКА: Структура промптов не найдена") - return False - - expected_types = ['optimized', 'deep'] - expected_langs = ['ru', 'en'] - - prompt_counts = {} - for prompt_type in expected_types: - for lang in expected_langs: - key = f"{prompt_type}.{lang}" - prompt_value = prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - prompt_counts[key] = len(prompt_value) - if prompt_value: - print(f"✅ {key}: {len(prompt_value)} символов") - else: - print(f"❌ {key}: отсутствует") - - # Проверяем что есть хотя бы один полноценный промпт - meaningful_prompts = sum(1 for count in prompt_counts.values() if count > 100) - if meaningful_prompts < 2: - print(f"❌ Мало полноценных промптов: {meaningful_prompts}/4") - return False - - print(f"✅ Найдено {meaningful_prompts} полноценных промптов") - - # Шаг 3: Имитация передачи manifest в Pyodide globals - print("\n📋 ШАГ 3: Имитация передачи manifest в Pyodide globals") - print("-" * 50) - - # Имитируем Pyodide globals - pyodide_globals = {} - - # Передаем manifest (как в offscreen.ts:504) - pyodide_globals['manifest'] = manifest - console_log('[BRIDGE DIAGNOSTIC] ✅ Manifest передан в Pyodide globals из pluginSettings') - - # Проверяем доступность - if 'manifest' not in pyodide_globals: - print("❌ ОШИБКА: Manifest не найден в Pyodide globals") - return False - - transmitted_manifest = pyodide_globals['manifest'] - if transmitted_manifest != manifest: - print("❌ ОШИБКА: Переданный manifest не совпадает с оригиналом") - return False - - print("✅ Manifest успешно передан в Pyodide globals") - - # Шаг 4: Имитация использования промптов в mcp_server.py - print("\n📋 ШАГ 4: Имитация использования промптов в mcp_server.py") - print("-" * 50) - - # Имитируем globals для mcp_server - import builtins - original_globals = builtins.globals - builtins.globals = lambda: pyodide_globals - - try: - # Тестируем get_user_prompts - plugin_settings = get_pyodide_var('pluginSettings') - loaded_prompts = get_user_prompts_test(plugin_settings) - - # Проверяем что промпты загружены - success_count = 0 - for prompt_type in expected_types: - for lang in expected_langs: - if loaded_prompts.get(prompt_type, {}).get(lang): - success_count += 1 - print(f"✅ {prompt_type}.{lang}: успешно загружен") - else: - print(f"❌ {prompt_type}.{lang}: НЕ загружен") - - if success_count < 2: - print(f"❌ Мало загруженных промптов: {success_count}/4") - return False - - print(f"✅ Успешно загружено {success_count} промптов из manifest") - - finally: - # Восстанавливаем оригинальные globals - builtins.globals = original_globals - - # Шаг 5: Проверка что промпты не из fallback - print("\n📋 ШАГ 5: Проверка что промпты загружены из manifest, а не fallback") - print("-" * 50) - - # Сравниваем загруженные промпты с manifest - manifest_mismatch = 0 - for prompt_type in expected_types: - for lang in expected_langs: - manifest_prompt = prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - loaded_prompt = loaded_prompts.get(prompt_type, {}).get(lang, '') - - if manifest_prompt and loaded_prompt == manifest_prompt: - print(f"✅ {prompt_type}.{lang}: совпадает с manifest") - elif manifest_prompt and loaded_prompt != manifest_prompt: - print(f"⚠️ {prompt_type}.{lang}: НЕ совпадает с manifest") - manifest_mismatch += 1 - else: - print(f"ℹ️ {prompt_type}.{lang}: manifest пустой, используется fallback") - - if manifest_mismatch > 0: - print(f"⚠️ Найдено {manifest_mismatch} несоответствий с manifest") - else: - print("✅ Все промпты загружены из manifest (без fallback)") - - # Финальная проверка - print("\n" + "=" * 80) - if success_count >= 2 and meaningful_prompts >= 2: - print("🎉 ТЕСТ ПРОЙДЕН: Manifest успешно передается и промпты загружаются!") - print("=" * 80) - print("📋 РЕЗУЛЬТАТЫ:") - print(" ✅ Manifest.json загружается корректно") - print(" ✅ Manifest передается в Pyodide globals") - print(" ✅ mcp_server.py получает доступ к manifest") - print(" ✅ Промпты загружаются из manifest, а не из fallback") - print(" ✅ Структура промптов соответствует ожиданиям") - return True - else: - print("❌ ТЕСТ НЕ ПРОЙДЕН: Обнаружены проблемы с передачей manifest") - return False - -if __name__ == "__main__": - success = test_manifest_transmission_and_usage() - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_prompts_extraction.py b/test_prompts_extraction.py deleted file mode 100644 index dc49b617..00000000 --- a/test_prompts_extraction.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест извлечения промптов из manifest.json в функции get_user_prompts() - -Сценарий 2: Тестирование извлечения промптов из manifest.json в функции get_user_prompts() -- Тестирование извлечения промптов из manifest.json -- Проверка что используются оригинальные промпты, а не встроенные fallback -- Тестирование fallback-логики при повреждении manifest.json -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def console_log(message: str): - """Эмуляция функции логирования из mcp_server.py""" - print(f"[PYTHON_LOG] {message}") - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасная функция получения значения из словаря""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Эмуляция функции получения переменных из Pyodide globals""" - try: - if name in globals(): - value = globals()[name] - return value - else: - return default - except Exception as e: - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """Встроенные промпты по умолчанию из mcp_server.py""" - builtin_prompts = { - 'optimized': { - 'ru': """Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки. - -ДАННЫЕ: -Описание: {description} -Состав: {composition} - -ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА: - -1. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ: -- Термины типа "3D/4D/5D", "революционный", "инновационный" - ТРЕБУЮТ доказательств -- Для каждого заявления ("лифтинг", "против морщин"): - * Найди КОНКРЕТНЫЙ активный компонент - * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг) - * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте - -2. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1): -- Проверь КАЖДЫЙ компонент на: - * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, и т.д.) - * Парабены (особенно butyl-, propyl-) - * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone) - * Потенциальные эндокринные дизрапторы -- Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10 - -3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ: -- Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален -- Collagen/Elastin: молекулы НЕ проникают, работают только как пленка -- Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины - -4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ: -- Современная косметика = без парабенов, с новыми консервантами -- Устаревшие формулы → снижение оценки на 2-3 балла - -5. ШКАЛА ОЦЕНКИ (СТРОГАЯ): -- 9-10: Идеальный состав, доказанные активы в высоких концентрациях, без токсичных компонентов -- 7-8: Хороший состав, минимум проблемных компонентов -- 5-6: Средний продукт, есть проблемные компоненты ИЛИ активы в низких дозах -- 3-4: Устаревшая формула, много токсичных компонентов, маркетинговые заявления не подтверждены -- 1-2: Опасный или полностью бесполезный продукт - -КРИТИЧЕСКИ ВАЖНО: -- Будь СКЕПТИЧЕН к маркетингу -- НЕ завышай оценку из вежливости -- Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10 -- Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку - -ФОРМАТ ОТВЕТА - ТОЛЬКО JSON: -{{ -"score": число_от_1_до_10, -"reasoning": "ДЕТАЛЬНЫЙ анализ: - 1. Проверка маркетинга: [разбери каждое заявление] - 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты] - 3. Реальная эффективность активов: [концентрации vs заявления] - 4. Сравнение с современными стандартами: [почему устарел/актуален] - 5. Итоговый вердикт: [честное заключение]", -"confidence": число_от_0_до_1, -"red_flags": ["список всех токсичных/проблемных компонентов"], -"marketing_lies": ["список не подтвержденных маркетинговых заявлений"] -}} - -ЯЗЫК: Русский, технический стиль с примерами.""", - 'en': """You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation. - -DATA: -Description: {description} -Composition: {composition} - -MANDATORY ANALYSIS PROTOCOL: - -1. TOXICOLOGICAL SCREENING (highest priority): -- Screen EVERY ingredient for: - * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea) - * Parabens (particularly butylparaben, propylparaben - EU restricted) - * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone) - * Known/suspected endocrine disruptors -- HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum - -2. MARKETING CLAIMS VERIFICATION: -- Buzzwords like "3D/4D/5D technology", "revolutionary", "clinical breakthrough" - DEMAND evidence -- For each claim ("lifting", "anti-wrinkle", "firming"): - * Identify the SPECIFIC active ingredient responsible - * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose) - * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product - -3. REALISTIC EFFICACY ASSESSMENT: -- Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect -- Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers -- Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep wrinkles - -4. MODERN FORMULATION STANDARDS COMPARISON: -- 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails -- Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction - -5. EVIDENCE-BASED SCORING RUBRIC (strict grading): -- 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile -- 7-8: Well-formulated, minor concerns only, actives present at reasonable levels -- 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims) -- 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing -- 1-2: Potentially harmful or fraudulent product - -CRITICAL ASSESSMENT RULES: -- Maintain scientific skepticism toward all marketing language -- Apply evidence-based standards, NOT brand reputation -- Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10 -- Claims unsupported by adequate active concentrations = reduce score proportionally -- Default to LOWER score when ingredient concentrations are ambiguous - -OUTPUT FORMAT - VALID JSON ONLY: -{{ -"score": integer_1_to_10, -"reasoning": "COMPREHENSIVE ANALYSIS: - 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks] - 2. Marketing Claims Audit: [fact-check each claim against ingredient reality] - 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence] - 4. Formulation Modernity Assessment: [evaluate against current industry standards] - 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]", -"confidence": float_0_to_1, -"red_flags": ["comprehensive list of problematic/toxic/outdated ingredients"], -"marketing_lies": ["specific unsubstantiated or misleading marketing claims"] -}} - -RESPONSE LANGUAGE: English, using precise technical terminology.""" - }, - 'deep': { - 'ru': """Проведи глубокий анализ товара с медицинской и научной точки зрения. -Описание: {description} -Состав: {composition} -Проанализируй: -1. Научную обоснованность заявленных свойств. -2. Потенциальные побочные эффекты и противопоказания. -3. Эффективность по сравнению с аналогами. -Верни детальный максимально подробный обоснованный анализ в структурированном виде (используй Markdown).""", - 'en': """Conduct a deep analysis of the product from a medical and scientific perspective. -Description: {description} -Composition: {composition} -Analyze: -1. Scientific validity of the claimed properties. -2. Potential side effects and contraindications. -3. Effectiveness compared to analogs. -Return a detailed, maximally comprehensive, reasoned analysis in a structured form (use Markdown).""" - } - } - - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def test_get_user_prompts_function(): - """Тест функции get_user_prompts с различными сценариями""" - print("🔍 ТЕСТ 2: Тестирование извлечения промптов из manifest.json") - print("=" * 70) - - # Загружаем manifest.json - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - print(f"✅ Manifest.json загружен: {len(json.dumps(manifest_data))} символов") - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return False - - # Тест 2.1: Проверка извлечения промптов из manifest.json - print("\n📋 ТЕСТ 2.1: Извлечение промптов из manifest.json") - - # Симулируем Pyodide globals с manifest - globals()['manifest'] = manifest_data - - # Тестируем извлечение промптов - plugin_settings = {} # Пустые настройки пользователя - - # Симулируем логику из get_user_prompts - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - manifest = get_pyodide_var('manifest', {}) - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - - print(f"✅ Кастомные промпты: {type(custom_prompts_raw)}") - print(f"✅ Manifest промпты доступны: {manifest_prompts is not None}") - - # Проверяем каждый промпт - test_cases = [ - ('optimized', 'ru'), - ('optimized', 'en'), - ('deep', 'ru'), - ('deep', 'en') - ] - - prompts_structure = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - for prompt_type, lang in test_cases: - print(f"\n🔍 Обработка {prompt_type}.{lang}:") - - # Проверяем кастомный промпт - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - custom_value = safe_dict_get(prompt_type_data, lang, '') - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts_structure[prompt_type][lang] = custom_value - print(f"✅ Используем кастомный промпт ({len(custom_value)} символов)") - else: - print("ℹ️ Кастомный промпт не найден - переходим к manifest") - - # Fallback на manifest.json - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts_structure[prompt_type][lang] = manifest_value - print(f"✅ Используем промпт из manifest ({len(manifest_value)} символов)") - else: - print("⚠️ Промпт в manifest пустой - переходим к встроенному") - - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - prompts_structure[prompt_type][lang] = builtin_value - print(f"✅ Используем встроенный промпт ({len(builtin_value)} символов)") - else: - print(f"⚠️ Встроенный промпт не найден для {prompt_type}.{lang}") - else: - print(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - prompts_structure[prompt_type][lang] = builtin_value - print(f"✅ Используем встроенный промпт ({len(builtin_value)} символов)") - - # Анализируем результаты - loaded_prompts = 0 - total_prompts = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts_structure[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 100: - loaded_prompts += 1 - print(f"✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: НЕ загружен или слишком короткий ({len(prompt_value) if prompt_value else 0} символов)") - - print(f"\n📊 Результаты: {loaded_prompts}/{total_prompts} промптов загружено") - - if loaded_prompts == total_prompts: - print("🎉 ТЕСТ 2.1 ПРОЙДЕН: Все промпты успешно извлечены!") - test_2_1_passed = True - else: - print("❌ ТЕСТ 2.1 НЕ ПРОЙДЕН: Некоторые промпты не загружены") - test_2_1_passed = False - - # Тест 2.2: Тестирование fallback логики при повреждении manifest.json - print("\n📋 ТЕСТ 2.2: Fallback логика при повреждении manifest.json") - - # Симулируем поврежденный manifest - damaged_manifest = { - 'options': { - 'prompts': { - 'optimized': { - 'ru': {'default': ''}, # Поврежденный промпт - 'en': {'default': 'valid prompt'} - } - } - } - } - - globals()['manifest'] = damaged_manifest - - print("🔄 Тестируем fallback при поврежденном manifest...") - - # Считаем сколько промптов fallbackнули на встроенные - builtin_fallbacks = 0 - - for prompt_type, lang in test_cases: - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - custom_value = safe_dict_get(prompt_type_data, lang, '') - - if not custom_value or len(custom_value.strip()) == 0: - # Пытаемся получить из поврежденного manifest - type_prompts = safe_dict_get(damaged_manifest, 'options', {}).get('prompts', {}) - lang_data = safe_dict_get(type_prompts, prompt_type, {}).get(lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if not manifest_value or len(manifest_value.strip()) == 0: - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - builtin_fallbacks += 1 - print(f"✅ {prompt_type}.{lang}: fallback на встроенный промпт ({len(builtin_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: встроенный промпт не найден") - else: - print(f"✅ {prompt_type}.{lang}: используем из поврежденного manifest ({len(manifest_value)} символов)") - else: - print(f"✅ {prompt_type}.{lang}: используем кастомный промпт ({len(custom_value)} символов)") - - print(f"\n📊 Fallback результаты: {builtin_fallbacks} промптов fallbackнули на встроенные") - - if builtin_fallbacks > 0: - print("🎉 ТЕСТ 2.2 ПРОЙДЕН: Fallback логика работает корректно!") - test_2_2_passed = True - else: - print("⚠️ ТЕСТ 2.2: Fallback не потребовался или не сработал") - test_2_2_passed = True # Это тоже корректно - - # Тест 2.3: Проверка что используются оригинальные промпты из manifest.json - print("\n📋 ТЕСТ 2.3: Проверка использования оригинальных промптов") - - # Восстанавливаем оригинальный manifest - globals()['manifest'] = manifest_data - - # Сравниваем промпты из manifest с встроенными - original_from_manifest = 0 - builtin_used = 0 - - for prompt_type, lang in test_cases: - # Получаем промпт из manifest - manifest_prompt = manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - - # Получаем встроенный промпт - builtin_prompt = _get_builtin_default_prompt(prompt_type, lang) - - if manifest_prompt and len(manifest_prompt.strip()) > 100: - if builtin_prompt and manifest_prompt != builtin_prompt: - original_from_manifest += 1 - print(f"✅ {prompt_type}.{lang}: используем ОРИГИНАЛЬНЫЙ промпт из manifest ({len(manifest_prompt)} символов)") - else: - builtin_used += 1 - print(f"ℹ️ {prompt_type}.{lang}: промпт из manifest совпадает со встроенным ({len(manifest_prompt)} символов)") - else: - print(f"⚠️ {prompt_type}.{lang}: промпт в manifest поврежден или отсутствует") - - print(f"\n📊 Оригинальные промпты: {original_from_manifest}/4") - print(f"📊 Совпадают со встроенными: {builtin_used}/4") - - if original_from_manifest >= 2: # Минимум 2 оригинальных промпта - print("🎉 ТЕСТ 2.3 ПРОЙДЕН: Используются оригинальные промпты из manifest.json!") - test_2_3_passed = True - else: - print("⚠️ ТЕСТ 2.3: Недостаточно оригинальных промптов в manifest.json") - test_2_3_passed = False - - # Итоговые результаты - all_tests_passed = test_2_1_passed and test_2_2_passed and test_2_3_passed - - print("\n" + "=" * 70) - print("📋 ИТОГОВЫЕ РЕЗУЛЬТАТЫ ТЕСТА 2:") - print(f" Тест 2.1 (Извлечение промптов): {'✅ ПРОЙДЕН' if test_2_1_passed else '❌ НЕ ПРОЙДЕН'}") - print(f" Тест 2.2 (Fallback логика): {'✅ ПРОЙДЕН' if test_2_2_passed else '❌ НЕ ПРОЙДЕН'}") - print(f" Тест 2.3 (Оригинальные промпты): {'✅ ПРОЙДЕН' if test_2_3_passed else '❌ НЕ ПРОЙДЕН'}") - - if all_tests_passed: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ: Функция get_user_prompts() работает корректно!") - return True - else: - print("\n💥 НЕКОТОРЫЕ ТЕСТЫ НЕ ПРОЙДЕНЫ: Есть проблемы с извлечением промптов") - return False - -if __name__ == "__main__": - success = test_get_user_prompts_function() - if success: - print("\n🎯 РЕЗУЛЬТАТ: Исправление механизма извлечения промптов работает корректно!") - else: - print("\n💥 РЕЗУЛЬТАТ: Обнаружены проблемы с извлечением промптов!") - - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_prompts_mechanism.py b/test_prompts_mechanism.py deleted file mode 100644 index 1f13749e..00000000 --- a/test_prompts_mechanism.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 ТЕСТОВЫЙ СКРИПТ ДЛЯ ДИАГНОСТИКИ МЕХАНИЗМА ПРОМПТОВ -Диагностирует проблемы передачи промптов из manifest.json в Pyodide контекст -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем путь к плагину в sys.path для импорта функций -plugin_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer') -sys.path.insert(0, plugin_path) - -# Загружаем manifest.json для тестирования -def load_manifest(): - """Загружает manifest.json для тестирования""" - manifest_path = os.path.join(plugin_path, 'manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return None - -def simulate_pyodide_globals(manifest: Dict[str, Any]) -> Dict[str, Any]: - """Симулирует Pyodide globals для тестирования""" - return { - 'manifest': manifest, - 'pluginSettings': { - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', - 'en': 'CUSTOM PROMPT ENGLISH' - }, - 'deep': { - 'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', - 'en': 'DEEP ANALYSIS ENGLISH' - } - } - } - } - -def simulate_empty_plugin_settings(manifest: Dict[str, Any]) -> Dict[str, Any]: - """Симулирует пустые настройки пользователя""" - return { - 'manifest': manifest, - 'pluginSettings': {} - } - -def simulate_broken_manifest() -> Dict[str, Any]: - """Симулирует поврежденный manifest""" - return { - 'manifest': {'invalid': 'data'}, - 'pluginSettings': {} - } - -def simulate_none_manifest() -> Dict[str, Any]: - """Симулирует отсутствие manifest""" - return { - 'pluginSettings': { - 'prompts': { - 'optimized': { - 'ru': 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ' - } - } - } - } - -def test_scenario_1_full_custom_prompts(): - """Сценарий 1: Полностью заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 1: Полностью заполненные кастомные промпты") - print("=" * 60) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return None - - # Симулируем Pyodide globals - globals_data = simulate_pyodide_globals(manifest) - - # Импортируем и тестируем функцию - try: - from mcp_server import get_user_prompts - - # Мокаем функции для симуляции Pyodide окружения - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - # Внедряем моки в глобальное пространство - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем ожидаемые результаты - expected_results = { - 'optimized': {'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', 'en': 'CUSTOM PROMPT ENGLISH'}, - 'deep': {'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', 'en': 'DEEP ANALYSIS ENGLISH'} - } - - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - if result.get(prompt_type, {}).get(lang) != expected_results[prompt_type][lang]: - print(f"❌ Несоответствие в {prompt_type}.{lang}") - success = False - - if success: - print("✅ Сценарий 1 ПРОЙДЕН: Кастомные промпты корректно используются") - else: - print("❌ Сценарий 1 ПРОВАЛЕН: Кастомные промпты не используются правильно") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 1: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_2_empty_custom_prompts(): - """Сценарий 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 СЦЕНАРИЙ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 60) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return None - - # Симулируем Pyodide globals с пустыми промптами - globals_data = simulate_empty_plugin_settings(manifest) - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используются промпты из manifest - manifest_prompts = manifest.get('options', {}).get('prompts', {}) - success = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - result_value = result.get(prompt_type, {}).get(lang, '') - - if manifest_value and result_value == manifest_value: - print(f"✅ {prompt_type}.{lang}: Используется промпт из manifest") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие промпта из manifest") - success = False - - if success: - print("✅ Сценарий 2 ПРОЙДЕН: Fallback на manifest работает корректно") - else: - print("❌ Сценарий 2 ПРОВАЛЕН: Fallback на manifest не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 2: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_3_broken_manifest(): - """Сценарий 3: Поврежденный manifest.json""" - print("\n🧪 СЦЕНАРИЙ 3: Поврежденный manifest.json") - print("=" * 60) - - globals_data = simulate_broken_manifest() - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - def mock_get_builtin_default_prompt(prompt_type, lang): - return f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - mcp_server._get_builtin_default_prompt = mock_get_builtin_default_prompt - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется fallback на встроенные промпты - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_value = f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - result_value = result.get(prompt_type, {}).get(lang, '') - - if result_value == expected_value: - print(f"✅ {prompt_type}.{lang}: Используется встроенный промпт") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие встроенного промпта") - success = False - - if success: - print("✅ Сценарий 3 ПРОЙДЕН: Fallback на встроенные промпты работает") - else: - print("❌ Сценарий 3 ПРОВАЛЕН: Fallback на встроенные промпты не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 3: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_4_missing_manifest(): - """Сценарий 4: Отсутствующий manifest в globals""" - print("\n🧪 СЦЕНАРИЙ 4: Отсутствующий manifest в globals") - print("=" * 60) - - globals_data = simulate_none_manifest() - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - def mock_get_builtin_default_prompt(prompt_type, lang): - return f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - mcp_server._get_builtin_default_prompt = mock_get_builtin_default_prompt - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется кастомный промпт и встроенные для остальных - success = True - - # optimized.ru должен быть кастомным - if result.get('optimized', {}).get('ru') == 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ': - print("✅ optimized.ru: Используется кастомный промпт") - else: - print("❌ optimized.ru: Кастомный промпт не используется") - success = False - - # optimized.en должен быть встроенным (отсутствует в кастомных) - if result.get('optimized', {}).get('en') == 'ВСТРОЕННЫЙ ПРОМПТ optimized.en': - print("✅ optimized.en: Используется встроенный промпт") - else: - print("❌ optimized.en: Встроенный промпт не используется") - success = False - - if success: - print("✅ Сценарий 4 ПРОЙДЕН: Частичный fallback работает корректно") - else: - print("❌ Сценарий 4 ПРОВАЛЕН: Частичный fallback не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 4: {e}") - import traceback - traceback.print_exc() - return None - -def run_diagnostic_tests(): - """Запуск всех диагностических тестов""" - print("🔍 НАЧИНАЕМ ДИАГНОСТИКУ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 80) - - # Тест 1: Анализ структуры manifest.json - print("\n📋 АНАЛИЗ СТРУКТУРЫ MANIFEST.JSON") - print("=" * 40) - - manifest = load_manifest() - if manifest: - print("✅ manifest.json загружен успешно") - - # Анализируем структуру промптов - options = manifest.get('options', {}) - prompts = options.get('prompts', {}) - - print(f"📊 Структура промптов в manifest:") - print(f" Типы промптов: {list(prompts.keys())}") - - for prompt_type in ['optimized', 'deep']: - if prompt_type in prompts: - print(f" {prompt_type}:") - type_prompts = prompts[prompt_type] - for lang in ['ru', 'en']: - if lang in type_prompts: - lang_data = type_prompts[lang] - default_value = lang_data.get('default', '') - print(f" {lang}: длина={len(default_value)} символов") - print(f" Предварительный просмотр: {default_value[:100]}...") - else: - print(f" {lang}: ОТСУТСТВУЕТ") - else: - print(f" {prompt_type}: ОТСУТСТВУЕТ") - else: - print("❌ Не удалось загрузить manifest.json") - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ТЕСТОВЫХ СЦЕНАРИЕВ") - print("=" * 40) - - results['scenario_1'] = test_scenario_1_full_custom_prompts() - results['scenario_2'] = test_scenario_2_empty_custom_prompts() - results['scenario_3'] = test_scenario_3_broken_manifest() - results['scenario_4'] = test_scenario_4_missing_manifest() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ") - print("=" * 50) - - passed = 0 - total = 0 - - for scenario_name, result in results.items(): - total += 1 - if result is not None: - passed += 1 - print(f"✅ {scenario_name}: ПРОЙДЕН") - else: - print(f"❌ {scenario_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} сценариев пройдено") - - if passed == total: - print("🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Механизм промптов работает корректно.") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная диагностика.") - - return results - -if __name__ == "__main__": - run_diagnostic_tests() \ No newline at end of file diff --git a/test_prompts_standalone.py b/test_prompts_standalone.py deleted file mode 100644 index a80ed086..00000000 --- a/test_prompts_standalone.py +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 АВТОНОМНЫЙ ТЕСТ МЕХАНИЗМА ПРОМПТОВ -Тестирует логику функции get_user_prompts без зависимости от Pyodide -""" - -import json -import sys -import os -from typing import Dict, Any - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасное получение значения из словаря""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def console_log(message: str): - """Функция логирования для тестирования""" - print(f"📋 LOG: {message}") - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Мокированная функция для доступа к Pyodide globals""" - # В реальном тесте здесь будет логика доступа к globals - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """Встроенные промпты по умолчанию для fallback""" - builtin_prompts = { - 'optimized': { - 'ru': "ВСТРОЕННЫЙ ПРОМПТ OPTIMIZED РУССКИЙ", - 'en': "BUILTIN PROMPT OPTIMIZED ENGLISH" - }, - 'deep': { - 'ru': "ВСТРОЕННЫЙ ПРОМПТ DEEP РУССКИЙ", - 'en': "BUILTIN PROMPT DEEP ENGLISH" - } - } - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def get_user_prompts_standalone(plugin_settings: Dict[str, Any] = None) -> Dict[str, Any]: - """ - Автономная версия функции get_user_prompts для тестирования логики - без зависимости от Pyodide - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (АВТОНОМНЫЙ ТЕСТ) =====") - - try: - # Диагностика входных данных - console_log(f"🔍 Диагностика входных данных:") - console_log(f" plugin_settings type: {type(plugin_settings)}") - console_log(f" plugin_settings is None: {plugin_settings is None}") - if isinstance(plugin_settings, dict): - console_log(f" plugin_settings keys: {list(plugin_settings.keys())}") - - # Загружаем manifest.json для тестирования - manifest_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer/manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest = json.load(f) - console_log("✅ manifest.json загружен успешно для тестирования") - except Exception as e: - console_log(f"❌ Ошибка загрузки manifest.json: {e}") - manifest = {} - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" custom_prompts_raw: {custom_prompts_raw}") - console_log(f" custom_prompts_raw type: {type(custom_prompts_raw)}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log("❌ manifest не найден") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подсчет загруженных промптов - loaded_prompts = len([p for pt in prompts.values() for p in pt.values() if p and len(p.strip()) > 0]) - total_prompts = len([p for pt in prompts.values() for p in pt.values()]) - console_log(f"📋 Загружено промптов: {loaded_prompts}/{total_prompts} (кастомных: {loaded_prompts})") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - import traceback - stack_trace = traceback.format_exc() - console_log(f" Stack trace: {stack_trace}") - - # Критический fallback - возвращаем пустые промпты - console_log("⚠️ Возвращаем критический fallback с пустыми промптами") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def test_scenario_1_full_custom_prompts(): - """Сценарий 1: Полностью заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 1: Полностью заполненные кастомные промпты") - print("=" * 60) - - plugin_settings = { - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', - 'en': 'CUSTOM PROMPT ENGLISH' - }, - 'deep': { - 'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', - 'en': 'DEEP ANALYSIS ENGLISH' - } - } - } - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем ожидаемые результаты - expected_results = { - 'optimized': {'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', 'en': 'CUSTOM PROMPT ENGLISH'}, - 'deep': {'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', 'en': 'DEEP ANALYSIS ENGLISH'} - } - - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - if result.get(prompt_type, {}).get(lang) != expected_results[prompt_type][lang]: - print(f"❌ Несоответствие в {prompt_type}.{lang}") - success = False - - if success: - print("✅ Сценарий 1 ПРОЙДЕН: Кастомные промпты корректно используются") - else: - print("❌ Сценарий 1 ПРОВАЛЕН: Кастомные промпты не используются правильно") - - return result - -def test_scenario_2_empty_custom_prompts(): - """Сценарий 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 СЦЕНАРИЙ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 60) - - plugin_settings = {} # Пустые настройки пользователя - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используются промпты из manifest - success = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - result_value = result.get(prompt_type, {}).get(lang, '') - if result_value and len(result_value) > 100: # Промпты из manifest должны быть длинными - print(f"✅ {prompt_type}.{lang}: Используется промпт из manifest (длина: {len(result_value)})") - else: - print(f"❌ {prompt_type}.{lang}: Промпт из manifest не найден или слишком короткий") - success = False - - if success: - print("✅ Сценарий 2 ПРОЙДЕН: Fallback на manifest работает корректно") - else: - print("❌ Сценарий 2 ПРОВАЛЕН: Fallback на manifest не работает") - - return result - -def test_scenario_3_broken_manifest(): - """Сценарий 3: Поврежденный manifest.json""" - print("\n🧪 СЦЕНАРИЙ 3: Поврежденный manifest.json") - print("=" * 60) - - # В этом сценарии симулируем поврежденный manifest, модифицируя функцию загрузки - original_load_manifest = None - - def mock_load_manifest(): - return {'invalid': 'data'} # Поврежденный manifest - - plugin_settings = {} - - # Сохраняем оригинальную функцию и заменяем на мок - import mcp_server - if hasattr(mcp_server, 'load_manifest'): - original_load_manifest = mcp_server.load_manifest - mcp_server.load_manifest = mock_load_manifest - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется fallback на встроенные промпты - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_pattern = f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type.upper()}" - result_value = result.get(prompt_type, {}).get(lang, '') - - if expected_pattern in result_value: - print(f"✅ {prompt_type}.{lang}: Используется встроенный промпт") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие встроенного промпта") - success = False - - if success: - print("✅ Сценарий 3 ПРОЙДЕН: Fallback на встроенные промпты работает") - else: - print("❌ Сценарий 3 ПРОВАЛЕН: Fallback на встроенные промпты не работает") - - return result - -def test_scenario_4_partial_custom_prompts(): - """Сценарий 4: Частично заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 4: Частично заполненные кастомные промпты") - print("=" * 60) - - plugin_settings = { - 'prompts': { - 'optimized': { - 'ru': 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ РУССКИЙ' - # en отсутствует - должен использоваться из manifest - } - # deep отсутствует - должен использоваться из manifest - } - } - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется кастомный промпт и manifest для остальных - success = True - - # optimized.ru должен быть кастомным - if result.get('optimized', {}).get('ru') == 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ РУССКИЙ': - print("✅ optimized.ru: Используется кастомный промпт") - else: - print("❌ optimized.ru: Кастомный промпт не используется") - success = False - - # optimized.en должен быть из manifest (длинный промпт) - if result.get('optimized', {}).get('en', '') and len(result['optimized']['en']) > 100: - print("✅ optimized.en: Используется промпт из manifest") - else: - print("❌ optimized.en: Промпт из manifest не используется") - success = False - - if success: - print("✅ Сценарий 4 ПРОЙДЕН: Частичный fallback работает корректно") - else: - print("❌ Сценарий 4 ПРОВАЛЕН: Частичный fallback не работает") - - return result - -def run_standalone_tests(): - """Запуск автономных тестов""" - print("🔍 НАЧИНАЕМ АВТОНОМНОЕ ТЕСТИРОВАНИЕ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 80) - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ТЕСТОВЫХ СЦЕНАРИЕВ") - print("=" * 40) - - results['scenario_1'] = test_scenario_1_full_custom_prompts() - results['scenario_2'] = test_scenario_2_empty_custom_prompts() - results['scenario_3'] = test_scenario_3_broken_manifest() - results['scenario_4'] = test_scenario_4_partial_custom_prompts() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ") - print("=" * 50) - - passed = 0 - total = 0 - - for scenario_name, result in results.items(): - total += 1 - if result is not None: - passed += 1 - print(f"✅ {scenario_name}: ПРОЙДЕН") - else: - print(f"❌ {scenario_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} сценариев пройдено") - - if passed == total: - print("🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Логика механизма промптов работает корректно.") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная диагностика логики.") - - return results - -if __name__ == "__main__": - run_standalone_tests() \ No newline at end of file diff --git a/test_text_truncation_fix.py b/test_text_truncation_fix.py deleted file mode 100644 index 1af5ae39..00000000 --- a/test_text_truncation_fix.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест для проверки исправления обрезки текста в промптах к AI. -Проверяет, что функции _analyze_composition_vs_description и _find_similar_products -используют полные тексты description и composition без обрезки. -""" - -import sys -import os - -def test_text_truncation_fix(): - """Тестируем исправление обрезки текста.""" - - print("=" * 60) - print("🔍 ТЕСТИРОВАНИЕ ИСПРАВЛЕНИЯ ОБРЕЗКИ ТЕКСТА") - print("=" * 60) - - # Имитируем длинные тексты для тестирования - long_description = """ - Это очень длинное описание косметического продукта с подробной информацией о его свойствах. - Крем предназначен для ухода за кожей лица и шеи. Он содержит активные ингредиенты, - которые способствуют увлажнению, питанию и защите кожи. Продукт разработан с использованием - современных технологий и натуральных компонентов. Длинное описание продолжает описывать - все преимущества продукта, его состав, способ применения и ожидаемые результаты использования. - """ * 5 # Умножаем для создания очень длинного текста - - long_composition = """ - Aqua/Water, Glycerin, Caprylic/Capric Triglyceride, Dimethicone, Butylene Glycol, - Cyclopentasiloxane, Dimethicone Crosspolymer, Cyclohexasiloxane, Phenyl Trimethicone, - Polymethylsilsesquioxane, Dimethicone/Vinyl Dimethicone Crosspolymer, Hydroxyethyl Acrylate/Sodium - Acryloyldimethyl Taurate Copolymer, PEG-100 Stearate, Glyceryl Stearate, PEG-8, Polysorbate 60, - Sodium Carbomer, Sodium Laureth Sulfate, Sodium Lauryl Sulfate, Disodium EDTA, Phenonip, - Parfum/Fragrance, Benzyl Alcohol, Methylparaben, Propylparaben, DMDM Hydantoin, Iodopropynyl - Butylcarbamate, Diazolidinyl Urea, Sodium Dehydroacetate, Citric Acid, Triethanolamine, - Tetrasodium EDTA, Hexylene Glycol, Caprylyl Glycol, Ethylhexylglycerin, Phenonip, Germall Plus, - """ * 3 # Умножаем для создания длинного состава - - print(f"📏 Длина тестового описания: {len(long_description)} символов") - print(f"📏 Длина тестового состава: {len(long_composition)} символов") - print() - - # Проверяем, что наши промпты НЕ содержат обрезки - test_prompt_analyze = f""" - Анализ соответствия товара на основе структурированных данных: - - Полный анализ: - Описание: {long_description} - Состав: {long_composition} - - Оцени соответствие по шкале 1-10 и верни JSON: {{"score": число, "reasoning": "объяснение", "confidence": значение_0_1}} - """ - - test_prompt_similar = f""" - Найди 3-5 аналогичных товаров на основе: - Категории: ['косметика', 'уход за кожей'] - Тип продукта: косметика_anti_aging - Состав: {long_composition} - - Проанализируй характеристики аналогичных товаров и верни результаты в формате JSON: - {{"analogs": [ - {{"name": "Название товара", "price_range": "Цена от-до", "key_features": ["особенности"], "similarity_score": 85}}, - ... - ]}} - """ - - # Проверки - print("✅ ПРОВЕРКА ПРОМПТА _analyze_composition_vs_description:") - print(f" - Полное описание передано: {len(long_description)} символов") - print(f" - Полный состав передан: {len(long_composition)} символов") - print(" - Статус: ✅ НЕТ ОБРЕЗКИ") - print() - - print("✅ ПРОВЕРКА ПРОМПТА _find_similar_products:") - print(f" - Полный состав передан: {len(long_composition)} символов") - print(" - Статус: ✅ НЕТ ОБРЕЗКИ") - print() - - # Проверка, что промпты содержат полные тексты - assert long_description in test_prompt_analyze, "❌ Описание обрезано в промпте анализа!" - assert long_composition in test_prompt_analyze, "❌ Состав обрезан в промпте анализа!" - assert long_composition in test_prompt_similar, "❌ Состав обрезан в промпте поиска аналогов!" - - print("🎉 ВСЕ ПРОВЕРКИ ПРОШЛИ УСПЕШНО!") - print("📝 Исправления работают корректно:") - print(" ✅ Полные тексты description и composition передаются в AI") - print(" ✅ JSON парсинг должен работать корректно") - print(" ✅ Обрезка остается только для логов и отображения пользователю") - print() - - print("🔧 РЕКОМЕНДАЦИИ ДЛЯ ТЕСТИРОВАНИЯ:") - print(" 1. Загрузите расширение в Chrome (chrome://extensions/)") - print(" 2. Перейдите на страницу товара Ozon") - print(" 3. Запустите анализатор") - print(" 4. Проверьте консоль на отсутствие ошибок JSON парсинга") - print(" 5. Убедитесь, что анализ работает с длинными текстами") - - print("\n" + "=" * 60) - print("✅ ТЕСТИРОВАНИЕ ЗАВЕРШЕНО УСПЕШНО") - print("=" * 60) - - return True - -if __name__ == "__main__": - try: - test_text_truncation_fix() - print("\n🎉 СКРИПТ ВЫПОЛНЕН УСПЕШНО!") - sys.exit(0) - except Exception as e: - print(f"\n❌ ОШИБКА В ТЕСТИРОВАНИИ: {e}") - sys.exit(1) \ No newline at end of file diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 12077b83..6604ccaa 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@extension/e2e", - "version": "0.5.1363", + "version": "0.5.1368", "description": "E2e tests configuration boilerplate", "private": true, "sideEffects": false, diff --git a/tests/ozon-analyzer-integration/mocks/environment.js b/tests/ozon-analyzer-integration/mocks/environment.js index b6f080a3..c7735a5b 100644 --- a/tests/ozon-analyzer-integration/mocks/environment.js +++ b/tests/ozon-analyzer-integration/mocks/environment.js @@ -175,7 +175,7 @@ async def perform_deep_analysis(input_data): json: () => Promise.resolve({ "name": "Ozon Analyzer", "ai_models": { - "basic_analysis": "gemini-flash", + "basic_analysis": "gemini-flash-lite", "deep_analysis": "gemini-pro" }, "settings": { diff --git a/tests/ozon-analyzer-integration/specs/ai-api.test.js b/tests/ozon-analyzer-integration/specs/ai-api.test.js index 1404ac78..12cf1e95 100644 --- a/tests/ozon-analyzer-integration/specs/ai-api.test.js +++ b/tests/ozon-analyzer-integration/specs/ai-api.test.js @@ -96,7 +96,7 @@ export class AIApiTest { Assert.isDefined(manifest.ai_models.scraping_fallback, 'Должен быть scraping_fallback алиас'); // Проверяем соответствие реальным названиям моделей - const expectedModels = ['gemini-flash', 'gemini-pro']; + const expectedModels = ['gemini-flash-lite', 'gemini-pro']; const configuredModels = Object.values(manifest.ai_models); for (const model of configuredModels) { From 1fa77ee7dd2e42425cd24de00cd2c3350dd2f218 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Mon, 13 Oct 2025 19:08:28 +0500 Subject: [PATCH 02/15] =?UTF-8?q?=D0=B4=D0=BE=20=D1=80=D0=B5=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- import-best-practices/README.en.md | 28 ---------------------------- import-best-practices/README.md | 28 ---------------------------- 2 files changed, 56 deletions(-) delete mode 100644 import-best-practices/README.en.md delete mode 100644 import-best-practices/README.md diff --git a/import-best-practices/README.en.md b/import-best-practices/README.en.md deleted file mode 100644 index 14ddd82b..00000000 --- a/import-best-practices/README.en.md +++ /dev/null @@ -1,28 +0,0 @@ -# import-best-practices - -This directory is intended for temporary import of best practices, organizational knowledge, and AI instructions from other (more advanced) projects. - -## Purpose -- Isolate imported AI materials from the current ones, preventing conflicts and accidental overwrites. -- Convenient for analysis, comparison, and adoption of new best practices. -- Safe and controlled process for integrating the best solutions. - -## Structure -Copy the following folders from another project into this directory: -- `docs/for-ai-best-practices` — AI best practices (EN, for AI only) -- `memory-bank` — organizational knowledge and rules (EN, for AI only) -- `.cursor/rules` — (optional) AI rules, if you need to transfer them -- (optional) other directories with AI metadata, if used in your architecture - -## How to use -1. Copy the required folders from another project into `import-best-practices`. -2. Ask your AI assistant: - ``` - Analyze the contents of import-best-practices and integrate/supplement the best practices, standards, and organizational knowledge into the current project, avoiding duplication and conflicts. - ``` -3. After integration, delete the `import-best-practices` directory (or archive it for history). - -## Important -- Do not use this directory for permanent storage — only for temporary import and analysis. -- All materials inside must be in English and are intended for AI assistants. -- For user instructions, see the `docs/` folder. \ No newline at end of file diff --git a/import-best-practices/README.md b/import-best-practices/README.md deleted file mode 100644 index 59fd9ea5..00000000 --- a/import-best-practices/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# import-best-practices - -Этот каталог предназначен для временного импорта best practices, организационных знаний и AI-инструкций из других (более передовых) проектов. - -## Назначение -- Изоляция импортируемых AI-материалов от текущих, предотвращение конфликтов и случайного перезаписывания. -- Удобство для анализа, сравнения и внедрения новых best practices. -- Безопасный и контролируемый процесс интеграции лучших наработок. - -## Структура -В этот каталог целиком копируются из другого проекта: -- `docs/for-ai-best-practices` — AI best practices (EN, только для AI) -- `memory-bank` — организационные знания и правила (EN, только для AI) -- `.cursor/rules` — (опционально) AI-правила, если требуется их перенос -- (опционально) другие каталоги с AI-метаданными, если используются в архитектуре - -## Как использовать -1. Скопируйте нужные папки из другого проекта в `import-best-practices`. -2. Дайте AI-ассистенту команду: - ``` - Проанализируй содержимое import-best-practices и внедри/дополни лучшие практики, стандарты и организационные знания в текущий проект, избегая дублирования и конфликтов. - ``` -3. После внедрения — удалите каталог `import-best-practices` (или архивируйте для истории). - -## Важно -- Не используйте этот каталог для постоянного хранения — только для временного импорта и анализа. -- Все материалы внутри — на английском языке и предназначены для AI-ассистентов. -- Для пользовательских инструкций используйте папку `docs/`. \ No newline at end of file From 8b8b1c09f90aa29674d38b09534692edaf569d23 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Tue, 14 Oct 2025 01:13:51 +0500 Subject: [PATCH 03/15] =?UTF-8?q?=D0=B2=20manifest.json=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B5=D0=B9?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../curl-templates/basic_analysis-en.curl | 14 ++++++++++++ .../curl-templates/basic_analysis-ru.curl | 14 ++++++++++++ .../curl-templates/deep_analysis-en.curl | 14 ++++++++++++ .../curl-templates/deep_analysis-ru.curl | 14 ++++++++++++ .../plugins/ozon-analyzer/manifest.json | 22 +++++++++++++++++-- .../curl-templates/basic-en.curl | 13 +++++++++++ .../curl-templates/basic-ru.curl | 13 +++++++++++ .../ozon-analyzer/curl-templates/deep-en.curl | 13 +++++++++++ .../ozon-analyzer/curl-templates/deep-ru.curl | 13 +++++++++++ 9 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-en.curl create mode 100644 chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-ru.curl create mode 100644 chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-en.curl create mode 100644 chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-ru.curl create mode 100644 public/plugins/ozon-analyzer/curl-templates/basic-en.curl create mode 100644 public/plugins/ozon-analyzer/curl-templates/basic-ru.curl create mode 100644 public/plugins/ozon-analyzer/curl-templates/deep-en.curl create mode 100644 public/plugins/ozon-analyzer/curl-templates/deep-ru.curl diff --git a/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-en.curl b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-en.curl new file mode 100644 index 00000000..e9dac2a6 --- /dev/null +++ b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-en.curl @@ -0,0 +1,14 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1/models/gemini-flash-lite-latest:generateContent" + -H "x-goog-api-key: $GEMINI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt" + } + ] + } + ] + }' \ No newline at end of file diff --git a/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-ru.curl b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-ru.curl new file mode 100644 index 00000000..e9dac2a6 --- /dev/null +++ b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-ru.curl @@ -0,0 +1,14 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1/models/gemini-flash-lite-latest:generateContent" + -H "x-goog-api-key: $GEMINI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt" + } + ] + } + ] + }' \ No newline at end of file diff --git a/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-en.curl b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-en.curl new file mode 100644 index 00000000..2f858148 --- /dev/null +++ b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-en.curl @@ -0,0 +1,14 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1/models/gemini-2.5-pro:generateContent" + -H "x-goog-api-key: $GEMINI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt" + } + ] + } + ] + }' \ No newline at end of file diff --git a/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-ru.curl b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-ru.curl new file mode 100644 index 00000000..2f858148 --- /dev/null +++ b/chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-ru.curl @@ -0,0 +1,14 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1/models/gemini-2.5-pro:generateContent" + -H "x-goog-api-key: $GEMINI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt" + } + ] + } + ] + }' \ No newline at end of file diff --git a/chrome-extension/public/plugins/ozon-analyzer/manifest.json b/chrome-extension/public/plugins/ozon-analyzer/manifest.json index c3c39b28..e50c6c51 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/manifest.json +++ b/chrome-extension/public/plugins/ozon-analyzer/manifest.json @@ -105,6 +105,11 @@ "description": { "ru": "Промпт для базового анализа соответствия описания и состава", "en": "Prompt for basic compliance analysis" + }, + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-ru.curl" + } } }, "en": { @@ -117,6 +122,11 @@ "description": { "ru": "Промпт для базового анализа соответствия описания и состава", "en": "Prompt for basic compliance analysis" + }, + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-en.curl" + } } } }, @@ -128,7 +138,11 @@ "ru": "Промпт глубокого анализа (русский)", "en": "Deep Analysis Prompt (Russian)" }, - "default_LLM": "gemini-2.5-pro:generateContent" + "LLM": { + "default": { + "curl_file": "curl-templates/deep_analysis-ru.curl" + } + } }, "en": { "type": "text", @@ -137,7 +151,11 @@ "ru": "Промпт глубокого анализа (английский)", "en": "Deep Analysis Prompt (English)" }, - "default_LLM": "gemini-2.5-pro:generateContent" + "LLM": { + "default": { + "curl_file": "curl-templates/deep_analysis-en.curl" + } + } } } } diff --git a/public/plugins/ozon-analyzer/curl-templates/basic-en.curl b/public/plugins/ozon-analyzer/curl-templates/basic-en.curl new file mode 100644 index 00000000..f27625b1 --- /dev/null +++ b/public/plugins/ozon-analyzer/curl-templates/basic-en.curl @@ -0,0 +1,13 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=$GEMINI_API_KEY" \ +-H "Content-Type: application/json" \ +-d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt_basic_analysis_en" + } + ] + } + ] +}' \ No newline at end of file diff --git a/public/plugins/ozon-analyzer/curl-templates/basic-ru.curl b/public/plugins/ozon-analyzer/curl-templates/basic-ru.curl new file mode 100644 index 00000000..492b51b9 --- /dev/null +++ b/public/plugins/ozon-analyzer/curl-templates/basic-ru.curl @@ -0,0 +1,13 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=$GEMINI_API_KEY" \ +-H "Content-Type: application/json" \ +-d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt_basic_analysis_ru" + } + ] + } + ] +}' \ No newline at end of file diff --git a/public/plugins/ozon-analyzer/curl-templates/deep-en.curl b/public/plugins/ozon-analyzer/curl-templates/deep-en.curl new file mode 100644 index 00000000..baa94139 --- /dev/null +++ b/public/plugins/ozon-analyzer/curl-templates/deep-en.curl @@ -0,0 +1,13 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key=$GEMINI_API_KEY" \ +-H "Content-Type: application/json" \ +-d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt_deep_analysis_en" + } + ] + } + ] +}' \ No newline at end of file diff --git a/public/plugins/ozon-analyzer/curl-templates/deep-ru.curl b/public/plugins/ozon-analyzer/curl-templates/deep-ru.curl new file mode 100644 index 00000000..743f6647 --- /dev/null +++ b/public/plugins/ozon-analyzer/curl-templates/deep-ru.curl @@ -0,0 +1,13 @@ +curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key=$GEMINI_API_KEY" \ +-H "Content-Type: application/json" \ +-d '{ + "contents": [ + { + "parts": [ + { + "text": "$prompt_deep_analysis_ru" + } + ] + } + ] +}' \ No newline at end of file From b38966b288cc727c20d1898b1ec3b9b860082168 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Tue, 14 Oct 2025 05:07:36 +0500 Subject: [PATCH 04/15] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=BE=20=D1=81=D0=BE=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B5=D0=B9=D1=80=D0=BE=D1=81=D0=B5=D1=82=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- chrome-extension/public/pyodide/package.json | 2 +- .../side-panel/assets/index-BDNBm95e.js | 50 ----- .../side-panel/assets/index-BDNBm95e.js.map | 1 - .../side-panel/assets/index-C-Ob5p6n.js | 50 +++++ .../side-panel/assets/index-C-Ob5p6n.js.map | 1 + chrome-extension/public/side-panel/index.html | 2 +- .../src/background/ai-api-client.ts | 91 +++++++++ chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/options/src/components/LLMSelector.tsx | 137 +++++++++++++ .../options/src/components/PluginDetails.tsx | 62 +++++- pages/options/src/components/index.ts | 3 +- pages/options/src/hooks/usePluginSettings.ts | 188 ++++++++++++++++++ pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- tests/e2e/package.json | 2 +- 37 files changed, 559 insertions(+), 82 deletions(-) delete mode 100644 chrome-extension/public/side-panel/assets/index-BDNBm95e.js delete mode 100644 chrome-extension/public/side-panel/assets/index-BDNBm95e.js.map create mode 100644 chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js create mode 100644 chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js.map create mode 100644 pages/options/src/components/LLMSelector.tsx create mode 100644 pages/options/src/hooks/usePluginSettings.ts diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 87b78aa6..44b16913 100644 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1350", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1350", + "version": "1.0.1355", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1355", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index e4329466..b447c604 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index 92c25d0e..37ca6da7 100644 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1375", + "version": "0.27.1380", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-BDNBm95e.js b/chrome-extension/public/side-panel/assets/index-BDNBm95e.js deleted file mode 100644 index d3ab39ed..00000000 --- a/chrome-extension/public/side-panel/assets/index-BDNBm95e.js +++ /dev/null @@ -1,50 +0,0 @@ -(function(){const m=document.createElement("link").relList;if(m&&m.supports&&m.supports("modulepreload"))return;for(const A of document.querySelectorAll('link[rel="modulepreload"]'))c(A);new MutationObserver(A=>{for(const D of A)if(D.type==="childList")for(const z of D.addedNodes)z.tagName==="LINK"&&z.rel==="modulepreload"&&c(z)}).observe(document,{childList:!0,subtree:!0});function h(A){const D={};return A.integrity&&(D.integrity=A.integrity),A.referrerPolicy&&(D.referrerPolicy=A.referrerPolicy),A.crossOrigin==="use-credentials"?D.credentials="include":A.crossOrigin==="anonymous"?D.credentials="omit":D.credentials="same-origin",D}function c(A){if(A.ep)return;A.ep=!0;const D=h(A);fetch(A.href,D)}})();var nu=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Rh(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var xo={exports:{}},Ln={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var zd;function Uh(){if(zd)return Ln;zd=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.fragment");function h(c,A,D){var z=null;if(D!==void 0&&(z=""+D),A.key!==void 0&&(z=""+A.key),"key"in A){D={};for(var I in A)I!=="key"&&(D[I]=A[I])}else D=A;return A=D.ref,{$$typeof:o,type:c,key:z,ref:A!==void 0?A:null,props:D}}return Ln.Fragment=m,Ln.jsx=h,Ln.jsxs=h,Ln}var Rd;function Nh(){return Rd||(Rd=1,xo.exports=Uh()),xo.exports}var y=Nh();const jh=({isDraftSaved:o,isDraftLoading:m,draftError:h,messageLength:c,minLength:A,maxLength:D})=>{const z=()=>m?y.jsx("div",{className:"draft-status-loader"}):h?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):o?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cm?"Загрузка черновика...":h||(o?"Черновик сохранен":c>0&&c=A&&c<=D?"Черновик будет сохранен автоматически":c>D?"Превышен лимит символов":""),H=()=>m?"draft-status loading":h?"draft-status error":o?"draft-status saved":c>0&&c=A&&c<=D?"draft-status ready":c>D?"draft-status error":"draft-status";return c===0&&!m&&!h?null:y.jsxs("div",{className:H(),children:[z(),y.jsx("span",{className:"draft-status-text",children:I()})]})};var _o={exports:{}},be={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ud;function wh(){if(Ud)return be;Ud=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),A=Symbol.for("react.profiler"),D=Symbol.for("react.consumer"),z=Symbol.for("react.context"),I=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),Z=Symbol.iterator;function $(f){return f===null||typeof f!="object"?null:(f=Z&&f[Z]||f["@@iterator"],typeof f=="function"?f:null)}var ue={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ie=Object.assign,oe={};function k(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}k.prototype.isReactComponent={},k.prototype.setState=function(f,R){if(typeof f!="object"&&typeof f!="function"&&f!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,f,R,"setState")},k.prototype.forceUpdate=function(f){this.updater.enqueueForceUpdate(this,f,"forceUpdate")};function K(){}K.prototype=k.prototype;function te(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}var V=te.prototype=new K;V.constructor=te,ie(V,k.prototype),V.isPureReactComponent=!0;var de=Array.isArray,C={H:null,A:null,T:null,S:null,V:null},F=Object.prototype.hasOwnProperty;function J(f,R,W,L,ee,me){return W=me.ref,{$$typeof:o,type:f,key:R,ref:W!==void 0?W:null,props:me}}function Y(f,R){return J(f.type,R,void 0,void 0,void 0,f.props)}function ye(f){return typeof f=="object"&&f!==null&&f.$$typeof===o}function Ae(f){var R={"=":"=0",":":"=2"};return"$"+f.replace(/[=:]/g,function(W){return R[W]})}var pe=/\/+/g;function Te(f,R){return typeof f=="object"&&f!==null&&f.key!=null?Ae(""+f.key):R.toString(36)}function ut(){}function Ke(f){switch(f.status){case"fulfilled":return f.value;case"rejected":throw f.reason;default:switch(typeof f.status=="string"?f.then(ut,ut):(f.status="pending",f.then(function(R){f.status==="pending"&&(f.status="fulfilled",f.value=R)},function(R){f.status==="pending"&&(f.status="rejected",f.reason=R)})),f.status){case"fulfilled":return f.value;case"rejected":throw f.reason}}throw f}function we(f,R,W,L,ee){var me=typeof f;(me==="undefined"||me==="boolean")&&(f=null);var re=!1;if(f===null)re=!0;else switch(me){case"bigint":case"string":case"number":re=!0;break;case"object":switch(f.$$typeof){case o:case m:re=!0;break;case O:return re=f._init,we(re(f._payload),R,W,L,ee)}}if(re)return ee=ee(f),re=L===""?"."+Te(f,0):L,de(ee)?(W="",re!=null&&(W=re.replace(pe,"$&/")+"/"),we(ee,R,W,"",function(st){return st})):ee!=null&&(ye(ee)&&(ee=Y(ee,W+(ee.key==null||f&&f.key===ee.key?"":(""+ee.key).replace(pe,"$&/")+"/")+re)),R.push(ee)),1;re=0;var ze=L===""?".":L+":";if(de(f))for(var Ne=0;Ne>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},Gh="Platform Settings",Bh="Available plugins",Ph="AI API Keys",qh="Free",Yh="Custom API keys",Lh="+ Add new key",Xh="Enter API key",Qh="Enter key name",Vh="Save all keys",Kh="Test connections",Zh="General Settings",kh="Automatic plugin updates",Jh="Show notifications",$h="Interface theme:",Wh="Light",Fh="Dark",Ih="System",ey="Security",ty="Verify plugin signatures",ly="Isolated execution mode",ay="Performance",ny="Maximum number of active plugins:",iy="Cache plugin data",uy="Plugin Details",sy={options:Hh,options_settings_title:Gh,options_plugins_title:Bh,options_settings_aiKeys_title:Ph,options_settings_aiKeys_badges_free:qh,options_settings_aiKeys_customKeys_title:Yh,options_settings_aiKeys_customKeys_addButton:Lh,options_settings_aiKeys_customKeys_keyPlaceholder:Xh,options_settings_aiKeys_customKeys_namePlaceholder:Qh,options_settings_aiKeys_actions_save:Vh,options_settings_aiKeys_actions_test:Kh,options_settings_general_title:Zh,options_settings_general_autoUpdate:kh,options_settings_general_showNotifications:Jh,options_settings_general_theme:$h,options_settings_general_theme_light:Wh,options_settings_general_theme_dark:Fh,options_settings_general_theme_system:Ih,options_settings_security_title:ey,options_settings_security_checkSignatures:ty,options_settings_security_isolatedMode:ly,options_settings_performance_title:ay,options_settings_performance_maxPlugins:ny,options_settings_performance_cacheData:iy,options_plugins_details_title:uy},oy={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",basic_analysis:"Базовый",deep_analysis:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},cy="Настройки Платформы",ry="Доступные плагины",fy="API Ключи нейросетей",dy="Бесплатный",gy="Пользовательские API ключи",my="+ Добавить новый ключ",hy="Введите API ключ",yy="Введите название ключа",py="Сохранить все ключи",vy="Тестировать подключения",by="Общие настройки",Sy="Автоматическое обновление плагинов",xy="Показывать уведомления",_y="Тема интерфейса:",Ey="Светлая",Ay="Тёмная",Ty="Системная",Cy="Безопасность",Oy="Проверять подписи плагинов",My="Изолированный режим выполнения",Dy="Производительность",zy="Максимальное количество активных плагинов:",Ry="Кэширование данных плагинов",Uy="Детали плагина",Ny={options:oy,options_settings_title:cy,options_plugins_title:ry,options_settings_aiKeys_title:fy,options_settings_aiKeys_badges_free:dy,options_settings_aiKeys_customKeys_title:gy,options_settings_aiKeys_customKeys_addButton:my,options_settings_aiKeys_customKeys_keyPlaceholder:hy,options_settings_aiKeys_customKeys_namePlaceholder:yy,options_settings_aiKeys_actions_save:py,options_settings_aiKeys_actions_test:vy,options_settings_general_title:by,options_settings_general_autoUpdate:Sy,options_settings_general_showNotifications:xy,options_settings_general_theme:_y,options_settings_general_theme_light:Ey,options_settings_general_theme_dark:Ay,options_settings_general_theme_system:Ty,options_settings_security_title:Cy,options_settings_security_checkSignatures:Oy,options_settings_security_isolatedMode:My,options_settings_performance_title:Dy,options_settings_performance_maxPlugins:zy,options_settings_performance_cacheData:Ry,options_plugins_details_title:Uy},jd={en:sy,ru:Ny},jy=(o="en")=>({t:Q.useMemo(()=>{const h=jd[o]||{},c=(A,D)=>D.split(".").reduce((z,I)=>z&&z[I]?z[I]:void 0,A);return A=>{const D=c(h,A);return D!==void 0?D:A}},[o,jd]),locale:o}),Eo=({checked:o,onChange:m,disabled:h,label:c,iconOn:A,iconOff:D})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:h?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:o,disabled:h,onChange:z=>m(z.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:o?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:o?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:o?A:D})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),wy=({error:o,resetError:m})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),o&&y.jsx("pre",{children:o.message}),m&&y.jsx("button",{onClick:m,children:"Сбросить"})]}),Hy=({children:o})=>{const[m,h]=ou.useState(null),c=ou.useCallback(()=>h(null),[]);if(m)return y.jsx(wy,{error:m,resetError:c});try{return y.jsx(y.Fragment,{children:o})}catch(A){return h(A),null}},Gy=(...o)=>o.filter(Boolean).join(" "),By=({value:o,manifest:m,disabled:h,onSave:c,locale:A,t:D})=>{const[z,I]=Q.useState("basic_analysis"),[H,b]=Q.useState("ru"),[O,Z]=Q.useState(""),$=()=>{try{const k=m?.options?.prompts;if(!k)return"";const V=((k[z]||{})[H]||{}).default||"";return typeof V=="object"?JSON.stringify(V,null,2):V}catch{return""}},ue=()=>{try{const te=((o||{})[z]||{})[H];return typeof te=="string"?te:te==null?"":JSON.stringify(te,null,2)}catch{return""}};Q.useEffect(()=>{Z(ue())},[z,H,o]);const ie=()=>{Z($())},oe=()=>{try{const k={...o};k[z]||(k[z]={ru:"",en:""}),k[z][H]=O,c(k)}catch(k){console.error("Failed to save custom prompt:",k)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:D("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:D("options.plugins.prompts.type")}),y.jsxs("select",{value:z,onChange:k=>I(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"basic_analysis",children:D("options.plugins.prompts.basic_analysis")}),y.jsx("option",{value:"deep_analysis",children:D("options.plugins.prompts.deep_analysis")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:D("options.plugins.prompts.language")}),y.jsxs("select",{value:H,onChange:k=>b(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:D("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:D("options.plugins.prompts.english")})]})]})]}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:D("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:$(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:ie,disabled:h,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:D("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:D("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:O,onChange:k=>Z(k.target.value),disabled:h,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:oe,disabled:h,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:D("options.plugins.prompts.save")})})]})})},Py=o=>{const{selectedPlugin:m,locale:h="en",onUpdateSetting:c}=o,{t:A}=jy(h),[D,z]=Q.useState(null),[I,H]=Q.useState(null),b=C=>C?typeof C=="string"?C:C[h]||C.ru||C.en||"":"";if(Q.useEffect(()=>{m?.manifest?.options&&$()},[m?.id]),!m||typeof m!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:A("options_plugins_details_title")}),y.jsx("p",{children:A("options.plugins.details.selectPlugin")})]});const O=m.settings||{enabled:!0,autorun:!1},Z=m.manifest?.host_permissions||[],$=async()=>{if(!(!m||!m.manifest?.options||I!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const F=Object.keys(m.manifest.options).map(ye=>`${m.id}_${ye}`),J=await chrome.storage.local.get(F),Y={};Object.entries(J).forEach(([ye,Ae])=>{const pe=ye.replace(`${m.id}_`,"");Ae!==void 0&&(Y[pe]=Ae)}),H(Y)}}catch(C){console.warn("Failed to load custom settings from chrome.storage.local:",C)}},ue=async(C,F)=>{if(m)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const J=`${m.id}_${C}`;await chrome.storage.local.set({[J]:F}),H(Y=>({...Y,[C]:F})),C==="prompts"&&await ie()}else console.warn("chrome.storage.local is not available")}catch(J){throw console.error(`Failed to save setting ${C} to chrome.storage.local:`,J),J}},ie=async()=>{if(m)try{const C=`${m.id}_prompts`,F=await chrome.storage.local.get([C]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${m.id}`),console.log(` Storage key: ${C}`),console.log(" Сохраненные промпты:",F[C]),F[C]){const J=F[C];console.log(" Структура промптов:"),console.log(` - basic_analysis.ru: ${J.basic_analysis?.ru?"✓":"✗"} (${J.basic_analysis?.ru?.length||0} символов)`),console.log(` - basic_analysis.en: ${J.basic_analysis?.en?"✓":"✗"} (${J.basic_analysis?.en?.length||0} символов)`),console.log(` - deep_analysis.ru: ${J.deep_analysis?.ru?"✓":"✗"} (${J.deep_analysis?.ru?.length||0} символов)`),console.log(` - deep_analysis.en: ${J.deep_analysis?.en?"✓":"✗"} (${J.deep_analysis?.en?.length||0} символов)`)}}catch(C){console.error("Ошибка диагностики промптов:",C)}},oe=(C,F)=>{if(I&&I[C]!==void 0)return I[C];if(C==="prompts"&&typeof F=="object"&&F!==null){const J=F,Y={basic_analysis:{ru:{},en:{}},deep_analysis:{ru:{},en:{}}};return J.basic_analysis?.ru?.default&&(Y.basic_analysis.ru=J.basic_analysis.ru.default),J.basic_analysis?.en?.default&&(Y.basic_analysis.en=J.basic_analysis.en.default),J.deep_analysis?.ru?.default&&(Y.deep_analysis.ru=J.deep_analysis.ru.default),J.deep_analysis?.en?.default&&(Y.deep_analysis.en=J.deep_analysis.en.default),Y}return F},k=(C,F)=>{const J=oe(C,F.default),Y=D===C||!(O.enabled??!0),ye=b(F.label),Ae=b(F.description);if(C==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(By,{value:J,manifest:m.manifest,disabled:Y,onSave:pe=>K(C,pe),locale:h,t:A})},C);if(F.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(Eo,{checked:J,disabled:Y,onChange:pe=>K(C,pe),label:y.jsxs(y.Fragment,{children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"})]})})},C);if(F.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"}),y.jsx("select",{id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:F.values?.map(pe=>y.jsx("option",{value:pe,children:b(F.labels?.[pe])||pe},pe))})]})},C);if(F.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"text",id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C);if(F.type==="number"){const pe=Te=>{const ut=parseFloat(Te.target.value);if(isNaN(ut))return;let Ke=ut;F.min!==void 0&&KeF.max&&(Ke=F.max),K(C,Ke)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"number",id:C,name:C,value:J,disabled:Y,onChange:pe,min:F.min,max:F.max,step:F.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C)}return null},K=async(C,F)=>{if(!m)return;const J=m.manifest?.options;if(J&&J[C]){I===null&&await $();try{z(C),await ue(C,F)}catch(Y){console.error(`Failed to update custom setting ${C}:`,Y)}finally{z(null)}}else if(c)try{z(C),await c(m.id,C,F)}catch(Y){console.error(`Failed to update setting ${C}:`,Y)}finally{z(null)}},V=Object.entries(m.manifest?.options??{}).map(([C,F])=>k(C,F)).filter(C=>C!==null),de=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(Eo,{checked:O.enabled??!0,disabled:D==="enabled",onChange:C=>K("enabled",C),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(Eo,{checked:O.autorun??!1,disabled:D==="autorun"||!(O.enabled??!0),onChange:C=>K("autorun",C),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(Hy,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:m.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",m.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:Gy("status-badge",O.enabled?"status-active":"status-inactive"),children:O.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",m.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",m.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:m.description})]}),Z.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:Z.map((C,F)=>y.jsx("li",{children:C},F))})]}),Array.isArray(m.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(m.manifest?.permissions??[]).map((C,F)=>y.jsx("li",{children:C},F))})]}):null,y.jsx(de,{}),m.manifest?.options&&Object.keys(m.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),V]}):null]})]})})},qy=({plugin:o,onUpdateSetting:m})=>{const h=m?async(A,D,z)=>{try{return await m(A,D,z),!0}catch(I){return console.error(`Failed to update setting ${D}:`,I),!1}}:void 0,c={selectedPlugin:{...o,description:o.description||"Описание не указано",icon:o.icon||"",manifest:o.manifest||{}},locale:"ru",onUpdateSetting:h};return y.jsx(Py,{...c})},wd=function(o){if(!o)return"unknown-page";try{const m=new URL(o);return m.search="",m.hash="",m.toString()}catch{return o}},Yy=({pluginId:o,pageKey:m,debounceMs:h=1e3})=>{const[c,A]=Q.useState(""),[D,z]=Q.useState(!1),[I,H]=Q.useState(!1),[b,O]=Q.useState(null),[Z,$]=Q.useState(""),ue=Q.useRef(null),ie=Q.useRef(""),oe=Q.useCallback(async V=>{if(V!==ie.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:o,pageKey:m,text:V});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:V}),ie.current=V,z(!0),O(null),$(V),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:o,pageKey:m,text:V})}catch(de){console.error("[useLazyChatSync] Error saving draft:",de),O("Ошибка сохранения черновика"),z(!1)}}},[o,m]),k=Q.useCallback(async()=>{H(!0),O(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:o,pageKey:m});try{const V=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m});V?.draftText?(A(V.draftText),ie.current=V.draftText,z(!0),$(V.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:o,pageKey:m,draft:V.draftText})):(A(""),ie.current="",z(!1),$(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:o,pageKey:m}))}catch(V){console.error("[useLazyChatSync] Error loading draft:",V),O("Ошибка загрузки черновика")}finally{H(!1)}},[o,m]),K=Q.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:o,pageKey:m});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:""}),ie.current="",z(!1),O(null),$(""),A(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:o,pageKey:m})}catch(V){console.error("[useLazyChatSync] Error clearing draft:",V),O("Ошибка очистки черновика")}},[o,m]),te=Q.useCallback(V=>{A(V),ue.current&&clearTimeout(ue.current),V.length===0?K():ue.current=setTimeout(()=>{oe(V)},h),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:o,pageKey:m,text:V})},[h,oe,K,o,m]);return Q.useEffect(()=>()=>{ue.current&&clearTimeout(ue.current)},[]),Q.useEffect(()=>{k()},[k]),Q.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",m),z(!1),H(!1),O(null),ie.current="",ue.current&&(clearTimeout(ue.current),ue.current=null),k()},[m,k]),{message:c,setMessage:te,isDraftSaved:D,isDraftLoading:I,draftError:b,loadDraft:k,clearDraft:K,draftText:Z}};var su={exports:{}},Ly=su.exports,Hd;function Xy(){return Hd||(Hd=1,(function(o,m){(function(h,c){c()})(Ly,function(){function h(b,O){return typeof O>"u"?O={autoBom:!1}:typeof O!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),O={autoBom:!O}),O.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(b.type)?new Blob(["\uFEFF",b],{type:b.type}):b}function c(b,O,Z){var $=new XMLHttpRequest;$.open("GET",b),$.responseType="blob",$.onload=function(){H($.response,O,Z)},$.onerror=function(){console.error("could not download file")},$.send()}function A(b){var O=new XMLHttpRequest;O.open("HEAD",b,!1);try{O.send()}catch{}return 200<=O.status&&299>=O.status}function D(b){try{b.dispatchEvent(new MouseEvent("click"))}catch{var O=document.createEvent("MouseEvents");O.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),b.dispatchEvent(O)}}var z=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof nu=="object"&&nu.global===nu?nu:void 0,I=z.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),H=z.saveAs||(typeof window!="object"||window!==z?function(){}:"download"in HTMLAnchorElement.prototype&&!I?function(b,O,Z){var $=z.URL||z.webkitURL,ue=document.createElement("a");O=O||b.name||"download",ue.download=O,ue.rel="noopener",typeof b=="string"?(ue.href=b,ue.origin===location.origin?D(ue):A(ue.href)?c(b,O,Z):D(ue,ue.target="_blank")):(ue.href=$.createObjectURL(b),setTimeout(function(){$.revokeObjectURL(ue.href)},4e4),setTimeout(function(){D(ue)},0))}:"msSaveOrOpenBlob"in navigator?function(b,O,Z){if(O=O||b.name||"download",typeof b!="string")navigator.msSaveOrOpenBlob(h(b,Z),O);else if(A(b))c(b,O,Z);else{var $=document.createElement("a");$.href=b,$.target="_blank",setTimeout(function(){D($)})}}:function(b,O,Z,$){if($=$||open("","_blank"),$&&($.document.title=$.document.body.innerText="downloading..."),typeof b=="string")return c(b,O,Z);var ue=b.type==="application/octet-stream",ie=/constructor/i.test(z.HTMLElement)||z.safari,oe=/CriOS\/[\d]+/.test(navigator.userAgent);if((oe||ue&&ie||I)&&typeof FileReader<"u"){var k=new FileReader;k.onloadend=function(){var V=k.result;V=oe?V:V.replace(/^data:[^;]*;/,"data:attachment/file;"),$?$.location.href=V:location=V,$=null},k.readAsDataURL(b)}else{var K=z.URL||z.webkitURL,te=K.createObjectURL(b);$?$.location=te:location.href=te,$=null,setTimeout(function(){K.revokeObjectURL(te)},4e4)}});z.saveAs=H.saveAs=H,o.exports=H})})(su)),su.exports}var Qy=Xy(),Ya;(function(o){o.Local="local",o.Sync="sync",o.Managed="managed",o.Session="session"})(Ya||(Ya={}));var jo;(function(o){o.ExtensionPagesOnly="TRUSTED_CONTEXTS",o.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(jo||(jo={}));const qa=globalThis.chrome,Gd=async(o,m)=>{const h=A=>typeof A=="function",c=A=>A instanceof Promise;return h(o)?(c(o),o(m)):o};let Bd=!1;const Pd=o=>{if(qa&&!qa.storage[o])throw new Error(`"storage" permission in manifest.ts: "storage ${o}" isn't defined`)},eg=(o,m,h)=>{let c=null,A=!1,D=[];const z=h?.storageEnum??Ya.Local,I=h?.liveUpdate??!1,H=h?.serialization?.serialize??(k=>k),b=h?.serialization?.deserialize??(k=>k);Bd===!1&&z===Ya.Session&&h?.sessionAccessForContentScripts===!0&&(Pd(z),qa?.storage[z].setAccessLevel({accessLevel:jo.ExtensionPagesAndContentScripts}).catch(k=>{console.error(k),console.error("Please call .setAccessLevel() into different context, like a background script.")}),Bd=!0);const O=async()=>{Pd(z);const k=await qa?.storage[z].get([o]);return k?b(k[o])??m:m},Z=async k=>{A||(c=await O()),c=await Gd(k,c),await qa?.storage[z].set({[o]:H(c)}),ie()},$=k=>(D=[...D,k],()=>{D=D.filter(K=>K!==k)}),ue=()=>c,ie=()=>{D.forEach(k=>k())},oe=async k=>{if(k[o]===void 0)return;const K=b(k[o].newValue);c!==K&&(c=await Gd(K,c),ie())};return O().then(k=>{c=k,A=!0,ie()}),I&&qa?.storage[z].onChanged.addListener(oe),{get:O,set:Z,getSnapshot:ue,subscribe:$}},qd=eg("theme-storage-key",{theme:"system",isLight:tg()},{storageEnum:Ya.Local,liveUpdate:!0});function tg(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Ao={...qd,toggle:async()=>{await qd.set(o=>{let m;switch(o.theme){case"light":m="dark";break;case"dark":m="system";break;case"system":default:m="light";break}const h=m==="system"?tg():m==="light";return{theme:m,isLight:h}})}},To=eg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Ya.Local,liveUpdate:!0}),Yd={...To,setAlignment:async o=>{await To.set({alignment:o})},getAlignment:async()=>(await To.get()).alignment},Vy=({plugin:o,currentView:m,isRunning:h,isPaused:c,currentTabUrl:A,onStart:D,onPause:z,onStop:I,onClose:H})=>{const[b,O]=Q.useState("chat"),[Z,$]=Q.useState(wd(A)),[ue,ie]=Q.useState("left");Q.useEffect(()=>{const M=wd(A);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:Z,newPageKey:M,currentTabUrl:A,timestamp:new Date().toISOString()}),$(M)},[A]),Q.useEffect(()=>{const M=async()=>{const p=await Yd.getAlignment();ie(p)};return M(),Yd.subscribe(()=>{M()})},[]);const{message:oe,setMessage:k,isDraftSaved:K,isDraftLoading:te,draftError:V,loadDraft:de,clearDraft:C,draftText:F}=Yy({pluginId:o.id,pageKey:Z,debounceMs:1e3}),[J,Y]=Q.useState([]),[ye,Ae]=Q.useState(!1),[pe,Te]=Q.useState(null),[ut,Ke]=Q.useState(60),[we,T]=Q.useState(!1),X=Q.useRef(null),q=Q.useRef(null);Q.useEffect(()=>{},[h]);const Ce=()=>{D()},f=o.name||(typeof o.manifest?.name=="string"?o.manifest.name:"")||o.id,R=o.id,W=Q.useCallback(async M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};try{return await chrome.runtime.sendMessage(p)}catch(B){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",B),B}},[]),L=Q.useCallback(M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};chrome.runtime.sendMessage(p)},[]),ee=Q.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const M={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},g={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},B=N=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:N,hasMessages:N&&"messages"in N,hasChat:N&&"chat"in N,messagesValue:N?.messages,chatValue:N?.chat,isMessagesArray:Array.isArray(N?.messages),isChatArray:Array.isArray(N?.chat),responseType:typeof N,responseKeys:N?Object.keys(N):"response is null/undefined",timestamp:new Date().toISOString()}),N===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let j=null;const P=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],ce=(Se,ve)=>{let xe=Se;for(const Ye of ve)if(xe&&typeof xe=="object"&&Ye in xe)xe=xe[Ye];else return;return xe};if(Array.isArray(N))j=N,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});else if(N&&typeof N=="object"){if(N.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",N.error),Te(`Ошибка от background: ${N.error}`),Y([]);return}for(const{path:Se,description:ve}of P){const xe=ce(N,Se);if(Array.isArray(xe)){j=xe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${ve}':`,{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});break}}j||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof N,responseKeys:Object.keys(N),responseSample:JSON.stringify(N).substring(0,500),timestamp:new Date().toISOString()}),j=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:N,responseType:typeof N,responseStringified:JSON.stringify(N).substring(0,200),timestamp:new Date().toISOString()}),j=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:j,isArray:Array.isArray(j),length:j?.length,firstMessage:j?.[0],firstMessageType:j?.[0]?typeof j[0]:"none"}),Array.isArray(j)&&j.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",j.length);const Se=j.filter(ve=>!ve||typeof ve!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",ve),!1):!0).map((ve,xe)=>{try{let Ye=ve.content||ve.text||"";typeof Ye=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",Ye),Ye=JSON.stringify(Ye)):Ye==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Ye=""):Ye=String(Ye);const cl={id:ve.id||String(ve.timestamp||Date.now()+xe),text:Ye,isUser:ve.role?ve.role==="user":!!ve.isUser,timestamp:ve.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${xe}:`,{id:cl.id,textLength:cl.text.length,textType:typeof cl.text,isUser:cl.isUser}),cl}catch(Ye){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${xe}:`,Ye,ve),{id:`error_${Date.now()}_${xe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:j.length,convertedCount:Se.length,firstConverted:Se[0],allConverted:Se.map(ve=>{try{const xe=typeof ve.text=="string"?ve.text.substring(0,50):String(ve.text||"").substring(0,50);return{id:ve.id,text:xe,isUser:ve.isUser,textType:typeof ve.text}}catch(xe){return console.warn("[PluginControlPanel] Error processing message text:",xe,ve),{id:ve.id,text:"[ERROR: invalid text]",isUser:ve.isUser,textType:typeof ve.text}}})}),Y(Se)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),B(M)}catch(N){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",N)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),B(g)}catch(N){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",N)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),B(p)}catch(N){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",N)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);Q.useCallback(M=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:M,hasMessages:M&&"messages"in M,hasChat:M&&"chat"in M,messagesValue:M?.messages,chatValue:M?.chat,isMessagesArray:Array.isArray(M?.messages),isChatArray:Array.isArray(M?.chat),responseType:typeof M,responseKeys:M?Object.keys(M):"response is null/undefined",timestamp:new Date().toISOString()}),M===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let g=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],B=(N,j)=>{let P=N;for(const ce of j)if(P&&typeof P=="object"&&ce in P)P=P[ce];else return;return P};if(Array.isArray(M))g=M,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});else if(M&&typeof M=="object"){if(M.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",M.error),Te(`Ошибка от background: ${M.error}`),Y([]);return}for(const{path:N,description:j}of p){const P=B(M,N);if(Array.isArray(P)){g=P,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${j}':`,{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});break}}g||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof M,responseKeys:Object.keys(M),responseSample:JSON.stringify(M).substring(0,500),timestamp:new Date().toISOString()}),g=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:M,responseType:typeof M,responseStringified:JSON.stringify(M).substring(0,200),timestamp:new Date().toISOString()}),g=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:g,isArray:Array.isArray(g),length:g?.length,firstMessage:g?.[0],firstMessageType:g?.[0]?typeof g[0]:"none"}),Array.isArray(g)&&g.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",g.length);const N=g.filter(j=>!j||typeof j!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",j),!1):!0).map((j,P)=>{try{let ce=j.content||j.text||"",Se=j.timestamp||Date.now();if(typeof ce=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",ce),ce=JSON.stringify(ce);else if(ce==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),ce="";else{ce=String(ce),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${P}:`,ce);try{const xe=JSON.parse(ce);typeof xe=="object"&&xe!==null&&"content"in xe&&(console.log("[PluginControlPanel] Распарсен JSON из content:",xe),ce=String(xe.content||""),xe.timestamp&&typeof xe.timestamp=="number"&&(Se=xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${P}:`,ce)}const ve={id:j.id||String(Se+P),text:ce,isUser:j.role?j.role==="user":!!j.isUser,timestamp:Se};return console.log(`[PluginControlPanel] Конвертировано сообщение ${P}:`,{id:ve.id,textLength:ve.text.length,textType:typeof ve.text,isUser:ve.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${P}:`,ve.text),ve}catch(ce){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${P}:`,ce,j),{id:`error_${Date.now()}_${P}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:g.length,convertedCount:N.length,firstConverted:N[0],allConverted:N.map(j=>{try{const P=typeof j.text=="string"?j.text.substring(0,50):String(j.text||"").substring(0,50);return{id:j.id,text:P,isUser:j.isUser,textType:typeof j.text}}catch(P){return console.warn("[PluginControlPanel] Error processing message text:",P,j),{id:j.id,text:"[ERROR: invalid text]",isUser:j.isUser,textType:typeof j.text}}})}),Y(N)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const me=Q.useCallback(async()=>{Ae(!0),Te(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString(),isRunning:h,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const M=await W({type:"GET_PLUGIN_CHAT",pluginId:R,pageKey:Z});console.log("[PluginControlPanel] loadChat - получен ответ от background:",M),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:ye,messagesCount:J.length,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),M?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",M.error),Te(`Ошибка загрузки чата: ${M.error}`),Y([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(g=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:g,hasMessages:g&&"messages"in g,hasChat:g&&"chat"in g,messagesValue:g?.messages,chatValue:g?.chat,isMessagesArray:Array.isArray(g?.messages),isChatArray:Array.isArray(g?.chat),responseType:typeof g,responseKeys:g?Object.keys(g):"response is null/undefined",timestamp:new Date().toISOString()}),g===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let p=null;const B=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],N=(j,P)=>{let ce=j;for(const Se of P)if(ce&&typeof ce=="object"&&Se in ce)ce=ce[Se];else return;return ce};if(Array.isArray(g))p=g,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(g&&typeof g=="object"){if(g.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",g.error),Te(`Ошибка от background: ${g.error}`),Y([]);return}for(const{path:j,description:P}of B){const ce=N(g,j);if(Array.isArray(ce)){p=ce,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${P}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof g,responseKeys:Object.keys(g),responseSample:JSON.stringify(g).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:g,responseType:typeof g,responseStringified:JSON.stringify(g).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const j=p.filter(P=>!P||typeof P!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",P),!1):!0).map((P,ce)=>{try{let Se=P.content||P.text||"",ve=P.timestamp||Date.now();if(typeof Se=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",Se),Se=JSON.stringify(Se);else if(Se==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Se="";else{Se=String(Se);try{const Ye=JSON.parse(Se);typeof Ye=="object"&&Ye!==null&&"content"in Ye&&(console.log("[PluginControlPanel] Распаршен JSON из content:",Ye),Se=String(Ye.content||""),Ye.timestamp&&typeof Ye.timestamp=="number"&&(ve=Ye.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const xe={id:P.id||String(ve+ce),text:Se,isUser:P.role?P.role==="user":!!P.isUser,timestamp:ve};return console.log(`[PluginControlPanel] Конвертировано сообщение ${ce}:`,{id:xe.id,textLength:xe.text.length,textType:typeof xe.text,isUser:xe.isUser}),xe}catch(Se){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${ce}:`,Se,P),{id:`error_${Date.now()}_${ce}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:j.length,firstConverted:j[0],allConverted:j.map(P=>{try{const ce=typeof P.text=="string"?P.text.substring(0,50):String(P.text||"").substring(0,50);return{id:P.id,text:ce,isUser:P.isUser,textType:typeof P.text}}catch(ce){return console.warn("[PluginControlPanel] Error processing message text:",ce,P),{id:P.id,text:"[ERROR: invalid text]",isUser:P.isUser,textType:typeof P.text}}})}),Y(j)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(M),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(M){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",M),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:ye,messagesCount:J.length,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:M,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),errorStack:M instanceof Error?M.stack:"No stack trace",errorName:M instanceof Error?M.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:R,pageKey:Z}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let g="Ошибка связи с background";M instanceof Error?(g+=`: ${M.message}`,M.name==="TypeError"&&M.message.includes("substring")&&(g+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:M,stack:M.stack,context:{pluginId:R,pageKey:Z}}))):g+=`: ${String(M)}`,Te(g),Y([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[R,Z,W,A,h,c]);Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString()}),me().catch(M=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",M)})},[me]),Q.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),me().catch(M=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",M)}),de()},[Z,me,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:R,pageKey:Z,timestamp:new Date().toISOString()});const M=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===R&&p.pageKey===Z&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:ye,messagesCount:J.length,currentPageKey:Z,pluginId:R,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),me().catch(B=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",B)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:ye,messagesCount:J.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),Te(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},g=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(M),chrome.runtime.onMessage.removeListener(g),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(M),chrome.runtime.onMessage.removeListener(g)}},[R,Z,L,me]),Q.useEffect(()=>{m==="chat"&&de()},[m,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:R,pageKey:Z,draftText:F,message:oe,currentView:m,isRunning:h,isPaused:c})}),Q.useEffect(()=>{const M=console.error;return console.error=(...g)=>{const p=g.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",g),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),M.apply(console,g)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=M,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),Q.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const M=g=>{const p=g.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),Q.useEffect(()=>{typeof F=="string"&&(k(F),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",F))},[F,k]);const re=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:oe}),!oe.trim())return;const M={id:Date.now().toString(),text:oe.trim(),timestamp:Date.now()};k(""),Te(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),L({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:R,pageKey:Z,message:{role:"user",content:M.text,timestamp:M.timestamp}}),C()};Q.useEffect(()=>{const M=p=>{if(!we)return;const B=document.querySelector(".chat-view");if(!B)return;const N=B.getBoundingClientRect(),j=N.bottom-p.clientY,P=100,ce=N.height-80;j>=P&&j<=ce&&Ke(N.height-j)},g=()=>{T(!1),document.body.style.cursor="",document.body.style.userSelect=""};return we&&(document.addEventListener("mousemove",M),document.addEventListener("mouseup",g)),()=>{document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",g)}},[we]),Q.useEffect(()=>{X.current?.scrollIntoView({behavior:"smooth"})},[J]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:J.length,firstMessage:J[0]?{id:J[0].id,text:typeof J[0].text=="string"?J[0].text.substring(0,50):String(J[0].text||"").substring(0,50),isUser:J[0].isUser,timestamp:J[0].timestamp,textType:typeof J[0].text}:null,allMessages:J.map(M=>{try{const g=typeof M.text=="string"?M.text.substring(0,30):String(M.text||"").substring(0,30);return{id:M.id,text:g,isUser:M.isUser,textType:typeof M.text}}catch(g){return console.warn("[PluginControlPanel] Error in message logging:",g,M),{id:M.id,text:"[LOGGING ERROR]",isUser:M.isUser,textType:typeof M.text}}}),timestamp:new Date().toISOString()})},[J]),Q.useEffect(()=>{m==="chat"&&setTimeout(()=>q.current?.focus(),100)},[m]);const ze=M=>{k(M.target.value);const g=M.target;g.style.height="auto";const p=Math.min(Math.max(g.scrollHeight,60),200);g.style.height=`${p}px`,Ke(p)},Ne=M=>{M.key==="Enter"&&!M.shiftKey&&(M.preventDefault(),re())},st=()=>{Ae(!0),Te(null),L({type:"DELETE_PLUGIN_CHAT",pluginId:R,pageKey:Z}),Y([]),C()},Dt=()=>{const M=JSON.stringify(J,null,2),g=new Blob([M],{type:"application/json"});Qy.saveAs(g,`plugin-chat-${R}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":ue},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:o.iconUrl||`plugins/${o.id}/${o.icon||"icon.svg"}`,alt:`${f} icon`,onError:M=>{const g=typeof f=="string"&&f.length>0?f.charAt(0):"P";M.currentTarget.src=`data:image/svg+xml;utf8,${g}`}}),y.jsx("h3",{children:f})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${h?"stop-mode":"start-mode"}`,onClick:Ce,disabled:h||c,title:h?"Остановить":"Запустить",children:h?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:z,disabled:!h||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:I,disabled:!h,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:H,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${b==="chat"?"active":""}`,onClick:()=>O("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${b==="details"?"active":""}`,onClick:()=>O("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[b==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:st,disabled:ye||!!pe,children:ye?"Очистка...":pe?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:Dt,disabled:ye||!!pe,children:ye?"Экспорт...":pe?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ee,disabled:ye,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[ye&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),pe&&y.jsx("div",{className:"chat-error",children:pe}),!ye&&!pe&&J.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:J.map((M,g)=>{console.log("[PluginControlPanel] render message:",g,M);let p=M.text,B=M.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",g,":",M.text);try{const N=JSON.parse(p);if(typeof N=="object"&&N!==null&&"content"in N){console.log("[PluginControlPanel] Парсинг JSON в рендере:",N);let j=N.content;typeof j=="object"?p=JSON.stringify(j):p=String(j||""),N.timestamp&&typeof N.timestamp=="number"&&(B=N.timestamp)}else if(typeof N=="string")p=N;else if(typeof N=="object"&&N!==null){const j=Object.values(N).filter(P=>typeof P=="string");j.length>0?p=String(j[0]):p=JSON.stringify(N)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",g,":",p),y.jsx("div",{className:`chat-message ${M.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(B).toLocaleTimeString()})]})},M.id||g)})}),y.jsx("div",{ref:X})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:q,className:"message-textarea",value:oe,onChange:ze,onKeyPress:Ne,placeholder:"Напишите сообщение...",style:{height:`${ut}px`}}),y.jsx("button",{className:"send-btn",onClick:re,disabled:!oe.trim(),children:"📤"}),y.jsx(jh,{isDraftSaved:K,isDraftLoading:te,draftError:V,messageLength:oe.length,minLength:10,maxLength:1e3})]})]}),b==="details"&&y.jsx(qy,{plugin:o})]})]})},Ky=({toasts:o,onRemove:m})=>y.jsx("div",{className:"toast-container",children:o.map(h=>y.jsx(Zy,{toast:h,onRemove:m},h.id))}),Zy=({toast:o,onRemove:m})=>{const[h,c]=Q.useState(!1),[A,D]=Q.useState(!1);Q.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),o.duration>0){const I=setTimeout(()=>{z()},o.duration);return()=>clearTimeout(I)}},[o.duration]);const z=Q.useCallback(()=>{D(!0),setTimeout(()=>{m(o.id)},300)},[o.id,m]);return y.jsx("div",{className:`toast toast-${o.type} ${h?"toast-visible":""} ${A?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:o.message}),y.jsx("button",{className:"toast-close",onClick:z,"aria-label":"Закрыть уведомление",children:"×"})]})})},ky=({theme:o,isLight:m,onToggle:h,isInSidebar:c=!1})=>{const A=()=>{switch(o){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},D=()=>{switch(o){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},z={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:h,style:z,title:D(),children:A()})};function lg(o){var m,h,c="";if(typeof o=="string"||typeof o=="number")c+=o;else if(typeof o=="object")if(Array.isArray(o)){var A=o.length;for(m=0;m{const m=Fy(o),{conflictingClassGroups:h,conflictingClassGroupModifiers:c}=o;return{getClassGroupId:z=>{const I=z.split(Po);return I[0]===""&&I.length!==1&&I.shift(),ag(I,m)||Wy(z)},getConflictingClassGroupIds:(z,I)=>{const H=h[z]||[];return I&&c[z]?[...H,...c[z]]:H}}},ag=(o,m)=>{if(o.length===0)return m.classGroupId;const h=o[0],c=m.nextPart.get(h),A=c?ag(o.slice(1),c):void 0;if(A)return A;if(m.validators.length===0)return;const D=o.join(Po);return m.validators.find(({validator:z})=>z(D))?.classGroupId},Ld=/^\[(.+)\]$/,Wy=o=>{if(Ld.test(o)){const m=Ld.exec(o)[1],h=m?.substring(0,m.indexOf(":"));if(h)return"arbitrary.."+h}},Fy=o=>{const{theme:m,classGroups:h}=o,c={nextPart:new Map,validators:[]};for(const A in h)wo(h[A],c,A,m);return c},wo=(o,m,h,c)=>{o.forEach(A=>{if(typeof A=="string"){const D=A===""?m:Xd(m,A);D.classGroupId=h;return}if(typeof A=="function"){if(Iy(A)){wo(A(c),m,h,c);return}m.validators.push({validator:A,classGroupId:h});return}Object.entries(A).forEach(([D,z])=>{wo(z,Xd(m,D),h,c)})})},Xd=(o,m)=>{let h=o;return m.split(Po).forEach(c=>{h.nextPart.has(c)||h.nextPart.set(c,{nextPart:new Map,validators:[]}),h=h.nextPart.get(c)}),h},Iy=o=>o.isThemeGetter,ep=o=>{if(o<1)return{get:()=>{},set:()=>{}};let m=0,h=new Map,c=new Map;const A=(D,z)=>{h.set(D,z),m++,m>o&&(m=0,c=h,h=new Map)};return{get(D){let z=h.get(D);if(z!==void 0)return z;if((z=c.get(D))!==void 0)return A(D,z),z},set(D,z){h.has(D)?h.set(D,z):A(D,z)}}},Ho="!",Go=":",tp=Go.length,lp=o=>{const{prefix:m,experimentalParseClassName:h}=o;let c=A=>{const D=[];let z=0,I=0,H=0,b;for(let ie=0;ieH?b-H:void 0;return{modifiers:D,hasImportantModifier:$,baseClassName:Z,maybePostfixModifierPosition:ue}};if(m){const A=m+Go,D=c;c=z=>z.startsWith(A)?D(z.substring(A.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:z,maybePostfixModifierPosition:void 0}}if(h){const A=c;c=D=>h({className:D,parseClassName:A})}return c},ap=o=>o.endsWith(Ho)?o.substring(0,o.length-1):o.startsWith(Ho)?o.substring(1):o,np=o=>{const m=Object.fromEntries(o.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const A=[];let D=[];return c.forEach(z=>{z[0]==="["||m[z]?(A.push(...D.sort(),z),D=[]):D.push(z)}),A.push(...D.sort()),A}},ip=o=>({cache:ep(o.cacheSize),parseClassName:lp(o),sortModifiers:np(o),...$y(o)}),up=/\s+/,sp=(o,m)=>{const{parseClassName:h,getClassGroupId:c,getConflictingClassGroupIds:A,sortModifiers:D}=m,z=[],I=o.trim().split(up);let H="";for(let b=I.length-1;b>=0;b-=1){const O=I[b],{isExternal:Z,modifiers:$,hasImportantModifier:ue,baseClassName:ie,maybePostfixModifierPosition:oe}=h(O);if(Z){H=O+(H.length>0?" "+H:H);continue}let k=!!oe,K=c(k?ie.substring(0,oe):ie);if(!K){if(!k){H=O+(H.length>0?" "+H:H);continue}if(K=c(ie),!K){H=O+(H.length>0?" "+H:H);continue}k=!1}const te=D($).join(":"),V=ue?te+Ho:te,de=V+K;if(z.includes(de))continue;z.push(de);const C=A(K,k);for(let F=0;F0?" "+H:H)}return H};function op(){let o=0,m,h,c="";for(;o{if(typeof o=="string")return o;let m,h="";for(let c=0;cZ(O),o());return h=ip(b),c=h.cache.get,A=h.cache.set,D=I,I(H)}function I(H){const b=c(H);if(b)return b;const O=sp(H,h);return A(H,O),O}return function(){return D(op.apply(null,arguments))}}const tt=o=>{const m=h=>h[o]||[];return m.isThemeGetter=!0,m},ig=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,ug=/^\((?:(\w[\w-]*):)?(.+)\)$/i,rp=/^\d+\/\d+$/,fp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,dp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,gp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,mp=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,hp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,Pa=o=>rp.test(o),Ee=o=>!!o&&!Number.isNaN(Number(o)),jl=o=>!!o&&Number.isInteger(Number(o)),Co=o=>o.endsWith("%")&&Ee(o.slice(0,-1)),ol=o=>fp.test(o),yp=()=>!0,pp=o=>dp.test(o)&&!gp.test(o),sg=()=>!1,vp=o=>mp.test(o),bp=o=>hp.test(o),Sp=o=>!ae(o)&&!ne(o),xp=o=>La(o,rg,sg),ae=o=>ig.test(o),Il=o=>La(o,fg,pp),Oo=o=>La(o,Cp,Ee),Qd=o=>La(o,og,sg),_p=o=>La(o,cg,bp),iu=o=>La(o,dg,vp),ne=o=>ug.test(o),Xn=o=>Xa(o,fg),Ep=o=>Xa(o,Op),Vd=o=>Xa(o,og),Ap=o=>Xa(o,rg),Tp=o=>Xa(o,cg),uu=o=>Xa(o,dg,!0),La=(o,m,h)=>{const c=ig.exec(o);return c?c[1]?m(c[1]):h(c[2]):!1},Xa=(o,m,h=!1)=>{const c=ug.exec(o);return c?c[1]?m(c[1]):h:!1},og=o=>o==="position"||o==="percentage",cg=o=>o==="image"||o==="url",rg=o=>o==="length"||o==="size"||o==="bg-size",fg=o=>o==="length",Cp=o=>o==="number",Op=o=>o==="family-name",dg=o=>o==="shadow",Mp=()=>{const o=tt("color"),m=tt("font"),h=tt("text"),c=tt("font-weight"),A=tt("tracking"),D=tt("leading"),z=tt("breakpoint"),I=tt("container"),H=tt("spacing"),b=tt("radius"),O=tt("shadow"),Z=tt("inset-shadow"),$=tt("text-shadow"),ue=tt("drop-shadow"),ie=tt("blur"),oe=tt("perspective"),k=tt("aspect"),K=tt("ease"),te=tt("animate"),V=()=>["auto","avoid","all","avoid-page","page","left","right","column"],de=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],C=()=>[...de(),ne,ae],F=()=>["auto","hidden","clip","visible","scroll"],J=()=>["auto","contain","none"],Y=()=>[ne,ae,H],ye=()=>[Pa,"full","auto",...Y()],Ae=()=>[jl,"none","subgrid",ne,ae],pe=()=>["auto",{span:["full",jl,ne,ae]},jl,ne,ae],Te=()=>[jl,"auto",ne,ae],ut=()=>["auto","min","max","fr",ne,ae],Ke=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],we=()=>["start","end","center","stretch","center-safe","end-safe"],T=()=>["auto",...Y()],X=()=>[Pa,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...Y()],q=()=>[o,ne,ae],Ce=()=>[...de(),Vd,Qd,{position:[ne,ae]}],f=()=>["no-repeat",{repeat:["","x","y","space","round"]}],R=()=>["auto","cover","contain",Ap,xp,{size:[ne,ae]}],W=()=>[Co,Xn,Il],L=()=>["","none","full",b,ne,ae],ee=()=>["",Ee,Xn,Il],me=()=>["solid","dashed","dotted","double"],re=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ze=()=>[Ee,Co,Vd,Qd],Ne=()=>["","none",ie,ne,ae],st=()=>["none",Ee,ne,ae],Dt=()=>["none",Ee,ne,ae],M=()=>[Ee,ne,ae],g=()=>[Pa,"full",...Y()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[ol],breakpoint:[ol],color:[yp],container:[ol],"drop-shadow":[ol],ease:["in","out","in-out"],font:[Sp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[ol],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[ol],shadow:[ol],spacing:["px",Ee],text:[ol],"text-shadow":[ol],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",Pa,ae,ne,k]}],container:["container"],columns:[{columns:[Ee,ae,ne,I]}],"break-after":[{"break-after":V()}],"break-before":[{"break-before":V()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:C()}],overflow:[{overflow:F()}],"overflow-x":[{"overflow-x":F()}],"overflow-y":[{"overflow-y":F()}],overscroll:[{overscroll:J()}],"overscroll-x":[{"overscroll-x":J()}],"overscroll-y":[{"overscroll-y":J()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:ye()}],"inset-x":[{"inset-x":ye()}],"inset-y":[{"inset-y":ye()}],start:[{start:ye()}],end:[{end:ye()}],top:[{top:ye()}],right:[{right:ye()}],bottom:[{bottom:ye()}],left:[{left:ye()}],visibility:["visible","invisible","collapse"],z:[{z:[jl,"auto",ne,ae]}],basis:[{basis:[Pa,"full","auto",I,...Y()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Ee,Pa,"auto","initial","none",ae]}],grow:[{grow:["",Ee,ne,ae]}],shrink:[{shrink:["",Ee,ne,ae]}],order:[{order:[jl,"first","last","none",ne,ae]}],"grid-cols":[{"grid-cols":Ae()}],"col-start-end":[{col:pe()}],"col-start":[{"col-start":Te()}],"col-end":[{"col-end":Te()}],"grid-rows":[{"grid-rows":Ae()}],"row-start-end":[{row:pe()}],"row-start":[{"row-start":Te()}],"row-end":[{"row-end":Te()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":ut()}],"auto-rows":[{"auto-rows":ut()}],gap:[{gap:Y()}],"gap-x":[{"gap-x":Y()}],"gap-y":[{"gap-y":Y()}],"justify-content":[{justify:[...Ke(),"normal"]}],"justify-items":[{"justify-items":[...we(),"normal"]}],"justify-self":[{"justify-self":["auto",...we()]}],"align-content":[{content:["normal",...Ke()]}],"align-items":[{items:[...we(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...we(),{baseline:["","last"]}]}],"place-content":[{"place-content":Ke()}],"place-items":[{"place-items":[...we(),"baseline"]}],"place-self":[{"place-self":["auto",...we()]}],p:[{p:Y()}],px:[{px:Y()}],py:[{py:Y()}],ps:[{ps:Y()}],pe:[{pe:Y()}],pt:[{pt:Y()}],pr:[{pr:Y()}],pb:[{pb:Y()}],pl:[{pl:Y()}],m:[{m:T()}],mx:[{mx:T()}],my:[{my:T()}],ms:[{ms:T()}],me:[{me:T()}],mt:[{mt:T()}],mr:[{mr:T()}],mb:[{mb:T()}],ml:[{ml:T()}],"space-x":[{"space-x":Y()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":Y()}],"space-y-reverse":["space-y-reverse"],size:[{size:X()}],w:[{w:[I,"screen",...X()]}],"min-w":[{"min-w":[I,"screen","none",...X()]}],"max-w":[{"max-w":[I,"screen","none","prose",{screen:[z]},...X()]}],h:[{h:["screen","lh",...X()]}],"min-h":[{"min-h":["screen","lh","none",...X()]}],"max-h":[{"max-h":["screen","lh",...X()]}],"font-size":[{text:["base",h,Xn,Il]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,ne,Oo]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Co,ae]}],"font-family":[{font:[Ep,ae,m]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[A,ne,ae]}],"line-clamp":[{"line-clamp":[Ee,"none",ne,Oo]}],leading:[{leading:[D,...Y()]}],"list-image":[{"list-image":["none",ne,ae]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",ne,ae]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:q()}],"text-color":[{text:q()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...me(),"wavy"]}],"text-decoration-thickness":[{decoration:[Ee,"from-font","auto",ne,Il]}],"text-decoration-color":[{decoration:q()}],"underline-offset":[{"underline-offset":[Ee,"auto",ne,ae]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:Y()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ne,ae]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ne,ae]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Ce()}],"bg-repeat":[{bg:f()}],"bg-size":[{bg:R()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},jl,ne,ae],radial:["",ne,ae],conic:[jl,ne,ae]},Tp,_p]}],"bg-color":[{bg:q()}],"gradient-from-pos":[{from:W()}],"gradient-via-pos":[{via:W()}],"gradient-to-pos":[{to:W()}],"gradient-from":[{from:q()}],"gradient-via":[{via:q()}],"gradient-to":[{to:q()}],rounded:[{rounded:L()}],"rounded-s":[{"rounded-s":L()}],"rounded-e":[{"rounded-e":L()}],"rounded-t":[{"rounded-t":L()}],"rounded-r":[{"rounded-r":L()}],"rounded-b":[{"rounded-b":L()}],"rounded-l":[{"rounded-l":L()}],"rounded-ss":[{"rounded-ss":L()}],"rounded-se":[{"rounded-se":L()}],"rounded-ee":[{"rounded-ee":L()}],"rounded-es":[{"rounded-es":L()}],"rounded-tl":[{"rounded-tl":L()}],"rounded-tr":[{"rounded-tr":L()}],"rounded-br":[{"rounded-br":L()}],"rounded-bl":[{"rounded-bl":L()}],"border-w":[{border:ee()}],"border-w-x":[{"border-x":ee()}],"border-w-y":[{"border-y":ee()}],"border-w-s":[{"border-s":ee()}],"border-w-e":[{"border-e":ee()}],"border-w-t":[{"border-t":ee()}],"border-w-r":[{"border-r":ee()}],"border-w-b":[{"border-b":ee()}],"border-w-l":[{"border-l":ee()}],"divide-x":[{"divide-x":ee()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ee()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...me(),"hidden","none"]}],"divide-style":[{divide:[...me(),"hidden","none"]}],"border-color":[{border:q()}],"border-color-x":[{"border-x":q()}],"border-color-y":[{"border-y":q()}],"border-color-s":[{"border-s":q()}],"border-color-e":[{"border-e":q()}],"border-color-t":[{"border-t":q()}],"border-color-r":[{"border-r":q()}],"border-color-b":[{"border-b":q()}],"border-color-l":[{"border-l":q()}],"divide-color":[{divide:q()}],"outline-style":[{outline:[...me(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Ee,ne,ae]}],"outline-w":[{outline:["",Ee,Xn,Il]}],"outline-color":[{outline:q()}],shadow:[{shadow:["","none",O,uu,iu]}],"shadow-color":[{shadow:q()}],"inset-shadow":[{"inset-shadow":["none",Z,uu,iu]}],"inset-shadow-color":[{"inset-shadow":q()}],"ring-w":[{ring:ee()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:q()}],"ring-offset-w":[{"ring-offset":[Ee,Il]}],"ring-offset-color":[{"ring-offset":q()}],"inset-ring-w":[{"inset-ring":ee()}],"inset-ring-color":[{"inset-ring":q()}],"text-shadow":[{"text-shadow":["none",$,uu,iu]}],"text-shadow-color":[{"text-shadow":q()}],opacity:[{opacity:[Ee,ne,ae]}],"mix-blend":[{"mix-blend":[...re(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":re()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Ee]}],"mask-image-linear-from-pos":[{"mask-linear-from":ze()}],"mask-image-linear-to-pos":[{"mask-linear-to":ze()}],"mask-image-linear-from-color":[{"mask-linear-from":q()}],"mask-image-linear-to-color":[{"mask-linear-to":q()}],"mask-image-t-from-pos":[{"mask-t-from":ze()}],"mask-image-t-to-pos":[{"mask-t-to":ze()}],"mask-image-t-from-color":[{"mask-t-from":q()}],"mask-image-t-to-color":[{"mask-t-to":q()}],"mask-image-r-from-pos":[{"mask-r-from":ze()}],"mask-image-r-to-pos":[{"mask-r-to":ze()}],"mask-image-r-from-color":[{"mask-r-from":q()}],"mask-image-r-to-color":[{"mask-r-to":q()}],"mask-image-b-from-pos":[{"mask-b-from":ze()}],"mask-image-b-to-pos":[{"mask-b-to":ze()}],"mask-image-b-from-color":[{"mask-b-from":q()}],"mask-image-b-to-color":[{"mask-b-to":q()}],"mask-image-l-from-pos":[{"mask-l-from":ze()}],"mask-image-l-to-pos":[{"mask-l-to":ze()}],"mask-image-l-from-color":[{"mask-l-from":q()}],"mask-image-l-to-color":[{"mask-l-to":q()}],"mask-image-x-from-pos":[{"mask-x-from":ze()}],"mask-image-x-to-pos":[{"mask-x-to":ze()}],"mask-image-x-from-color":[{"mask-x-from":q()}],"mask-image-x-to-color":[{"mask-x-to":q()}],"mask-image-y-from-pos":[{"mask-y-from":ze()}],"mask-image-y-to-pos":[{"mask-y-to":ze()}],"mask-image-y-from-color":[{"mask-y-from":q()}],"mask-image-y-to-color":[{"mask-y-to":q()}],"mask-image-radial":[{"mask-radial":[ne,ae]}],"mask-image-radial-from-pos":[{"mask-radial-from":ze()}],"mask-image-radial-to-pos":[{"mask-radial-to":ze()}],"mask-image-radial-from-color":[{"mask-radial-from":q()}],"mask-image-radial-to-color":[{"mask-radial-to":q()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":de()}],"mask-image-conic-pos":[{"mask-conic":[Ee]}],"mask-image-conic-from-pos":[{"mask-conic-from":ze()}],"mask-image-conic-to-pos":[{"mask-conic-to":ze()}],"mask-image-conic-from-color":[{"mask-conic-from":q()}],"mask-image-conic-to-color":[{"mask-conic-to":q()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Ce()}],"mask-repeat":[{mask:f()}],"mask-size":[{mask:R()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",ne,ae]}],filter:[{filter:["","none",ne,ae]}],blur:[{blur:Ne()}],brightness:[{brightness:[Ee,ne,ae]}],contrast:[{contrast:[Ee,ne,ae]}],"drop-shadow":[{"drop-shadow":["","none",ue,uu,iu]}],"drop-shadow-color":[{"drop-shadow":q()}],grayscale:[{grayscale:["",Ee,ne,ae]}],"hue-rotate":[{"hue-rotate":[Ee,ne,ae]}],invert:[{invert:["",Ee,ne,ae]}],saturate:[{saturate:[Ee,ne,ae]}],sepia:[{sepia:["",Ee,ne,ae]}],"backdrop-filter":[{"backdrop-filter":["","none",ne,ae]}],"backdrop-blur":[{"backdrop-blur":Ne()}],"backdrop-brightness":[{"backdrop-brightness":[Ee,ne,ae]}],"backdrop-contrast":[{"backdrop-contrast":[Ee,ne,ae]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Ee,ne,ae]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Ee,ne,ae]}],"backdrop-invert":[{"backdrop-invert":["",Ee,ne,ae]}],"backdrop-opacity":[{"backdrop-opacity":[Ee,ne,ae]}],"backdrop-saturate":[{"backdrop-saturate":[Ee,ne,ae]}],"backdrop-sepia":[{"backdrop-sepia":["",Ee,ne,ae]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":Y()}],"border-spacing-x":[{"border-spacing-x":Y()}],"border-spacing-y":[{"border-spacing-y":Y()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",ne,ae]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Ee,"initial",ne,ae]}],ease:[{ease:["linear","initial",K,ne,ae]}],delay:[{delay:[Ee,ne,ae]}],animate:[{animate:["none",te,ne,ae]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[oe,ne,ae]}],"perspective-origin":[{"perspective-origin":C()}],rotate:[{rotate:st()}],"rotate-x":[{"rotate-x":st()}],"rotate-y":[{"rotate-y":st()}],"rotate-z":[{"rotate-z":st()}],scale:[{scale:Dt()}],"scale-x":[{"scale-x":Dt()}],"scale-y":[{"scale-y":Dt()}],"scale-z":[{"scale-z":Dt()}],"scale-3d":["scale-3d"],skew:[{skew:M()}],"skew-x":[{"skew-x":M()}],"skew-y":[{"skew-y":M()}],transform:[{transform:[ne,ae,"","none","gpu","cpu"]}],"transform-origin":[{origin:C()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:g()}],"translate-x":[{"translate-x":g()}],"translate-y":[{"translate-y":g()}],"translate-z":[{"translate-z":g()}],"translate-none":["translate-none"],accent:[{accent:q()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:q()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ne,ae]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":Y()}],"scroll-mx":[{"scroll-mx":Y()}],"scroll-my":[{"scroll-my":Y()}],"scroll-ms":[{"scroll-ms":Y()}],"scroll-me":[{"scroll-me":Y()}],"scroll-mt":[{"scroll-mt":Y()}],"scroll-mr":[{"scroll-mr":Y()}],"scroll-mb":[{"scroll-mb":Y()}],"scroll-ml":[{"scroll-ml":Y()}],"scroll-p":[{"scroll-p":Y()}],"scroll-px":[{"scroll-px":Y()}],"scroll-py":[{"scroll-py":Y()}],"scroll-ps":[{"scroll-ps":Y()}],"scroll-pe":[{"scroll-pe":Y()}],"scroll-pt":[{"scroll-pt":Y()}],"scroll-pr":[{"scroll-pr":Y()}],"scroll-pb":[{"scroll-pb":Y()}],"scroll-pl":[{"scroll-pl":Y()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ne,ae]}],fill:[{fill:["none",...q()]}],"stroke-w":[{stroke:[Ee,Xn,Il,Oo]}],stroke:[{stroke:["none",...q()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},Dp=cp(Mp),Kd=(...o)=>Dp(Jy(o));var Mo,Zd;function zp(){if(Zd)return Mo;Zd=1;var o=function(te){return m(te)&&!h(te)};function m(K){return!!K&&typeof K=="object"}function h(K){var te=Object.prototype.toString.call(K);return te==="[object RegExp]"||te==="[object Date]"||D(K)}var c=typeof Symbol=="function"&&Symbol.for,A=c?Symbol.for("react.element"):60103;function D(K){return K.$$typeof===A}function z(K){return Array.isArray(K)?[]:{}}function I(K,te){return te.clone!==!1&&te.isMergeableObject(K)?oe(z(K),K,te):K}function H(K,te,V){return K.concat(te).map(function(de){return I(de,V)})}function b(K,te){if(!te.customMerge)return oe;var V=te.customMerge(K);return typeof V=="function"?V:oe}function O(K){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(K).filter(function(te){return Object.propertyIsEnumerable.call(K,te)}):[]}function Z(K){return Object.keys(K).concat(O(K))}function $(K,te){try{return te in K}catch{return!1}}function ue(K,te){return $(K,te)&&!(Object.hasOwnProperty.call(K,te)&&Object.propertyIsEnumerable.call(K,te))}function ie(K,te,V){var de={};return V.isMergeableObject(K)&&Z(K).forEach(function(C){de[C]=I(K[C],V)}),Z(te).forEach(function(C){ue(K,C)||($(K,C)&&V.isMergeableObject(te[C])?de[C]=b(C,V)(K[C],te[C],V):de[C]=I(te[C],V))}),de}function oe(K,te,V){V=V||{},V.arrayMerge=V.arrayMerge||H,V.isMergeableObject=V.isMergeableObject||o,V.cloneUnlessOtherwiseSpecified=I;var de=Array.isArray(te),C=Array.isArray(K),F=de===C;return F?de?V.arrayMerge(K,te,V):ie(K,te,V):I(te,V)}oe.all=function(te,V){if(!Array.isArray(te))throw new Error("first argument should be an array");return te.reduce(function(de,C){return oe(de,C,V)},{})};var k=oe;return Mo=k,Mo}zp();var Do={exports:{}},Qn={},zo={exports:{}},Ro={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var kd;function Rp(){return kd||(kd=1,(function(o){function m(T,X){var q=T.length;T.push(X);e:for(;0>>1,f=T[Ce];if(0>>1;CeA(L,q))eeA(me,L)?(T[Ce]=me,T[ee]=q,Ce=ee):(T[Ce]=L,T[W]=q,Ce=W);else if(eeA(me,q))T[Ce]=me,T[ee]=q,Ce=ee;else break e}}return X}function A(T,X){var q=T.sortIndex-X.sortIndex;return q!==0?q:T.id-X.id}if(o.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var D=performance;o.unstable_now=function(){return D.now()}}else{var z=Date,I=z.now();o.unstable_now=function(){return z.now()-I}}var H=[],b=[],O=1,Z=null,$=3,ue=!1,ie=!1,oe=!1,k=!1,K=typeof setTimeout=="function"?setTimeout:null,te=typeof clearTimeout=="function"?clearTimeout:null,V=typeof setImmediate<"u"?setImmediate:null;function de(T){for(var X=h(b);X!==null;){if(X.callback===null)c(b);else if(X.startTime<=T)c(b),X.sortIndex=X.expirationTime,m(H,X);else break;X=h(b)}}function C(T){if(oe=!1,de(T),!ie)if(h(H)!==null)ie=!0,F||(F=!0,Te());else{var X=h(b);X!==null&&we(C,X.startTime-T)}}var F=!1,J=-1,Y=5,ye=-1;function Ae(){return k?!0:!(o.unstable_now()-yeT&&Ae());){var Ce=Z.callback;if(typeof Ce=="function"){Z.callback=null,$=Z.priorityLevel;var f=Ce(Z.expirationTime<=T);if(T=o.unstable_now(),typeof f=="function"){Z.callback=f,de(T),X=!0;break t}Z===h(H)&&c(H),de(T)}else c(H);Z=h(H)}if(Z!==null)X=!0;else{var R=h(b);R!==null&&we(C,R.startTime-T),X=!1}}break e}finally{Z=null,$=q,ue=!1}X=void 0}}finally{X?Te():F=!1}}}var Te;if(typeof V=="function")Te=function(){V(pe)};else if(typeof MessageChannel<"u"){var ut=new MessageChannel,Ke=ut.port2;ut.port1.onmessage=pe,Te=function(){Ke.postMessage(null)}}else Te=function(){K(pe,0)};function we(T,X){J=K(function(){T(o.unstable_now())},X)}o.unstable_IdlePriority=5,o.unstable_ImmediatePriority=1,o.unstable_LowPriority=4,o.unstable_NormalPriority=3,o.unstable_Profiling=null,o.unstable_UserBlockingPriority=2,o.unstable_cancelCallback=function(T){T.callback=null},o.unstable_forceFrameRate=function(T){0>T||125Ce?(T.sortIndex=q,m(b,T),h(H)===null&&T===h(b)&&(oe?(te(J),J=-1):oe=!0,we(C,q-Ce))):(T.sortIndex=f,m(H,T),ie||ue||(ie=!0,F||(F=!0,Te()))),T},o.unstable_shouldYield=Ae,o.unstable_wrapCallback=function(T){var X=$;return function(){var q=$;$=X;try{return T.apply(this,arguments)}finally{$=q}}}})(Ro)),Ro}var Jd;function Up(){return Jd||(Jd=1,zo.exports=Rp()),zo.exports}var Uo={exports:{}},gt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var $d;function Np(){if($d)return gt;$d=1;var o=Bo();function m(H){var b="https://react.dev/errors/"+H;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(m){console.error(m)}}return o(),Uo.exports=Np(),Uo.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Fd;function wp(){if(Fd)return Qn;Fd=1;var o=Up(),m=Bo(),h=jp();function c(e){var t="https://react.dev/errors/"+e;if(1f||(e.current=Ce[f],Ce[f]=null,f--)}function L(e,t){f++,Ce[f]=e.current,e.current=t}var ee=R(null),me=R(null),re=R(null),ze=R(null);function Ne(e,t){switch(L(re,t),L(me,e),L(ee,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?nd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=nd(t),e=id(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}W(ee),L(ee,e)}function st(){W(ee),W(me),W(re)}function Dt(e){e.memoizedState!==null&&L(ze,e);var t=ee.current,l=id(t,e.type);t!==l&&(L(me,e),L(ee,l))}function M(e){me.current===e&&(W(ee),W(me)),ze.current===e&&(W(ze),Gn._currentValue=q)}var g=Object.prototype.hasOwnProperty,p=o.unstable_scheduleCallback,B=o.unstable_cancelCallback,N=o.unstable_shouldYield,j=o.unstable_requestPaint,P=o.unstable_now,ce=o.unstable_getCurrentPriorityLevel,Se=o.unstable_ImmediatePriority,ve=o.unstable_UserBlockingPriority,xe=o.unstable_NormalPriority,Ye=o.unstable_LowPriority,cl=o.unstable_IdlePriority,gg=o.log,mg=o.unstable_setDisableYieldValue,Qa=null,St=null;function rl(e){if(typeof gg=="function"&&mg(e),St&&typeof St.setStrictMode=="function")try{St.setStrictMode(Qa,e)}catch{}}var xt=Math.clz32?Math.clz32:pg,hg=Math.log,yg=Math.LN2;function pg(e){return e>>>=0,e===0?32:31-(hg(e)/yg|0)|0}var Vn=256,Kn=4194304;function wl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Zn(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var n=0,i=e.suspendedLanes,u=e.pingedLanes;e=e.warmLanes;var s=a&134217727;return s!==0?(a=s&~i,a!==0?n=wl(a):(u&=s,u!==0?n=wl(u):l||(l=s&~e,l!==0&&(n=wl(l))))):(s=a&~i,s!==0?n=wl(s):u!==0?n=wl(u):l||(l=a&~e,l!==0&&(n=wl(l)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:n}function Va(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function vg(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function qo(){var e=Vn;return Vn<<=1,(Vn&4194048)===0&&(Vn=256),e}function Yo(){var e=Kn;return Kn<<=1,(Kn&62914560)===0&&(Kn=4194304),e}function cu(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Ka(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function bg(e,t,l,a,n,i){var u=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var s=e.entanglements,r=e.expirationTimes,x=e.hiddenUpdates;for(l=u&~l;0)":-1n||r[a]!==x[n]){var U=` -`+r[a].replace(" at new "," at ");return e.displayName&&U.includes("")&&(U=U.replace("",e.displayName)),U}while(1<=a&&0<=n);break}}}finally{hu=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?ia(l):""}function Tg(e){switch(e.tag){case 26:case 27:case 5:return ia(e.type);case 16:return ia("Lazy");case 13:return ia("Suspense");case 19:return ia("SuspenseList");case 0:case 15:return yu(e.type,!1);case 11:return yu(e.type.render,!1);case 1:return yu(e.type,!0);case 31:return ia("Activity");default:return""}}function Wo(e){try{var t="";do t+=Tg(e),e=e.return;while(e);return t}catch(l){return` -Error generating stack: `+l.message+` -`+l.stack}}function zt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Fo(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Cg(e){var t=Fo(e)?"checked":"value",l=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),a=""+e[t];if(!e.hasOwnProperty(t)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var n=l.get,i=l.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return n.call(this)},set:function(u){a=""+u,i.call(this,u)}}),Object.defineProperty(e,t,{enumerable:l.enumerable}),{getValue:function(){return a},setValue:function(u){a=""+u},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function $n(e){e._valueTracker||(e._valueTracker=Cg(e))}function Io(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var l=t.getValue(),a="";return e&&(a=Fo(e)?e.checked?"true":"false":e.value),e=a,e!==l?(t.setValue(e),!0):!1}function Wn(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Og=/[\n"\\]/g;function Rt(e){return e.replace(Og,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function pu(e,t,l,a,n,i,u,s){e.name="",u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"?e.type=u:e.removeAttribute("type"),t!=null?u==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+zt(t)):e.value!==""+zt(t)&&(e.value=""+zt(t)):u!=="submit"&&u!=="reset"||e.removeAttribute("value"),t!=null?vu(e,u,zt(t)):l!=null?vu(e,u,zt(l)):a!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.name=""+zt(s):e.removeAttribute("name")}function ec(e,t,l,a,n,i,u,s){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;l=l!=null?""+zt(l):"",t=t!=null?""+zt(t):l,s||t===e.value||(e.value=t),e.defaultValue=t}a=a??n,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=s?e.checked:!!a,e.defaultChecked=!!a,u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"&&(e.name=u)}function vu(e,t,l){t==="number"&&Wn(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function ua(e,t,l,a){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Eu=!1;if(kt)try{var $a={};Object.defineProperty($a,"passive",{get:function(){Eu=!0}}),window.addEventListener("test",$a,$a),window.removeEventListener("test",$a,$a)}catch{Eu=!1}var dl=null,Au=null,In=null;function sc(){if(In)return In;var e,t=Au,l=t.length,a,n="value"in dl?dl.value:dl.textContent,i=n.length;for(e=0;e=Ia),gc=" ",mc=!1;function hc(e,t){switch(e){case"keyup":return tm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function yc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var ra=!1;function am(e,t){switch(e){case"compositionend":return yc(t);case"keypress":return t.which!==32?null:(mc=!0,gc);case"textInput":return e=t.data,e===gc&&mc?null:e;default:return null}}function nm(e,t){if(ra)return e==="compositionend"||!Du&&hc(e,t)?(e=sc(),In=Au=dl=null,ra=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Ac(l)}}function Cc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Cc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Oc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Wn(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=Wn(e.document)}return t}function Uu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var dm=kt&&"documentMode"in document&&11>=document.documentMode,fa=null,Nu=null,an=null,ju=!1;function Mc(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;ju||fa==null||fa!==Wn(a)||(a=fa,"selectionStart"in a&&Uu(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),an&&ln(an,a)||(an=a,a=Xi(Nu,"onSelect"),0>=u,n-=u,$t=1<<32-xt(t)+n|l<i?i:8;var u=T.T,s={};T.T=s,bs(e,!1,t,l);try{var r=n(),x=T.S;if(x!==null&&x(s,r),r!==null&&typeof r=="object"&&typeof r.then=="function"){var U=xm(r,a);bn(e,t,U,Ot(e))}else bn(e,t,a,Ot(e))}catch(G){bn(e,t,{then:function(){},status:"rejected",reason:G},Ot())}finally{X.p=i,T.T=u}}function Cm(){}function ps(e,t,l,a){if(e.tag!==5)throw Error(c(476));var n=Dr(e).queue;Mr(e,n,t,q,l===null?Cm:function(){return zr(e),l(a)})}function Dr(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:q,baseState:q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:q},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function zr(e){var t=Dr(e).next.queue;bn(e,t,{},Ot())}function vs(){return dt(Gn)}function Rr(){return Fe().memoizedState}function Ur(){return Fe().memoizedState}function Om(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Ot();e=hl(l);var a=yl(t,e,l);a!==null&&(Mt(a,t,l),gn(a,t,l)),t={cache:ku()},e.payload=t;return}t=t.return}}function Mm(e,t,l){var a=Ot();l={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},Ei(e)?jr(t,l):(l=Bu(e,t,l,a),l!==null&&(Mt(l,e,a),wr(l,t,a)))}function Nr(e,t,l){var a=Ot();bn(e,t,l,a)}function bn(e,t,l,a){var n={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(Ei(e))jr(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var u=t.lastRenderedState,s=i(u,l);if(n.hasEagerState=!0,n.eagerState=s,_t(s,u))return ui(e,t,n,0),Xe===null&&ii(),!1}catch{}finally{}if(l=Bu(e,t,n,a),l!==null)return Mt(l,e,a),wr(l,t,a),!0}return!1}function bs(e,t,l,a){if(a={lane:2,revertLane:Ws(),action:a,hasEagerState:!1,eagerState:null,next:null},Ei(e)){if(t)throw Error(c(479))}else t=Bu(e,l,a,2),t!==null&&Mt(t,e,2)}function Ei(e){var t=e.alternate;return e===_e||t!==null&&t===_e}function jr(e,t){xa=pi=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function wr(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,Xo(e,l)}}var Ai={readContext:dt,use:bi,useCallback:Je,useContext:Je,useEffect:Je,useImperativeHandle:Je,useLayoutEffect:Je,useInsertionEffect:Je,useMemo:Je,useReducer:Je,useRef:Je,useState:Je,useDebugValue:Je,useDeferredValue:Je,useTransition:Je,useSyncExternalStore:Je,useId:Je,useHostTransitionStatus:Je,useFormState:Je,useActionState:Je,useOptimistic:Je,useMemoCache:Je,useCacheRefresh:Je},Hr={readContext:dt,use:bi,useCallback:function(e,t){return pt().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:br,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,_i(4194308,4,Er.bind(null,t,e),l)},useLayoutEffect:function(e,t){return _i(4194308,4,e,t)},useInsertionEffect:function(e,t){_i(4,2,e,t)},useMemo:function(e,t){var l=pt();t=t===void 0?null:t;var a=e();if(kl){rl(!0);try{e()}finally{rl(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=pt();if(l!==void 0){var n=l(t);if(kl){rl(!0);try{l(t)}finally{rl(!1)}}}else n=t;return a.memoizedState=a.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},a.queue=e,e=e.dispatch=Mm.bind(null,_e,e),[a.memoizedState,e]},useRef:function(e){var t=pt();return e={current:e},t.memoizedState=e},useState:function(e){e=gs(e);var t=e.queue,l=Nr.bind(null,_e,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:hs,useDeferredValue:function(e,t){var l=pt();return ys(l,e,t)},useTransition:function(){var e=gs(!1);return e=Mr.bind(null,_e,e.queue,!0,!1),pt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=_e,n=pt();if(je){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Xe===null)throw Error(c(349));(Re&124)!==0||ar(a,t,l)}n.memoizedState=l;var i={value:l,getSnapshot:t};return n.queue=i,br(ir.bind(null,a,i,e),[e]),a.flags|=2048,Ea(9,xi(),nr.bind(null,a,i,l,t),null),l},useId:function(){var e=pt(),t=Xe.identifierPrefix;if(je){var l=Wt,a=$t;l=(a&~(1<<32-xt(a)-1)).toString(32)+l,t="«"+t+"R"+l,l=vi++,0ge?(it=se,se=null):it=se.sibling;var Ue=_(v,se,S[ge],w);if(Ue===null){se===null&&(se=it);break}e&&se&&Ue.alternate===null&&t(v,se),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue,se=it}if(ge===S.length)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;gege?(it=se,se=null):it=se.sibling;var Nl=_(v,se,Ue.value,w);if(Nl===null){se===null&&(se=it);break}e&&se&&Nl.alternate===null&&t(v,se),d=i(Nl,d,ge),Oe===null?le=Nl:Oe.sibling=Nl,Oe=Nl,se=it}if(Ue.done)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;!Ue.done;ge++,Ue=S.next())Ue=G(v,Ue.value,w),Ue!==null&&(d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return je&&Ll(v,ge),le}for(se=a(se);!Ue.done;ge++,Ue=S.next())Ue=E(se,v,ge,Ue.value,w),Ue!==null&&(e&&Ue.alternate!==null&&se.delete(Ue.key===null?ge:Ue.key),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return e&&se.forEach(function(zh){return t(v,zh)}),je&&Ll(v,ge),le}function qe(v,d,S,w){if(typeof S=="object"&&S!==null&&S.type===ie&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case $:e:{for(var le=S.key;d!==null;){if(d.key===le){if(le=S.type,le===ie){if(d.tag===7){l(v,d.sibling),w=n(d,S.props.children),w.return=v,v=w;break e}}else if(d.elementType===le||typeof le=="object"&&le!==null&&le.$$typeof===Y&&Br(le)===d.type){l(v,d.sibling),w=n(d,S.props),xn(w,S),w.return=v,v=w;break e}l(v,d);break}else t(v,d);d=d.sibling}S.type===ie?(w=ql(S.props.children,v.mode,w,S.key),w.return=v,v=w):(w=oi(S.type,S.key,S.props,null,v.mode,w),xn(w,S),w.return=v,v=w)}return u(v);case ue:e:{for(le=S.key;d!==null;){if(d.key===le)if(d.tag===4&&d.stateNode.containerInfo===S.containerInfo&&d.stateNode.implementation===S.implementation){l(v,d.sibling),w=n(d,S.children||[]),w.return=v,v=w;break e}else{l(v,d);break}else t(v,d);d=d.sibling}w=Yu(S,v.mode,w),w.return=v,v=w}return u(v);case Y:return le=S._init,S=le(S._payload),qe(v,d,S,w)}if(we(S))return he(v,d,S,w);if(Te(S)){if(le=Te(S),typeof le!="function")throw Error(c(150));return S=le.call(S),fe(v,d,S,w)}if(typeof S.then=="function")return qe(v,d,Ti(S),w);if(S.$$typeof===V)return qe(v,d,di(v,S),w);Ci(v,S)}return typeof S=="string"&&S!==""||typeof S=="number"||typeof S=="bigint"?(S=""+S,d!==null&&d.tag===6?(l(v,d.sibling),w=n(d,S),w.return=v,v=w):(l(v,d),w=qu(S,v.mode,w),w.return=v,v=w),u(v)):l(v,d)}return function(v,d,S,w){try{Sn=0;var le=qe(v,d,S,w);return Aa=null,le}catch(se){if(se===fn||se===mi)throw se;var Oe=Et(29,se,null,v.mode);return Oe.lanes=w,Oe.return=v,Oe}finally{}}}var Ta=Pr(!0),qr=Pr(!1),Ht=R(null),Xt=null;function vl(e){var t=e.alternate;L(et,et.current&1),L(Ht,e),Xt===null&&(t===null||Sa.current!==null||t.memoizedState!==null)&&(Xt=e)}function Yr(e){if(e.tag===22){if(L(et,et.current),L(Ht,e),Xt===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(Xt=e)}}else bl()}function bl(){L(et,et.current),L(Ht,Ht.current)}function tl(e){W(Ht),Xt===e&&(Xt=null),W(et)}var et=R(0);function Oi(e){for(var t=e;t!==null;){if(t.tag===13){var l=t.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||co(l)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function Ss(e,t,l,a){t=e.memoizedState,l=l(a,t),l=l==null?t:O({},t,l),e.memoizedState=l,e.lanes===0&&(e.updateQueue.baseState=l)}var xs={enqueueSetState:function(e,t,l){e=e._reactInternals;var a=Ot(),n=hl(a);n.payload=t,l!=null&&(n.callback=l),t=yl(e,n,a),t!==null&&(Mt(t,e,a),gn(t,e,a))},enqueueReplaceState:function(e,t,l){e=e._reactInternals;var a=Ot(),n=hl(a);n.tag=1,n.payload=t,l!=null&&(n.callback=l),t=yl(e,n,a),t!==null&&(Mt(t,e,a),gn(t,e,a))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var l=Ot(),a=hl(l);a.tag=2,t!=null&&(a.callback=t),t=yl(e,a,l),t!==null&&(Mt(t,e,l),gn(t,e,l))}};function Lr(e,t,l,a,n,i,u){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(a,i,u):t.prototype&&t.prototype.isPureReactComponent?!ln(l,a)||!ln(n,i):!0}function Xr(e,t,l,a){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(l,a),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(l,a),t.state!==e&&xs.enqueueReplaceState(t,t.state,null)}function Jl(e,t){var l=t;if("ref"in t){l={};for(var a in t)a!=="ref"&&(l[a]=t[a])}if(e=e.defaultProps){l===t&&(l=O({},l));for(var n in e)l[n]===void 0&&(l[n]=e[n])}return l}var Mi=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Qr(e){Mi(e)}function Vr(e){console.error(e)}function Kr(e){Mi(e)}function Di(e,t){try{var l=e.onUncaughtError;l(t.value,{componentStack:t.stack})}catch(a){setTimeout(function(){throw a})}}function Zr(e,t,l){try{var a=e.onCaughtError;a(l.value,{componentStack:l.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(n){setTimeout(function(){throw n})}}function _s(e,t,l){return l=hl(l),l.tag=3,l.payload={element:null},l.callback=function(){Di(e,t)},l}function kr(e){return e=hl(e),e.tag=3,e}function Jr(e,t,l,a){var n=l.type.getDerivedStateFromError;if(typeof n=="function"){var i=a.value;e.payload=function(){return n(i)},e.callback=function(){Zr(t,l,a)}}var u=l.stateNode;u!==null&&typeof u.componentDidCatch=="function"&&(e.callback=function(){Zr(t,l,a),typeof n!="function"&&(Tl===null?Tl=new Set([this]):Tl.add(this));var s=a.stack;this.componentDidCatch(a.value,{componentStack:s!==null?s:""})})}function zm(e,t,l,a,n){if(l.flags|=32768,a!==null&&typeof a=="object"&&typeof a.then=="function"){if(t=l.alternate,t!==null&&on(t,l,n,!0),l=Ht.current,l!==null){switch(l.tag){case 13:return Xt===null?Ks():l.alternate===null&&ke===0&&(ke=3),l.flags&=-257,l.flags|=65536,l.lanes=n,a===Wu?l.flags|=16384:(t=l.updateQueue,t===null?l.updateQueue=new Set([a]):t.add(a),ks(e,a,n)),!1;case 22:return l.flags|=65536,a===Wu?l.flags|=16384:(t=l.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([a])},l.updateQueue=t):(l=t.retryQueue,l===null?t.retryQueue=new Set([a]):l.add(a)),ks(e,a,n)),!1}throw Error(c(435,l.tag))}return ks(e,a,n),Ks(),!1}if(je)return t=Ht.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=n,a!==Qu&&(e=Error(c(422),{cause:a}),sn(Ut(e,l)))):(a!==Qu&&(t=Error(c(423),{cause:a}),sn(Ut(t,l))),e=e.current.alternate,e.flags|=65536,n&=-n,e.lanes|=n,a=Ut(a,l),n=_s(e.stateNode,a,n),es(e,n),ke!==4&&(ke=2)),!1;var i=Error(c(520),{cause:a});if(i=Ut(i,l),Mn===null?Mn=[i]:Mn.push(i),ke!==4&&(ke=2),t===null)return!0;a=Ut(a,l),l=t;do{switch(l.tag){case 3:return l.flags|=65536,e=n&-n,l.lanes|=e,e=_s(l.stateNode,a,e),es(l,e),!1;case 1:if(t=l.type,i=l.stateNode,(l.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(Tl===null||!Tl.has(i))))return l.flags|=65536,n&=-n,l.lanes|=n,n=kr(n),Jr(n,e,l,a),es(l,n),!1}l=l.return}while(l!==null);return!1}var $r=Error(c(461)),at=!1;function ot(e,t,l,a){t.child=e===null?qr(t,null,l,a):Ta(t,e.child,l,a)}function Wr(e,t,l,a,n){l=l.render;var i=t.ref;if("ref"in a){var u={};for(var s in a)s!=="ref"&&(u[s]=a[s])}else u=a;return Kl(t),a=is(e,t,l,u,i,n),s=us(),e!==null&&!at?(ss(e,t,n),ll(e,t,n)):(je&&s&&Lu(t),t.flags|=1,ot(e,t,a,n),t.child)}function Fr(e,t,l,a,n){if(e===null){var i=l.type;return typeof i=="function"&&!Pu(i)&&i.defaultProps===void 0&&l.compare===null?(t.tag=15,t.type=i,Ir(e,t,i,a,n)):(e=oi(l.type,null,a,t,t.mode,n),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!zs(e,n)){var u=i.memoizedProps;if(l=l.compare,l=l!==null?l:ln,l(u,a)&&e.ref===t.ref)return ll(e,t,n)}return t.flags|=1,e=Jt(i,a),e.ref=t.ref,e.return=t,t.child=e}function Ir(e,t,l,a,n){if(e!==null){var i=e.memoizedProps;if(ln(i,a)&&e.ref===t.ref)if(at=!1,t.pendingProps=a=i,zs(e,n))(e.flags&131072)!==0&&(at=!0);else return t.lanes=e.lanes,ll(e,t,n)}return Es(e,t,l,a,n)}function ef(e,t,l){var a=t.pendingProps,n=a.children,i=e!==null?e.memoizedState:null;if(a.mode==="hidden"){if((t.flags&128)!==0){if(a=i!==null?i.baseLanes|l:l,e!==null){for(n=t.child=e.child,i=0;n!==null;)i=i|n.lanes|n.childLanes,n=n.sibling;t.childLanes=i&~a}else t.childLanes=0,t.child=null;return tf(e,t,a,l)}if((l&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&gi(t,i!==null?i.cachePool:null),i!==null?Ic(t,i):ls(),Yr(t);else return t.lanes=t.childLanes=536870912,tf(e,t,i!==null?i.baseLanes|l:l,l)}else i!==null?(gi(t,i.cachePool),Ic(t,i),bl(),t.memoizedState=null):(e!==null&&gi(t,null),ls(),bl());return ot(e,t,n,l),t.child}function tf(e,t,l,a){var n=$u();return n=n===null?null:{parent:Ie._currentValue,pool:n},t.memoizedState={baseLanes:l,cachePool:n},e!==null&&gi(t,null),ls(),Yr(t),e!==null&&on(e,t,a,!0),null}function zi(e,t){var l=t.ref;if(l===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(c(284));(e===null||e.ref!==l)&&(t.flags|=4194816)}}function Es(e,t,l,a,n){return Kl(t),l=is(e,t,l,a,void 0,n),a=us(),e!==null&&!at?(ss(e,t,n),ll(e,t,n)):(je&&a&&Lu(t),t.flags|=1,ot(e,t,l,n),t.child)}function lf(e,t,l,a,n,i){return Kl(t),t.updateQueue=null,l=tr(t,a,l,n),er(e),a=us(),e!==null&&!at?(ss(e,t,i),ll(e,t,i)):(je&&a&&Lu(t),t.flags|=1,ot(e,t,l,i),t.child)}function af(e,t,l,a,n){if(Kl(t),t.stateNode===null){var i=ha,u=l.contextType;typeof u=="object"&&u!==null&&(i=dt(u)),i=new l(a,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=xs,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=a,i.state=t.memoizedState,i.refs={},Fu(t),u=l.contextType,i.context=typeof u=="object"&&u!==null?dt(u):ha,i.state=t.memoizedState,u=l.getDerivedStateFromProps,typeof u=="function"&&(Ss(t,l,u,a),i.state=t.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(u=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),u!==i.state&&xs.enqueueReplaceState(i,i.state,null),hn(t,a,i,n),mn(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),a=!0}else if(e===null){i=t.stateNode;var s=t.memoizedProps,r=Jl(l,s);i.props=r;var x=i.context,U=l.contextType;u=ha,typeof U=="object"&&U!==null&&(u=dt(U));var G=l.getDerivedStateFromProps;U=typeof G=="function"||typeof i.getSnapshotBeforeUpdate=="function",s=t.pendingProps!==s,U||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s||x!==u)&&Xr(t,i,a,u),ml=!1;var _=t.memoizedState;i.state=_,hn(t,a,i,n),mn(),x=t.memoizedState,s||_!==x||ml?(typeof G=="function"&&(Ss(t,l,G,a),x=t.memoizedState),(r=ml||Lr(t,l,r,a,_,x,u))?(U||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=a,t.memoizedState=x),i.props=a,i.state=x,i.context=u,a=r):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),a=!1)}else{i=t.stateNode,Iu(e,t),u=t.memoizedProps,U=Jl(l,u),i.props=U,G=t.pendingProps,_=i.context,x=l.contextType,r=ha,typeof x=="object"&&x!==null&&(r=dt(x)),s=l.getDerivedStateFromProps,(x=typeof s=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==G||_!==r)&&Xr(t,i,a,r),ml=!1,_=t.memoizedState,i.state=_,hn(t,a,i,n),mn();var E=t.memoizedState;u!==G||_!==E||ml||e!==null&&e.dependencies!==null&&fi(e.dependencies)?(typeof s=="function"&&(Ss(t,l,s,a),E=t.memoizedState),(U=ml||Lr(t,l,U,a,_,E,r)||e!==null&&e.dependencies!==null&&fi(e.dependencies))?(x||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(a,E,r),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(a,E,r)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),t.memoizedProps=a,t.memoizedState=E),i.props=a,i.state=E,i.context=r,a=U):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),a=!1)}return i=a,zi(e,t),a=(t.flags&128)!==0,i||a?(i=t.stateNode,l=a&&typeof l.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&a?(t.child=Ta(t,e.child,null,n),t.child=Ta(t,null,l,n)):ot(e,t,l,n),t.memoizedState=i.state,e=t.child):e=ll(e,t,n),e}function nf(e,t,l,a){return un(),t.flags|=256,ot(e,t,l,a),t.child}var As={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ts(e){return{baseLanes:e,cachePool:Vc()}}function Cs(e,t,l){return e=e!==null?e.childLanes&~l:0,t&&(e|=Gt),e}function uf(e,t,l){var a=t.pendingProps,n=!1,i=(t.flags&128)!==0,u;if((u=i)||(u=e!==null&&e.memoizedState===null?!1:(et.current&2)!==0),u&&(n=!0,t.flags&=-129),u=(t.flags&32)!==0,t.flags&=-33,e===null){if(je){if(n?vl(t):bl(),je){var s=Ze,r;if(r=s){e:{for(r=s,s=Lt;r.nodeType!==8;){if(!s){s=null;break e}if(r=Yt(r.nextSibling),r===null){s=null;break e}}s=r}s!==null?(t.memoizedState={dehydrated:s,treeContext:Yl!==null?{id:$t,overflow:Wt}:null,retryLane:536870912,hydrationErrors:null},r=Et(18,null,null,0),r.stateNode=s,r.return=t,t.child=r,mt=t,Ze=null,r=!0):r=!1}r||Ql(t)}if(s=t.memoizedState,s!==null&&(s=s.dehydrated,s!==null))return co(s)?t.lanes=32:t.lanes=536870912,null;tl(t)}return s=a.children,a=a.fallback,n?(bl(),n=t.mode,s=Ri({mode:"hidden",children:s},n),a=ql(a,n,l,null),s.return=t,a.return=t,s.sibling=a,t.child=s,n=t.child,n.memoizedState=Ts(l),n.childLanes=Cs(e,u,l),t.memoizedState=As,a):(vl(t),Os(t,s))}if(r=e.memoizedState,r!==null&&(s=r.dehydrated,s!==null)){if(i)t.flags&256?(vl(t),t.flags&=-257,t=Ms(e,t,l)):t.memoizedState!==null?(bl(),t.child=e.child,t.flags|=128,t=null):(bl(),n=a.fallback,s=t.mode,a=Ri({mode:"visible",children:a.children},s),n=ql(n,s,l,null),n.flags|=2,a.return=t,n.return=t,a.sibling=n,t.child=a,Ta(t,e.child,null,l),a=t.child,a.memoizedState=Ts(l),a.childLanes=Cs(e,u,l),t.memoizedState=As,t=n);else if(vl(t),co(s)){if(u=s.nextSibling&&s.nextSibling.dataset,u)var x=u.dgst;u=x,a=Error(c(419)),a.stack="",a.digest=u,sn({value:a,source:null,stack:null}),t=Ms(e,t,l)}else if(at||on(e,t,l,!1),u=(l&e.childLanes)!==0,at||u){if(u=Xe,u!==null&&(a=l&-l,a=(a&42)!==0?1:ru(a),a=(a&(u.suspendedLanes|l))!==0?0:a,a!==0&&a!==r.retryLane))throw r.retryLane=a,ma(e,a),Mt(u,e,a),$r;s.data==="$?"||Ks(),t=Ms(e,t,l)}else s.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=r.treeContext,Ze=Yt(s.nextSibling),mt=t,je=!0,Xl=null,Lt=!1,e!==null&&(jt[wt++]=$t,jt[wt++]=Wt,jt[wt++]=Yl,$t=e.id,Wt=e.overflow,Yl=t),t=Os(t,a.children),t.flags|=4096);return t}return n?(bl(),n=a.fallback,s=t.mode,r=e.child,x=r.sibling,a=Jt(r,{mode:"hidden",children:a.children}),a.subtreeFlags=r.subtreeFlags&65011712,x!==null?n=Jt(x,n):(n=ql(n,s,l,null),n.flags|=2),n.return=t,a.return=t,a.sibling=n,t.child=a,a=n,n=t.child,s=e.child.memoizedState,s===null?s=Ts(l):(r=s.cachePool,r!==null?(x=Ie._currentValue,r=r.parent!==x?{parent:x,pool:x}:r):r=Vc(),s={baseLanes:s.baseLanes|l,cachePool:r}),n.memoizedState=s,n.childLanes=Cs(e,u,l),t.memoizedState=As,a):(vl(t),l=e.child,e=l.sibling,l=Jt(l,{mode:"visible",children:a.children}),l.return=t,l.sibling=null,e!==null&&(u=t.deletions,u===null?(t.deletions=[e],t.flags|=16):u.push(e)),t.child=l,t.memoizedState=null,l)}function Os(e,t){return t=Ri({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Ri(e,t){return e=Et(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function Ms(e,t,l){return Ta(t,e.child,null,l),e=Os(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function sf(e,t,l){e.lanes|=t;var a=e.alternate;a!==null&&(a.lanes|=t),Ku(e.return,t,l)}function Ds(e,t,l,a,n){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:a,tail:l,tailMode:n}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=a,i.tail=l,i.tailMode=n)}function of(e,t,l){var a=t.pendingProps,n=a.revealOrder,i=a.tail;if(ot(e,t,a.children,l),a=et.current,(a&2)!==0)a=a&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&sf(e,l,t);else if(e.tag===19)sf(e,l,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}a&=1}switch(L(et,a),n){case"forwards":for(l=t.child,n=null;l!==null;)e=l.alternate,e!==null&&Oi(e)===null&&(n=l),l=l.sibling;l=n,l===null?(n=t.child,t.child=null):(n=l.sibling,l.sibling=null),Ds(t,!1,n,l,i);break;case"backwards":for(l=null,n=t.child,t.child=null;n!==null;){if(e=n.alternate,e!==null&&Oi(e)===null){t.child=n;break}e=n.sibling,n.sibling=l,l=n,n=e}Ds(t,!0,l,null,i);break;case"together":Ds(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function ll(e,t,l){if(e!==null&&(t.dependencies=e.dependencies),Al|=t.lanes,(l&t.childLanes)===0)if(e!==null){if(on(e,t,l,!1),(l&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,l=Jt(e,e.pendingProps),t.child=l,l.return=t;e.sibling!==null;)e=e.sibling,l=l.sibling=Jt(e,e.pendingProps),l.return=t;l.sibling=null}return t.child}function zs(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&fi(e)))}function Rm(e,t,l){switch(t.tag){case 3:Ne(t,t.stateNode.containerInfo),gl(t,Ie,e.memoizedState.cache),un();break;case 27:case 5:Dt(t);break;case 4:Ne(t,t.stateNode.containerInfo);break;case 10:gl(t,t.type,t.memoizedProps.value);break;case 13:var a=t.memoizedState;if(a!==null)return a.dehydrated!==null?(vl(t),t.flags|=128,null):(l&t.child.childLanes)!==0?uf(e,t,l):(vl(t),e=ll(e,t,l),e!==null?e.sibling:null);vl(t);break;case 19:var n=(e.flags&128)!==0;if(a=(l&t.childLanes)!==0,a||(on(e,t,l,!1),a=(l&t.childLanes)!==0),n){if(a)return of(e,t,l);t.flags|=128}if(n=t.memoizedState,n!==null&&(n.rendering=null,n.tail=null,n.lastEffect=null),L(et,et.current),a)break;return null;case 22:case 23:return t.lanes=0,ef(e,t,l);case 24:gl(t,Ie,e.memoizedState.cache)}return ll(e,t,l)}function cf(e,t,l){if(e!==null)if(e.memoizedProps!==t.pendingProps)at=!0;else{if(!zs(e,l)&&(t.flags&128)===0)return at=!1,Rm(e,t,l);at=(e.flags&131072)!==0}else at=!1,je&&(t.flags&1048576)!==0&&Bc(t,ri,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var a=t.elementType,n=a._init;if(a=n(a._payload),t.type=a,typeof a=="function")Pu(a)?(e=Jl(a,e),t.tag=1,t=af(null,t,a,e,l)):(t.tag=0,t=Es(null,t,a,e,l));else{if(a!=null){if(n=a.$$typeof,n===de){t.tag=11,t=Wr(null,t,a,e,l);break e}else if(n===J){t.tag=14,t=Fr(null,t,a,e,l);break e}}throw t=Ke(a)||a,Error(c(306,t,""))}}return t;case 0:return Es(e,t,t.type,t.pendingProps,l);case 1:return a=t.type,n=Jl(a,t.pendingProps),af(e,t,a,n,l);case 3:e:{if(Ne(t,t.stateNode.containerInfo),e===null)throw Error(c(387));a=t.pendingProps;var i=t.memoizedState;n=i.element,Iu(e,t),hn(t,a,null,l);var u=t.memoizedState;if(a=u.cache,gl(t,Ie,a),a!==i.cache&&Zu(t,[Ie],l,!0),mn(),a=u.element,i.isDehydrated)if(i={element:a,isDehydrated:!1,cache:u.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=nf(e,t,a,l);break e}else if(a!==n){n=Ut(Error(c(424)),t),sn(n),t=nf(e,t,a,l);break e}else{switch(e=t.stateNode.containerInfo,e.nodeType){case 9:e=e.body;break;default:e=e.nodeName==="HTML"?e.ownerDocument.body:e}for(Ze=Yt(e.firstChild),mt=t,je=!0,Xl=null,Lt=!0,l=qr(t,null,a,l),t.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(un(),a===n){t=ll(e,t,l);break e}ot(e,t,a,l)}t=t.child}return t;case 26:return zi(e,t),e===null?(l=gd(t.type,null,t.pendingProps,null))?t.memoizedState=l:je||(l=t.type,e=t.pendingProps,a=Vi(re.current).createElement(l),a[ft]=t,a[ht]=e,rt(a,l,e),lt(a),t.stateNode=a):t.memoizedState=gd(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return Dt(t),e===null&&je&&(a=t.stateNode=rd(t.type,t.pendingProps,re.current),mt=t,Lt=!0,n=Ze,Ml(t.type)?(ro=n,Ze=Yt(a.firstChild)):Ze=n),ot(e,t,t.pendingProps.children,l),zi(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&je&&((n=a=Ze)&&(a=ih(a,t.type,t.pendingProps,Lt),a!==null?(t.stateNode=a,mt=t,Ze=Yt(a.firstChild),Lt=!1,n=!0):n=!1),n||Ql(t)),Dt(t),n=t.type,i=t.pendingProps,u=e!==null?e.memoizedProps:null,a=i.children,uo(n,i)?a=null:u!==null&&uo(n,u)&&(t.flags|=32),t.memoizedState!==null&&(n=is(e,t,Em,null,null,l),Gn._currentValue=n),zi(e,t),ot(e,t,a,l),t.child;case 6:return e===null&&je&&((e=l=Ze)&&(l=uh(l,t.pendingProps,Lt),l!==null?(t.stateNode=l,mt=t,Ze=null,e=!0):e=!1),e||Ql(t)),null;case 13:return uf(e,t,l);case 4:return Ne(t,t.stateNode.containerInfo),a=t.pendingProps,e===null?t.child=Ta(t,null,a,l):ot(e,t,a,l),t.child;case 11:return Wr(e,t,t.type,t.pendingProps,l);case 7:return ot(e,t,t.pendingProps,l),t.child;case 8:return ot(e,t,t.pendingProps.children,l),t.child;case 12:return ot(e,t,t.pendingProps.children,l),t.child;case 10:return a=t.pendingProps,gl(t,t.type,a.value),ot(e,t,a.children,l),t.child;case 9:return n=t.type._context,a=t.pendingProps.children,Kl(t),n=dt(n),a=a(n),t.flags|=1,ot(e,t,a,l),t.child;case 14:return Fr(e,t,t.type,t.pendingProps,l);case 15:return Ir(e,t,t.type,t.pendingProps,l);case 19:return of(e,t,l);case 31:return a=t.pendingProps,l=t.mode,a={mode:a.mode,children:a.children},e===null?(l=Ri(a,l),l.ref=t.ref,t.child=l,l.return=t,t=l):(l=Jt(e.child,a),l.ref=t.ref,t.child=l,l.return=t,t=l),t;case 22:return ef(e,t,l);case 24:return Kl(t),a=dt(Ie),e===null?(n=$u(),n===null&&(n=Xe,i=ku(),n.pooledCache=i,i.refCount++,i!==null&&(n.pooledCacheLanes|=l),n=i),t.memoizedState={parent:a,cache:n},Fu(t),gl(t,Ie,n)):((e.lanes&l)!==0&&(Iu(e,t),hn(t,null,null,l),mn()),n=e.memoizedState,i=t.memoizedState,n.parent!==a?(n={parent:a,cache:a},t.memoizedState=n,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=n),gl(t,Ie,a)):(a=i.cache,gl(t,Ie,a),a!==n.cache&&Zu(t,[Ie],l,!0))),ot(e,t,t.pendingProps.children,l),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function al(e){e.flags|=4}function rf(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!vd(t)){if(t=Ht.current,t!==null&&((Re&4194048)===Re?Xt!==null:(Re&62914560)!==Re&&(Re&536870912)===0||t!==Xt))throw dn=Wu,Kc;e.flags|=8192}}function Ui(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?Yo():536870912,e.lanes|=t,Da|=t)}function _n(e,t){if(!je)switch(e.tailMode){case"hidden":t=e.tail;for(var l=null;t!==null;)t.alternate!==null&&(l=t),t=t.sibling;l===null?e.tail=null:l.sibling=null;break;case"collapsed":l=e.tail;for(var a=null;l!==null;)l.alternate!==null&&(a=l),l=l.sibling;a===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:a.sibling=null}}function Ve(e){var t=e.alternate!==null&&e.alternate.child===e.child,l=0,a=0;if(t)for(var n=e.child;n!==null;)l|=n.lanes|n.childLanes,a|=n.subtreeFlags&65011712,a|=n.flags&65011712,n.return=e,n=n.sibling;else for(n=e.child;n!==null;)l|=n.lanes|n.childLanes,a|=n.subtreeFlags,a|=n.flags,n.return=e,n=n.sibling;return e.subtreeFlags|=a,e.childLanes=l,t}function Um(e,t,l){var a=t.pendingProps;switch(Xu(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ve(t),null;case 1:return Ve(t),null;case 3:return l=t.stateNode,a=null,e!==null&&(a=e.memoizedState.cache),t.memoizedState.cache!==a&&(t.flags|=2048),It(Ie),st(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(e===null||e.child===null)&&(nn(t)?al(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,Yc())),Ve(t),null;case 26:return l=t.memoizedState,e===null?(al(t),l!==null?(Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217)):l?l!==e.memoizedState?(al(t),Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217):(e.memoizedProps!==a&&al(t),Ve(t),t.flags&=-16777217),null;case 27:M(t),l=re.current;var n=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==a&&al(t);else{if(!a){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}e=ee.current,nn(t)?Pc(t):(e=rd(n,a,l),t.stateNode=e,al(t))}return Ve(t),null;case 5:if(M(t),l=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==a&&al(t);else{if(!a){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}if(e=ee.current,nn(t))Pc(t);else{switch(n=Vi(re.current),e){case 1:e=n.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:e=n.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":e=n.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":e=n.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":e=n.createElement("div"),e.innerHTML=" + diff --git a/chrome-extension/src/background/ai-api-client.ts b/chrome-extension/src/background/ai-api-client.ts index 98c1442f..ed64586d 100644 --- a/chrome-extension/src/background/ai-api-client.ts +++ b/chrome-extension/src/background/ai-api-client.ts @@ -506,4 +506,95 @@ export async function setApiKeyForModel(modelAlias: string, apiKey: string): Pro export async function checkApiKeyAvailability(modelAlias: string): Promise { const apiKey = await getApiKeyForModel(modelAlias); return apiKey !== null && apiKey.length > 0; +} + +/** + * Получает выбранную LLM для промпта с учетом типа промпта и языка + * @param prompt_type - тип промпта (basic_analysis, deep_analysis, etc.) + * @param language - язык промпта (ru, en) + * @param pluginId - ID плагина для получения специфичных настроек + * @returns объект с моделью и API-ключом + */ +export async function getLLMForPrompt( + prompt_type: string, + language: string, + pluginId?: string +): Promise<{ model: ModelAlias; apiKey: string; prompt: string }> { + try { + // Формируем ключ для localStorage: pluginId:prompt_type:language или просто prompt_type:language + const storageKey = pluginId ? `${pluginId}:${prompt_type}:${language}` : `${prompt_type}:${language}`; + + // Пытаемся получить выбранную модель из localStorage + let selectedModel = localStorage.getItem(storageKey) as ModelAlias | null; + + // Если модель не найдена в localStorage, используем дефолт из manifest + if (!selectedModel) { + // Для получения дефолтной модели нужно загрузить manifest плагина + const manifestData = await loadPluginManifest(pluginId); + selectedModel = manifestData?.ai_models?.[prompt_type] as ModelAlias; + + if (!selectedModel) { + throw new Error(`Не найдена модель по умолчанию для промпта ${prompt_type}`); + } + } + + // Проверяем, что модель поддерживается + if (!Object.keys(MODEL_CONFIGS).includes(selectedModel)) { + throw new Error(`Неподдерживаемая модель: ${selectedModel}`); + } + + // Получаем API-ключ (глобальный или плагин-специфичный) + const apiKey = await getApiKeyForModel(selectedModel); + if (!apiKey) { + throw new Error(`API-ключ для модели ${selectedModel} не найден`); + } + + // Получаем промпт из manifest с подстановкой $prompt_{type}_{language} + const manifestData = await loadPluginManifest(pluginId); + const promptKey = `$prompt_${prompt_type}_${language}`; + let prompt = manifestData?.options?.prompts?.[prompt_type]?.[language]?.default; + + if (!prompt) { + throw new Error(`Промпт ${promptKey} не найден в manifest`); + } + + return { + model: selectedModel, + apiKey, + prompt + }; + + } catch (error) { + console.error('[AI Client] Error getting LLM for prompt:', error); + throw error; + } +} + +/** + * Загружает manifest плагина + */ +async function loadPluginManifest(pluginId?: string): Promise { + if (!pluginId) { + // Если pluginId не указан, пытаемся найти manifest в chrome-extension/public/plugins/ + // Для упрощения возвращаем дефолтные значения или null + return null; + } + + try { + // Путь к manifest плагина + const manifestPath = `plugins/${pluginId}/manifest.json`; + + // В контексте расширения manifest может быть загружен через chrome.runtime.getURL + const manifestUrl = chrome.runtime.getURL(manifestPath); + const response = await fetch(manifestUrl); + + if (!response.ok) { + throw new Error(`Не удалось загрузить manifest: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error('[AI Client] Error loading plugin manifest:', error); + return null; + } } \ No newline at end of file diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 803d94a8..d016eb79 100644 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.825", + "version": "1.0.830", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index 15989789..ce0980dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1350", + "version": "1.0.1355", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 70d11345..8f466602 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index b813123c..83873722 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1355", + "version": "0.5.1360", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index e5e3be15..0a559572 100644 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 6539645b..6b520a43 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 2501cf92..2086bc1a 100644 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 06ec0b14..68404ee2 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 806ea319..bce80b53 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 10b2d537..4a78933c 100644 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 16a47690..c585e9f3 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index ef0e7386..88993562 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index c73abfe3..0f81573e 100644 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1376", + "version": "0.5.1381", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 7961a2a7..b368ba48 100644 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 42d1a23f..75270f3a 100644 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 77f8e358..2bae2359 100644 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 9f2e3e47..b9297b95 100644 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 1b573a67..86ea36ff 100644 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 4232ca90..8374f19d 100644 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index a8b28de1..035f9bce 100644 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx new file mode 100644 index 00000000..ad104e74 --- /dev/null +++ b/pages/options/src/components/LLMSelector.tsx @@ -0,0 +1,137 @@ +import React, { useState, useEffect } from 'react'; +import { usePluginSettings } from '../hooks/usePluginSettings'; +import { AIKey } from '../hooks/useAIKeys'; + +interface LLMSelectorProps { + promptType: 'basic_analysis' | 'deep_analysis'; + language: 'ru' | 'en'; + globalAIKeys: AIKey[]; + defaultLLMCurl: string; + hasDefaultLLM: boolean; + onLLMChange: (llm: string, apiKey?: string) => void; +} + +const LLMSelector: React.FC = ({ + promptType, + language, + globalAIKeys, + defaultLLMCurl, + hasDefaultLLM, + onLLMChange, +}) => { + const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings, getAPIKey } = usePluginSettings(); + const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl); + const [apiKey, setApiKey] = useState(''); + + // Загружаем текущие настройки при монтировании + useEffect(() => { + const currentSettings = promptType === 'basic_analysis' + ? settings.basic_analysis[language] + : settings.deep_analysis[language]; + + // Логика: если сохранённое llm есть — использовать его; иначе — если hasDefaultLLM — "Default LLM"; иначе — "" + const savedLLM = currentSettings.llm; + let initialLLM = savedLLM; + + if (!savedLLM) { + if (hasDefaultLLM) { + initialLLM = 'default'; + } else { + initialLLM = ''; + } + } + + setSelectedLLM(initialLLM); + setApiKey(getAPIKey()); + }, [promptType, language, settings, getAPIKey, hasDefaultLLM]); + + // Сохраняем выбор LLM + const handleLLMChange = async (newLLM: string) => { + setSelectedLLM(newLLM); + + const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings; + await updateFn(language, { + llm: newLLM, + custom_prompt: promptType === 'basic_analysis' + ? settings.basic_analysis[language].custom_prompt + : settings.deep_analysis[language].custom_prompt, + }); + + onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined); + }; + + // Сохраняем API ключ + const handleApiKeyChange = async (newApiKey: string) => { + setApiKey(newApiKey); + await settings.saveSettings?.({ + ...settings, + api_keys: { default: newApiKey }, + }); + onLLMChange(selectedLLM, newApiKey); + }; + + // Получаем список опций для селекта + const getLLMOptions = () => { + const options = [ + { value: 'default', label: 'Default LLM' }, + ...globalAIKeys.map(key => ({ + value: key.id, + label: key.name, + })), + ]; + return options; + }; + + return ( +
+ + + + + {selectedLLM === 'default' && ( +
+ + handleApiKeyChange(e.target.value)} + placeholder="Введите API ключ" + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ccc', + borderRadius: '4px', + backgroundColor: 'white', + fontSize: '14px' + }} + /> +
+ )} +
+ ); +}; + +export default LLMSelector; \ No newline at end of file diff --git a/pages/options/src/components/PluginDetails.tsx b/pages/options/src/components/PluginDetails.tsx index 5311908e..e01122fd 100644 --- a/pages/options/src/components/PluginDetails.tsx +++ b/pages/options/src/components/PluginDetails.tsx @@ -3,6 +3,9 @@ import type { Plugin } from '../hooks/usePlugins'; import type { PluginSettings } from '@extension/storage'; import ToggleButton from './ToggleButton'; import LocalErrorBoundary from './LocalErrorBoundary'; +import LLMSelector from './LLMSelector'; +import { useAIKeys, AIKey } from '../hooks/useAIKeys'; +import { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings'; import { useState, useEffect } from 'react'; import type { ReactNode } from 'react'; @@ -49,13 +52,54 @@ interface PromptsEditorProps { onSave: (value: PromptsStructure) => void; locale: 'en' | 'ru'; t: (key: string) => string; // функция перевода + globalAIKeys: AIKey[]; + pluginSettings: PluginSettingsType; } -const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: PromptsEditorProps) => { +const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => { const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis'); const [language, setLanguage] = useState<'ru' | 'en'>('ru'); const [customPrompt, setCustomPrompt] = useState(''); + // Получить дефолтную LLM для конкретного промпта и языка + const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => { + try { + const promptsConfig = manifest?.options?.prompts; + if (!promptsConfig) return 'default'; + + const typePrompts = promptsConfig[type] || {}; + const langPrompts = typePrompts[lang] || {}; + const llmConfig = langPrompts.LLM?.default; + + if (!llmConfig) return 'default'; + + // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro + if (type === 'basic_analysis') { + return 'gemini-flash-lite'; + } else if (type === 'deep_analysis') { + return 'gemini-pro'; + } + + return 'default'; + } catch { + return 'default'; + } + }; + + // Проверить наличие дефолтной LLM для конкретного промпта и языка + const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => { + try { + const promptsConfig = manifest?.options?.prompts; + if (!promptsConfig) return false; + + const typePrompts = promptsConfig[type] || {}; + const langPrompts = typePrompts[lang] || {}; + return !!langPrompts.LLM?.default; + } catch { + return false; + } + }; + // Получаем оригинальный промпт из manifest const getOriginalPrompt = (): string => { try { @@ -167,6 +211,18 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts + {/* LLM Selector для текущего промпта и языка */} + { + console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey); + }} + /> + {/* Textarea */}
@@ -259,6 +315,8 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts const PluginDetails = (props: PluginDetailsProps) => { const { selectedPlugin, locale = 'en', onUpdateSetting } = props; const { t } = useTranslations(locale); + const { aiKeys } = useAIKeys(); + const { settings: pluginSettings } = usePluginSettings(); const [isUpdating, setIsUpdating] = useState(null); const [customSettings, setCustomSettings] = useState | null>(null); @@ -421,6 +479,8 @@ const PluginDetails = (props: PluginDetailsProps) => { onSave={(newValue) => handleSettingChange(key, newValue)} locale={locale} t={t} + globalAIKeys={aiKeys} + pluginSettings={pluginSettings} />
); diff --git a/pages/options/src/components/index.ts b/pages/options/src/components/index.ts index 2c2ed20b..9aaedbec 100644 --- a/pages/options/src/components/index.ts +++ b/pages/options/src/components/index.ts @@ -1,4 +1,5 @@ export { PluginsTab } from './PluginsTab'; export { SettingsTab } from './SettingsTab'; export { default as PluginDetails } from './PluginDetails'; -export { IDELayout } from './IDELayout'; \ No newline at end of file +export { default as LLMSelector } from './LLMSelector'; +export { IDELayout } from './IDELayout'; \ No newline at end of file diff --git a/pages/options/src/hooks/usePluginSettings.ts b/pages/options/src/hooks/usePluginSettings.ts new file mode 100644 index 00000000..2777f93f --- /dev/null +++ b/pages/options/src/hooks/usePluginSettings.ts @@ -0,0 +1,188 @@ +import * as React from 'react'; +import { APIKeyManager } from '../utils/encryption'; + +export interface PluginSettings { + basic_analysis: { + ru: { + llm: string; + custom_prompt: string; + }; + en: { + llm: string; + custom_prompt: string; + }; + }; + deep_analysis: { + ru: { + llm: string; + custom_prompt: string; + }; + en: { + llm: string; + custom_prompt: string; + }; + }; + api_keys: { + default: string; // encrypted + }; +} + +export interface PluginPromptSettings { + llm: string; + custom_prompt: string; +} + +const STORAGE_KEY = 'plugin-ozon-analyzer-settings'; + +const DEFAULT_SETTINGS: PluginSettings = { + basic_analysis: { + ru: { + llm: '', + custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.', + }, + en: { + llm: '', + custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.', + }, + }, + deep_analysis: { + ru: { + llm: '', + custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.', + }, + en: { + llm: '', + custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.', + }, + }, + api_keys: { + default: '', + }, +}; + +export const usePluginSettings = () => { + const [settings, setSettings] = React.useState(DEFAULT_SETTINGS); + const [isLoading, setIsLoading] = React.useState(true); + + React.useEffect(() => { + loadSettings(); + }, []); + + const loadSettings = async () => { + try { + setIsLoading(true); + const result = await chrome.storage.local.get([STORAGE_KEY]); + const storedSettings = result[STORAGE_KEY]; + + if (storedSettings) { + // Расшифровываем API ключи + const decryptedApiKeys = { ...storedSettings.api_keys }; + if (storedSettings.api_keys?.default) { + decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || ''; + } + + setSettings({ + ...DEFAULT_SETTINGS, + ...storedSettings, + api_keys: decryptedApiKeys, + }); + } else { + setSettings(DEFAULT_SETTINGS); + } + } catch (error) { + console.error('Failed to load plugin settings:', error); + setSettings(DEFAULT_SETTINGS); + } finally { + setIsLoading(false); + } + }; + + const saveSettings = async (newSettings: PluginSettings) => { + try { + // Шифруем API ключи перед сохранением + const settingsToSave = { ...newSettings }; + if (newSettings.api_keys?.default) { + await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default); + } else { + await APIKeyManager.removeKey('ozon-analyzer-default'); + } + + // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON + settingsToSave.api_keys = { default: '' }; + + await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave }); + setSettings(newSettings); + } catch (error) { + console.error('Failed to save plugin settings:', error); + throw error; + } + }; + + const updateBasicAnalysisSettings = async ( + language: 'ru' | 'en', + settings: PluginPromptSettings + ) => { + const newSettings = { + ...settings, + basic_analysis: { + ...settings.basic_analysis, + [language]: settings, + }, + }; + await saveSettings(newSettings); + }; + + const updateDeepAnalysisSettings = async ( + language: 'ru' | 'en', + settings: PluginPromptSettings + ) => { + const newSettings = { + ...settings, + deep_analysis: { + ...settings.deep_analysis, + [language]: settings, + }, + }; + await saveSettings(newSettings); + }; + + const updateAPIKey = async (key: string) => { + const newSettings = { + ...settings, + api_keys: { + default: key, + }, + }; + await saveSettings(newSettings); + }; + + const getBasicAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => { + return settings.basic_analysis[language]; + }; + + const getDeepAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => { + return settings.deep_analysis[language]; + }; + + const getAPIKey = (): string => { + return settings.api_keys?.default || ''; + }; + + const resetToDefaults = async () => { + await saveSettings(DEFAULT_SETTINGS); + }; + + return { + settings, + isLoading, + updateBasicAnalysisSettings, + updateDeepAnalysisSettings, + updateAPIKey, + getBasicAnalysisSettings, + getDeepAnalysisSettings, + getAPIKey, + resetToDefaults, + saveSettings, + loadSettings, + }; +}; \ No newline at end of file diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 27ac3e8d..dfb74fb4 100644 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1368", + "version": "0.5.1373", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 8c880030..135e5c8f 100644 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1373", + "version": "0.27.1378", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 92c25d0e..37ca6da7 100644 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1375", + "version": "0.27.1380", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 6604ccaa..b37e0d35 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@extension/e2e", - "version": "0.5.1368", + "version": "0.5.1373", "description": "E2e tests configuration boilerplate", "private": true, "sideEffects": false, From 2197f8115a92eea684dca1c885d84a189b2ef903 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Tue, 14 Oct 2025 15:00:41 +0500 Subject: [PATCH 05/15] =?UTF-8?q?=D1=85=D0=BE=D1=82=D1=8F=20=D0=B1=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=BF=D0=BE=D0=BB=D0=BE=D0=BC=D0=B0=D0=BB=20?= =?UTF-8?q?=D1=82=D0=BE=20=D1=87=D1=82=D0=BE=20=D0=B1=D1=8B=D0=BB=D0=BE=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=B3=D0=B8=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .roo/mcp.json | 14 +-- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- .../plugins/ozon-analyzer/mcp_server.py | 119 +++++++++--------- chrome-extension/public/pyodide/package.json | 2 +- chrome-extension/src/background/index.ts | 76 +++++++++-- chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- tests/e2e/package.json | 2 +- 30 files changed, 157 insertions(+), 108 deletions(-) diff --git a/.roo/mcp.json b/.roo/mcp.json index 67390cea..70011302 100644 --- a/.roo/mcp.json +++ b/.roo/mcp.json @@ -1,15 +1,3 @@ { - "mcpServers": { - "context7": { - "command": "npx", - "args": [ - "-y", - "@upstash/context7-mcp" - ], - "env": { - "DEFAULT_MINIMUM_TOKENS": "" - }, - "alwaysAllow": [] - } - } + "mcpServers": {} } \ No newline at end of file diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 44b16913..4cfa638a 100644 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1355", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1355", + "version": "1.0.1356", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1356", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index b447c604..829504bb 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py index ea3558e6..4514d0c6 100644 --- a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py +++ b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py @@ -327,7 +327,7 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s else: console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") # Fallback 2: встроенные промпты по умолчанию - # default_value = _get_builtin_default_prompt(prompt_type, lang) + default_value = _get_builtin_default_prompt(prompt_type, lang) if default_value: prompts[prompt_type][lang] = default_value console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") @@ -406,55 +406,6 @@ def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[s 'deep_analysis': {'ru': '', 'en': ''} } - prompts = { - 'basic_analysis': {'ru': '', 'en': ''}, - 'deep_analysis': {'ru': '', 'en': ''} - } - - # Получаем manifest из plugin_settings или globals для fallback значений - if plugin_settings and isinstance(plugin_settings, dict): - manifest = safe_dict_get(plugin_settings, 'manifest', get_pyodide_var('manifest', {})) - else: - manifest = get_pyodide_var('manifest', {}) - manifest_prompts = safe_dict_get(manifest, 'options.prompts', {}) - - for prompt_type in ['basic_analysis', 'deep_analysis']: - for lang in ['ru', 'en']: - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - - custom_value = safe_dict_get(prompt_type_data, lang, '') - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - # Fallback на manifest.json - lang_data = safe_dict_get(manifest_prompts, f"{prompt_type}.{lang}", {}) - - manifest_value = safe_dict_get(lang_data, "default", "") - prompts[prompt_type][lang] = manifest_value - console_log(f"ℹ️ Используем промпт по умолчанию: {prompt_type}.{lang}") - - console_log(f"🔍 Диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Final prompts structure: {prompts}") - console_log(f"📋 Загружено промптов: {len([p for pt in prompts.values() for p in pt.values() if p])} кастомных") - return prompts - - except Exception as e: - console_log(f"❌ Ошибка загрузки промптов: {str(e)}") - console_log(f"🔍 Диагностика ошибки:") - console_log(f" Plugin settings: {plugin_settings}") - console_log(f" Plugin settings type: {type(plugin_settings)}") - # Критический fallback - возвращаем пустые промпты - return { - 'basic_analysis': {'ru': '', 'en': ''}, - 'deep_analysis': {'ru': '', 'en': ''} - } - def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: """ @@ -2660,6 +2611,44 @@ async def _analyze_product_async(description: str, composition: str, categories: return analysis_result, analogs +def get_selected_llm_config(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[str, str]: + """ + Получает выбранные LLM модели для каждого типа анализа с fallback на manifest.json. + + Args: + plugin_settings: Настройки плагина из Pyodide globals + + Returns: + Словарь с выбранными моделями: {'basic_analysis': 'model_name', 'deep_analysis': 'model_name'} + """ + console_log("[LLM_CONFIG] ===== ПОЛУЧЕНИЕ КОНФИГУРАЦИИ LLM =====") + + # Получить manifest из plugin_settings или globals + manifest = None + if plugin_settings and isinstance(plugin_settings, dict): + manifest = safe_dict_get(plugin_settings, 'manifest', None) + if manifest is None: + manifest = get_pyodide_var('manifest', {}) + + console_log(f"[LLM_CONFIG] Manifest получен: {manifest is not None}") + + # Получить ai_models из manifest + ai_models = safe_dict_get(manifest, 'ai_models', {}) + console_log(f"[LLM_CONFIG] AI models из manifest: {ai_models}") + + # Настроить модели для каждого типа анализа с fallback + result = { + 'basic_analysis': safe_dict_get(ai_models, 'compliance_check', + safe_dict_get(ai_models, 'basic_analysis', 'gemini-flash-lite')), + 'deep_analysis': safe_dict_get(ai_models, 'deep_analysis', 'gemini-pro') + } + + console_log(f"[LLM_CONFIG] Финальная конфигурация LLM: {result}") + console_log("[LLM_CONFIG] ===== КОНЕЦ ПОЛУЧЕНИЯ КОНФИГУРАЦИИ LLM =====") + + return result + + def get_safe_content_language(plugin_settings: Dict[str, Any]) -> str: """ Безопасная функция определения языка контента для сообщений чата. @@ -3721,7 +3710,6 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: if not description or not composition: return { "status": "error", "message": "Описание или состав не были переданы для глубокого анализа."} - # Получить pluginSettings из pyodide globals plugin_settings = get_pyodide_var('pluginSettings', {}) @@ -3743,6 +3731,13 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: # console_log(f"[LANGUAGE] Язык контента определен: '{content_language}'") # console_log(f"[LANGUAGE] Финальный результат get_safe_content_language: '{content_language}'") + # Получить выбранную LLM для deep_analysis + llm_config = get_selected_llm_config(plugin_settings) + selected_model = llm_config.get('deep_analysis', 'deep_analysis') + + console_log(f"[LLM_SELECTION] Выбрана модель для deep_analysis: {selected_model}") + + # Получить пользовательские промпты try: @@ -3801,10 +3796,8 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: console_log(f"⚠️ Используем fallback промпт deep_analysis для языка {content_language}") try: - # "deep_analysis" - это псевдоним из `manifest.json` этого плагина. - # Платформа сама определит, какую реальную модель (например, gemini-pro) - # использовать, и подставит соответствующий API-ключ. - result = await ozon_analyzer_server._call_ai_model("deep_analysis", prompt) + # Используем выбранную модель вместо хардкода "deep_analysis" + result = await ozon_analyzer_server._call_ai_model(selected_model, prompt) # Проверка типа данных от AI в perform_deep_analysis if not isinstance(result, str): @@ -3823,7 +3816,11 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: chat_message(f"❌ Ошибка глубокого анализа: {str(e)[:100]}...") else: # English chat_message(f"❌ Deep analysis error: {str(e)[:100]}...") - return { "status": "error", "message": f"Ошибка глубокого анализа: {str(e)}" } + return { + "status": "error", + "message": f"Deep analysis error: {str(e)}" if content_language != "ru" + else f"Ошибка глубокого анализа: {str(e)}" + } finally: # [DIAGNOSTIC] ЛОГИРОВАНИЕ В КОНЦЕ perform_deep_analysis console_log("[DIAGNOSTIC] >>>>>> perform_deep_analysis FUNCTION ENDING <<<<<<") @@ -4383,8 +4380,14 @@ async def _analyze_composition_vs_description(description: str, composition: str # console_log(f"[DIAGNOSTIC] Длина промпта: {safe_len(prompt)} символов") # console_log(f"[DIAGNOSTIC] Промпт начинается: {prompt[:100]}...") # console_log("[DIAGNOSTIC] ===== НАЧАЛО ВЫЗОВА _call_ai_model() =====") - # Используем псевдоним "compliance_check" для проверки соответствия описания и состава - result_str = await ozon_analyzer_server._call_ai_model("compliance_check", prompt) + # Получить выбранную LLM для basic_analysis + llm_config = get_selected_llm_config(plugin_settings) + selected_model = llm_config.get('basic_analysis', 'compliance_check') + + console_log(f"[LLM_SELECTION] Выбрана модель для basic_analysis: {selected_model}") + + # Используем выбранную модель вместо хардкода "compliance_check" + result_str = await ozon_analyzer_server._call_ai_model(selected_model, prompt) console_log("[DIAGNOSTIC] ===== КОНЕЦ ВЫЗОВА _call_ai_model() =====") # [DIAGNOSTIC] ===== РЕЗУЛЬТАТ ВЫЗОВА AI МОДЕЛИ ===== diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index 37ca6da7..624b4ace 100644 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1380", + "version": "0.27.1381", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index 5a4c2234..390906cb 100644 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -1466,9 +1466,64 @@ chrome.runtime.onMessage.addListener( const pluginSettings = await getPluginSettings(msg.pluginId, manifestDefaults, customSettingsKeys); - console.log('[BACKGROUND] ✅ Настройки плагина получены из chrome.storage.local:', pluginSettings); - console.log('[BACKGROUND] 🔑 Значение response_language:', pluginSettings?.response_language); - console.log('[BACKGROUND] 📊 Все полученные настройки плагина:', JSON.stringify(pluginSettings, null, 2)); + // Загрузка LLM настроек и API ключей для ozon-analyzer плагина + let enrichedPluginSettings = { ...pluginSettings }; + if (msg.pluginId === 'ozon-analyzer') { + try { + console.log('[BACKGROUND] 🔧 Загружаем LLM настройки и API ключи для ozon-analyzer'); + + // Загружаем LLM настройки из специального хранилища + const llmSettingsResult = await chrome.storage.local.get(['plugin-ozon-analyzer-settings']); + const llmSettings = llmSettingsResult['plugin-ozon-analyzer-settings']; + + if (llmSettings) { + console.log('[BACKGROUND] ✅ Найдены LLM настройки:', JSON.stringify(llmSettings, null, 2)); + + // Добавляем llm_settings в pluginSettings + enrichedPluginSettings.llm_settings = { + basic_analysis: llmSettings.basic_analysis, + deep_analysis: llmSettings.deep_analysis + }; + + // Добавляем api_keys в pluginSettings + enrichedPluginSettings.api_keys = llmSettings.api_keys; + + console.log('[BACKGROUND] ✅ LLM настройки добавлены в pluginSettings'); + } else { + console.log('[BACKGROUND] ⚠️ LLM настройки не найдены в хранилище'); + // Используем дефолтные значения если настройки не найдены + enrichedPluginSettings.llm_settings = { + basic_analysis: { + ru: { llm: '', custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.' }, + en: { llm: '', custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.' } + }, + deep_analysis: { + ru: { llm: '', custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.' }, + en: { llm: '', custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.' } + } + }; + enrichedPluginSettings.api_keys = { default: '' }; + } + } catch (llmError) { + console.error('[BACKGROUND] ❌ Ошибка загрузки LLM настроек:', llmError); + // Используем дефолтные значения при ошибке + enrichedPluginSettings.llm_settings = { + basic_analysis: { + ru: { llm: '', custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.' }, + en: { llm: '', custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.' } + }, + deep_analysis: { + ru: { llm: '', custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.' }, + en: { llm: '', custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.' } + } + }; + enrichedPluginSettings.api_keys = { default: '' }; + } + } + + console.log('[BACKGROUND] ✅ Настройки плагина получены из chrome.storage.local:', enrichedPluginSettings); + console.log('[BACKGROUND] 🔑 Значение response_language:', enrichedPluginSettings?.response_language); + console.log('[BACKGROUND] 📊 Все полученные настройки плагина:', JSON.stringify(enrichedPluginSettings, null, 2)); if (!pluginSettings.enabled) { console.log('[background][RUN_WORKFLOW][INFO] Plugin disabled'); sendResponse({ error: 'Плагин отключен' }); @@ -1583,13 +1638,16 @@ chrome.runtime.onMessage.addListener( const requestId = msg.requestId || `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const transferId = `${requestId}_html_${Date.now()}`; + // Используем enrichedPluginSettings (с LLM настройками) вместо обычных pluginSettings + const settingsToSend = enrichedPluginSettings; + if (htmlTransmissionMode === 'direct') { // ПРЯМАЯ ПЕРЕДАЧА HTML console.log('[background][RUN_WORKFLOW] 📨 Using DIRECT HTML transmission'); console.log('[background][RUN_WORKFLOW] HTML size:', pageHtml.length, 'chars'); try { - await sendHtmlDirectly(msg.pluginId, pageKey, pageHtml, requestId, transferId, pluginSettings); + await sendHtmlDirectly(msg.pluginId, pageKey, pageHtml, requestId, transferId, settingsToSend); console.log('[background][RUN_WORKFLOW] ✅ Direct transmission completed'); } catch (directError) { console.log('[background][RUN_WORKFLOW] ❌ Direct transmission failed, switching to chunked mode'); @@ -1607,7 +1665,7 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked fallback'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', pluginSettings); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', settingsToSend); const transferState = { chunks: chunkingResult.chunks, @@ -1620,7 +1678,7 @@ chrome.runtime.onMessage.addListener( totalSize: chunkingResult.totalSize, timestamp: Date.now(), fallbackFromDirect: true, - pluginSettings: pluginSettings // Добавлено: передаем pluginSettings + pluginSettings: settingsToSend // Добавлено: передаем pluginSettings }, resolve: () => { console.log(`[CHUNKING] Transfer ${transferId} completed successfully`); @@ -1649,8 +1707,8 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked передачи'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', pluginSettings); - console.log('[BACKGROUND] 🔑 Response language в transfer metadata:', pluginSettings?.response_language); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', settingsToSend); + console.log('[BACKGROUND] 🔑 Response language в transfer metadata:', settingsToSend?.response_language); const transferState = { chunks: chunkingResult.chunks, @@ -1662,7 +1720,7 @@ chrome.runtime.onMessage.addListener( requestId: requestId, totalSize: chunkingResult.totalSize, timestamp: Date.now(), - pluginSettings: pluginSettings // Добавлено: передаем pluginSettings + pluginSettings: settingsToSend // Добавлено: передаем pluginSettings }, resolve: () => { console.log(`[CHUNKING] Transfer ${transferId} completed successfully`); diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index d016eb79..6ee384f5 100644 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.830", + "version": "1.0.831", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index ce0980dc..39735f2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1355", + "version": "1.0.1356", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 8f466602..06eec708 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 83873722..e1da12d5 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1360", + "version": "0.5.1361", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 0a559572..20f8b4f2 100644 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 6b520a43..61d54cb6 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 2086bc1a..f2a4cfdf 100644 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 68404ee2..0edf947f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index bce80b53..ba563a6e 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 4a78933c..62639cbb 100644 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index c585e9f3..57142fd6 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 88993562..f612158c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 0f81573e..648f39ab 100644 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1381", + "version": "0.5.1382", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index b368ba48..62b1874c 100644 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 75270f3a..13048307 100644 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 2bae2359..ac810e94 100644 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index b9297b95..dd5d05ba 100644 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 86ea36ff..f28fb8ec 100644 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 8374f19d..de8005ec 100644 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 035f9bce..60b49e35 100644 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index dfb74fb4..2b3f5009 100644 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1373", + "version": "0.5.1374", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 135e5c8f..9daab841 100644 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1378", + "version": "0.27.1379", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 37ca6da7..624b4ace 100644 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1380", + "version": "0.27.1381", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/tests/e2e/package.json b/tests/e2e/package.json index b37e0d35..adab07dc 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@extension/e2e", - "version": "0.5.1373", + "version": "0.5.1374", "description": "E2e tests configuration boilerplate", "private": true, "sideEffects": false, From c8b17315e75fbfa1d1240000af66f36e9bbaeb4d Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Tue, 14 Oct 2025 16:43:12 +0500 Subject: [PATCH 06/15] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=20=D0=B2=D0=B2=D0=BE=D0=B4=20=D0=90=D0=9F?= =?UTF-8?q?=D0=98-=D0=BA=D0=BB=D1=8E=D1=87=D0=B0=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=B1=D0=B8=D0=BD=D0=B0=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BC=D0=BF=D1=82/=D1=8F=D0=B7=D1=8B=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- chrome-extension/public/pyodide/package.json | 2 +- .../{index-C-Ob5p6n.js => index-BGEZNpCW.js} | 22 +++++----- .../side-panel/assets/index-BGEZNpCW.js.map | 1 + .../side-panel/assets/index-C-Ob5p6n.js.map | 1 - chrome-extension/public/side-panel/index.html | 2 +- chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/options/src/components/LLMSelector.tsx | 41 +++++++++++++------ pages/options/src/hooks/usePluginSettings.ts | 20 +++++++++ pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- tests/e2e/package.json | 2 +- 33 files changed, 90 insertions(+), 53 deletions(-) rename chrome-extension/public/side-panel/assets/{index-C-Ob5p6n.js => index-BGEZNpCW.js} (53%) create mode 100644 chrome-extension/public/side-panel/assets/index-BGEZNpCW.js.map delete mode 100644 chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js.map diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 4cfa638a..6acbc16d 100644 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1356", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1356", + "version": "1.0.1367", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1367", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 829504bb..97095d2e 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index 624b4ace..9c27115a 100644 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1381", + "version": "0.27.1392", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js b/chrome-extension/public/side-panel/assets/index-BGEZNpCW.js similarity index 53% rename from chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js rename to chrome-extension/public/side-panel/assets/index-BGEZNpCW.js index 0a3263c7..ec95f46a 100644 --- a/chrome-extension/public/side-panel/assets/index-C-Ob5p6n.js +++ b/chrome-extension/public/side-panel/assets/index-BGEZNpCW.js @@ -1,4 +1,4 @@ -(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))c(S);new MutationObserver(S=>{for(const A of S)if(A.type==="childList")for(const z of A.addedNodes)z.tagName==="LINK"&&z.rel==="modulepreload"&&c(z)}).observe(document,{childList:!0,subtree:!0});function d(S){const A={};return S.integrity&&(A.integrity=S.integrity),S.referrerPolicy&&(A.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?A.credentials="include":S.crossOrigin==="anonymous"?A.credentials="omit":A.credentials="same-origin",A}function c(S){if(S.ep)return;S.ep=!0;const A=d(S);fetch(S.href,A)}})();var ss=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Py(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Eo={exports:{}},Yn={};/** +(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))c(S);new MutationObserver(S=>{for(const T of S)if(T.type==="childList")for(const z of T.addedNodes)z.tagName==="LINK"&&z.rel==="modulepreload"&&c(z)}).observe(document,{childList:!0,subtree:!0});function d(S){const T={};return S.integrity&&(T.integrity=S.integrity),S.referrerPolicy&&(T.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?T.credentials="include":S.crossOrigin==="anonymous"?T.credentials="omit":T.credentials="same-origin",T}function c(S){if(S.ep)return;S.ep=!0;const T=d(S);fetch(S.href,T)}})();var ss=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Py(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Eo={exports:{}},Xn={};/** * @license React * react-jsx-runtime.production.js * @@ -6,7 +6,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Nd;function Gy(){if(Nd)return Yn;Nd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function d(c,S,A){var z=null;if(A!==void 0&&(z=""+A),S.key!==void 0&&(z=""+S.key),"key"in S){A={};for(var ee in S)ee!=="key"&&(A[ee]=S[ee])}else A=S;return S=A.ref,{$$typeof:u,type:c,key:z,ref:S!==void 0?S:null,props:A}}return Yn.Fragment=r,Yn.jsx=d,Yn.jsxs=d,Yn}var jd;function Ky(){return jd||(jd=1,Eo.exports=Gy()),Eo.exports}var y=Ky();const By=({isDraftSaved:u,isDraftLoading:r,draftError:d,messageLength:c,minLength:S,maxLength:A})=>{const z=()=>r?y.jsx("div",{className:"draft-status-loader"}):d?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):u?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cr?"Загрузка черновика...":d||(u?"Черновик сохранен":c>0&&c=S&&c<=A?"Черновик будет сохранен автоматически":c>A?"Превышен лимит символов":""),R=()=>r?"draft-status loading":d?"draft-status error":u?"draft-status saved":c>0&&c=S&&c<=A?"draft-status ready":c>A?"draft-status error":"draft-status";return c===0&&!r&&!d?null:y.jsxs("div",{className:R(),children:[z(),y.jsx("span",{className:"draft-status-text",children:ee()})]})};var Ao={exports:{}},Se={};/** + */var Nd;function Gy(){if(Nd)return Xn;Nd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function d(c,S,T){var z=null;if(T!==void 0&&(z=""+T),S.key!==void 0&&(z=""+S.key),"key"in S){T={};for(var ee in S)ee!=="key"&&(T[ee]=S[ee])}else T=S;return S=T.ref,{$$typeof:u,type:c,key:z,ref:S!==void 0?S:null,props:T}}return Xn.Fragment=r,Xn.jsx=d,Xn.jsxs=d,Xn}var jd;function Ky(){return jd||(jd=1,Eo.exports=Gy()),Eo.exports}var y=Ky();const By=({isDraftSaved:u,isDraftLoading:r,draftError:d,messageLength:c,minLength:S,maxLength:T})=>{const z=()=>r?y.jsx("div",{className:"draft-status-loader"}):d?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):u?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cr?"Загрузка черновика...":d||(u?"Черновик сохранен":c>0&&c=S&&c<=T?"Черновик будет сохранен автоматически":c>T?"Превышен лимит символов":""),R=()=>r?"draft-status loading":d?"draft-status error":u?"draft-status saved":c>0&&c=S&&c<=T?"draft-status ready":c>T?"draft-status error":"draft-status";return c===0&&!r&&!d?null:y.jsxs("div",{className:R(),children:[z(),y.jsx("span",{className:"draft-status-text",children:ee()})]})};var Ao={exports:{}},Se={};/** * @license React * react.production.js * @@ -14,7 +14,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Hd;function Ly(){if(Hd)return Se;Hd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),A=Symbol.for("react.consumer"),z=Symbol.for("react.context"),ee=Symbol.for("react.forward_ref"),R=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),W=Symbol.iterator;function $(g){return g===null||typeof g!="object"?null:(g=W&&g[W]||g["@@iterator"],typeof g=="function"?g:null)}var I={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},F=Object.assign,ie={};function ae(g,N,te){this.props=g,this.context=N,this.refs=ie,this.updater=te||I}ae.prototype.isReactComponent={},ae.prototype.setState=function(g,N){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,N,"setState")},ae.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function T(){}T.prototype=ae.prototype;function q(g,N,te){this.props=g,this.context=N,this.refs=ie,this.updater=te||I}var B=q.prototype=new T;B.constructor=q,F(B,ae.prototype),B.isPureReactComponent=!0;var K=Array.isArray,Q={H:null,A:null,T:null,S:null,V:null},ce=Object.prototype.hasOwnProperty;function w(g,N,te,Z,ne,ye){return te=ye.ref,{$$typeof:u,type:g,key:N,ref:te!==void 0?te:null,props:ye}}function D(g,N){return w(g.type,N,void 0,void 0,void 0,g.props)}function le(g){return typeof g=="object"&&g!==null&&g.$$typeof===u}function ve(g){var N={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(te){return N[te]})}var De=/\/+/g;function he(g,N){return typeof g=="object"&&g!==null&&g.key!=null?ve(""+g.key):N.toString(36)}function Ne(){}function lt(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(Ne,Ne):(g.status="pending",g.then(function(N){g.status==="pending"&&(g.status="fulfilled",g.value=N)},function(N){g.status==="pending"&&(g.status="rejected",g.reason=N)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function we(g,N,te,Z,ne){var ye=typeof g;(ye==="undefined"||ye==="boolean")&&(g=null);var de=!1;if(g===null)de=!0;else switch(ye){case"bigint":case"string":case"number":de=!0;break;case"object":switch(g.$$typeof){case u:case r:de=!0;break;case O:return de=g._init,we(de(g._payload),N,te,Z,ne)}}if(de)return ne=ne(g),de=Z===""?"."+he(g,0):Z,K(ne)?(te="",de!=null&&(te=de.replace(De,"$&/")+"/"),we(ne,N,te,"",function(ut){return ut})):ne!=null&&(le(ne)&&(ne=D(ne,te+(ne.key==null||g&&g.key===ne.key?"":(""+ne.key).replace(De,"$&/")+"/")+de)),N.push(ne)),1;de=0;var ze=Z===""?".":Z+":";if(K(g))for(var je=0;je>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},Yy="Platform Settings",Xy="Available plugins",ky="AI API Keys",Qy="Free",Vy="Custom API keys",Zy="+ Add new key",Jy="Enter API key",$y="Enter key name",Wy="Save all keys",Fy="Test connections",Iy="General Settings",eh="Automatic plugin updates",th="Show notifications",lh="Interface theme:",ah="Light",nh="Dark",ih="System",sh="Security",uh="Verify plugin signatures",oh="Isolated execution mode",ch="Performance",rh="Maximum number of active plugins:",fh="Cache plugin data",dh="Plugin Details",gh={options:qy,options_settings_title:Yy,options_plugins_title:Xy,options_settings_aiKeys_title:ky,options_settings_aiKeys_badges_free:Qy,options_settings_aiKeys_customKeys_title:Vy,options_settings_aiKeys_customKeys_addButton:Zy,options_settings_aiKeys_customKeys_keyPlaceholder:Jy,options_settings_aiKeys_customKeys_namePlaceholder:$y,options_settings_aiKeys_actions_save:Wy,options_settings_aiKeys_actions_test:Fy,options_settings_general_title:Iy,options_settings_general_autoUpdate:eh,options_settings_general_showNotifications:th,options_settings_general_theme:lh,options_settings_general_theme_light:ah,options_settings_general_theme_dark:nh,options_settings_general_theme_system:ih,options_settings_security_title:sh,options_settings_security_checkSignatures:uh,options_settings_security_isolatedMode:oh,options_settings_performance_title:ch,options_settings_performance_maxPlugins:rh,options_settings_performance_cacheData:fh,options_plugins_details_title:dh},mh={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",basic_analysis:"Базовый",deep_analysis:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},yh="Настройки Платформы",hh="Доступные плагины",ph="API Ключи нейросетей",vh="Бесплатный",bh="Пользовательские API ключи",Sh="+ Добавить новый ключ",_h="Введите API ключ",xh="Введите название ключа",Eh="Сохранить все ключи",Ah="Тестировать подключения",Th="Общие настройки",Ch="Автоматическое обновление плагинов",Mh="Показывать уведомления",Oh="Тема интерфейса:",Dh="Светлая",zh="Тёмная",Rh="Системная",wh="Безопасность",Uh="Проверять подписи плагинов",Nh="Изолированный режим выполнения",jh="Производительность",Hh="Максимальное количество активных плагинов:",Ph="Кэширование данных плагинов",Gh="Детали плагина",Kh={options:mh,options_settings_title:yh,options_plugins_title:hh,options_settings_aiKeys_title:ph,options_settings_aiKeys_badges_free:vh,options_settings_aiKeys_customKeys_title:bh,options_settings_aiKeys_customKeys_addButton:Sh,options_settings_aiKeys_customKeys_keyPlaceholder:_h,options_settings_aiKeys_customKeys_namePlaceholder:xh,options_settings_aiKeys_actions_save:Eh,options_settings_aiKeys_actions_test:Ah,options_settings_general_title:Th,options_settings_general_autoUpdate:Ch,options_settings_general_showNotifications:Mh,options_settings_general_theme:Oh,options_settings_general_theme_light:Dh,options_settings_general_theme_dark:zh,options_settings_general_theme_system:Rh,options_settings_security_title:wh,options_settings_security_checkSignatures:Uh,options_settings_security_isolatedMode:Nh,options_settings_performance_title:jh,options_settings_performance_maxPlugins:Hh,options_settings_performance_cacheData:Ph,options_plugins_details_title:Gh},Gd={en:gh,ru:Kh},ng=(u="en")=>({t:V.useMemo(()=>{const d=Gd[u]||{},c=(S,A)=>A.split(".").reduce((z,ee)=>z&&z[ee]?z[ee]:void 0,S);return S=>{const A=c(d,S);return A!==void 0?A:S}},[u,Gd]),locale:u}),To=({checked:u,onChange:r,disabled:d,label:c,iconOn:S,iconOff:A})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:d?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:u,disabled:d,onChange:z=>r(z.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:u?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:u?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:u?S:A})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),Bh=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),Lh=({children:u})=>{const[r,d]=rs.useState(null),c=rs.useCallback(()=>d(null),[]);if(r)return y.jsx(Bh,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return d(S),null}};class Co{static ALGORITHM="AES-GCM";static KEY_LENGTH=256;static IV_LENGTH=12;static async getEncryptionKey(){try{const r=await chrome.storage.local.get(["encryptionKey"]);if(r.encryptionKey){const A=new Uint8Array(r.encryptionKey);return await crypto.subtle.importKey("raw",A,this.ALGORITHM,!1,["encrypt","decrypt"])}const d=await crypto.subtle.generateKey({name:this.ALGORITHM,length:this.KEY_LENGTH},!0,["encrypt","decrypt"]),c=await crypto.subtle.exportKey("raw",d),S=new Uint8Array(c);return await chrome.storage.local.set({encryptionKey:Array.from(S)}),d}catch(r){throw console.error("Failed to get/create encryption key:",r),new Error("Не удалось инициализировать шифрование")}}static async encrypt(r){try{const d=await this.getEncryptionKey(),c=crypto.getRandomValues(new Uint8Array(this.IV_LENGTH)),S=new TextEncoder().encode(r),A=await crypto.subtle.encrypt({name:this.ALGORITHM,iv:c},d,S),z=new Uint8Array(A),ee=new Uint8Array(c.length+z.length);return ee.set(c),ee.set(z,c.length),btoa(String.fromCharCode(...ee))}catch(d){throw console.error("Encryption failed:",d),new Error("Ошибка шифрования")}}static async decrypt(r){try{const d=await this.getEncryptionKey(),c=new Uint8Array(atob(r).split("").map(ee=>ee.charCodeAt(0))),S=c.slice(0,this.IV_LENGTH),A=c.slice(this.IV_LENGTH),z=await crypto.subtle.decrypt({name:this.ALGORITHM,iv:S},d,A);return new TextDecoder().decode(z)}catch(d){throw console.error("Decryption failed:",d),new Error("Ошибка расшифрования")}}static validateAPIKey(r){return!r||typeof r!="string"?{isValid:!1,error:"Ключ не может быть пустым"}:r.length<10?{isValid:!1,error:"Ключ слишком короткий"}:r.length>200?{isValid:!1,error:"Ключ слишком длинный"}:/[<>\"'&]/.test(r)?{isValid:!1,error:"Ключ содержит недопустимые символы"}:{isValid:!0}}}class St{static async saveEncryptedKey(r,d){try{const c=Co.validateAPIKey(d);if(!c.isValid)throw new Error(c.error);const S=await Co.encrypt(d),A=await this.getAllEncryptedKeys();A[r]=S,await chrome.storage.local.set({encryptedApiKeys:A})}catch(c){throw console.error("Failed to save encrypted API key:",c),c}}static async getDecryptedKey(r){try{const c=(await this.getAllEncryptedKeys())[r];return c?await Co.decrypt(c):null}catch(d){return console.error("Failed to get decrypted API key:",d),null}}static async removeKey(r){try{const d=await this.getAllEncryptedKeys();delete d[r],await chrome.storage.local.set({encryptedApiKeys:d})}catch(d){throw console.error("Failed to remove API key:",d),d}}static async getAllEncryptedKeys(){try{return(await chrome.storage.local.get(["encryptedApiKeys"])).encryptedApiKeys||{}}catch(r){return console.error("Failed to get encrypted keys:",r),{}}}static async getAllKeyIds(){try{const r=await this.getAllEncryptedKeys();return Object.keys(r)}catch(r){return console.error("Failed to get key IDs:",r),[]}}static async keyExists(r){try{const d=await this.getAllEncryptedKeys();return r in d}catch(d){return console.error("Failed to check key existence:",d),!1}}}const Mo="plugin-ozon-analyzer-settings",Xn={basic_analysis:{ru:{llm:"",custom_prompt:"Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки."},en:{llm:"",custom_prompt:"Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages."}},deep_analysis:{ru:{llm:"",custom_prompt:"Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению."},en:{llm:"",custom_prompt:"Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations."}},api_keys:{default:""}},ig=()=>{const[u,r]=V.useState(Xn),[d,c]=V.useState(!0);V.useEffect(()=>{S()},[]);const S=async()=>{try{c(!0);const F=(await chrome.storage.local.get([Mo]))[Mo];if(F){const ie={...F.api_keys};F.api_keys?.default&&(ie.default=await St.getDecryptedKey("ozon-analyzer-default")||""),r({...Xn,...F,api_keys:ie})}else r(Xn)}catch(I){console.error("Failed to load plugin settings:",I),r(Xn)}finally{c(!1)}},A=async I=>{try{const F={...I};I.api_keys?.default?await St.saveEncryptedKey("ozon-analyzer-default",I.api_keys.default):await St.removeKey("ozon-analyzer-default"),F.api_keys={default:""},await chrome.storage.local.set({[Mo]:F}),r(I)}catch(F){throw console.error("Failed to save plugin settings:",F),F}};return{settings:u,isLoading:d,updateBasicAnalysisSettings:async(I,F)=>{const ie={...F,basic_analysis:{...F.basic_analysis,[I]:F}};await A(ie)},updateDeepAnalysisSettings:async(I,F)=>{const ie={...F,deep_analysis:{...F.deep_analysis,[I]:F}};await A(ie)},updateAPIKey:async I=>{const F={...u,api_keys:{default:I}};await A(F)},getBasicAnalysisSettings:I=>u.basic_analysis[I],getDeepAnalysisSettings:I=>u.deep_analysis[I],getAPIKey:()=>u.api_keys?.default||"",resetToDefaults:async()=>{await A(Xn)},saveSettings:A,loadSettings:S}},qh=({promptType:u,language:r,globalAIKeys:d,defaultLLMCurl:c,hasDefaultLLM:S,onLLMChange:A})=>{const{settings:z,updateBasicAnalysisSettings:ee,updateDeepAnalysisSettings:R,getAPIKey:v}=ig(),[O,W]=V.useState(c),[$,I]=V.useState("");V.useEffect(()=>{const q=(u==="basic_analysis"?z.basic_analysis[r]:z.deep_analysis[r]).llm;let B=q;q||(S?B="default":B=""),W(B),I(v())},[u,r,z,v,S]);const F=async T=>{W(T),await(u==="basic_analysis"?ee:R)(r,{llm:T,custom_prompt:u==="basic_analysis"?z.basic_analysis[r].custom_prompt:z.deep_analysis[r].custom_prompt}),A(T,T==="default"?$:void 0)},ie=async T=>{I(T),await z.saveSettings?.({...z,api_keys:{default:T}}),A(O,T)},ae=()=>[{value:"default",label:"Default LLM"},...d.map(q=>({value:q.id,label:q.name}))];return y.jsxs("div",{style:{marginBottom:"16px"},children:[y.jsxs("label",{style:{display:"block",marginBottom:"8px",fontWeight:"bold"},children:["Выбор нейросети для ",u==="basic_analysis"?"базового":"глубокого"," анализа:"]}),y.jsx("select",{value:O,onChange:T=>F(T.target.value),placeholder:"Выберите нейросеть",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",marginBottom:"8px"},children:ae().map(T=>y.jsx("option",{value:T.value,children:T.label},T.value))}),O==="default"&&y.jsxs("div",{children:[y.jsx("label",{style:{display:"block",marginBottom:"4px",fontSize:"14px"},children:"API ключ:"}),y.jsx("input",{type:"password",value:$,onChange:T=>ie(T.target.value),placeholder:"Введите API ключ",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})]})},Yh=()=>{const{t:u}=ng("ru"),[r,d]=V.useState([{id:"gemini-flash-lite",name:"Google Gemini Flash Lite - Базовый анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0},{id:"gemini-pro",name:"Gemini 2.5 Pro - Глубокий анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0}]),[c,S]=V.useState([]),A=V.useRef({});V.useEffect(()=>(z(),()=>{Object.values(A.current).forEach(T=>{clearTimeout(T)}),A.current={}}),[]);const z=async()=>{try{console.log("[useAIKeys] Starting to load AI keys...");const q=["gemini-flash-lite","gemini-pro"].map(async Q=>{const ce=await St.getDecryptedKey(Q);return console.log(`[useAIKeys] Loaded key ${Q}:`,ce?"present":"empty"),{keyId:Q,decryptedKey:ce}}),B=await Promise.all(q);console.log("[useAIKeys] Fixed keys results:",B),d(Q=>Q.map(ce=>{const w=B.find(D=>D.keyId===ce.id);return console.log(`[useAIKeys] Setting key ${ce.id}:`,w?.decryptedKey?"configured":"not_configured"),{...ce,key:w?.decryptedKey||"",status:w?.decryptedKey?"configured":"not_configured"}}));const K=await chrome.storage.local.get(["customKeys"]);if(console.log("[useAIKeys] Custom keys metadata:",K.customKeys),K.customKeys){const Q=await Promise.all(K.customKeys.map(async ce=>{const w=await St.getDecryptedKey(ce.id);return console.log(`[useAIKeys] Custom key ${ce.id}:`,w?"present":"empty"),{...ce,key:w||"",status:w?"configured":"not_configured"}}));console.log("[useAIKeys] Setting custom keys:",Q),S(Q)}else console.log("[useAIKeys] No custom keys found"),S([]);console.log("[useAIKeys] AI keys loaded successfully")}catch(T){console.error("Failed to load AI keys:",T),d(q=>q.map(B=>({...B,key:"",status:"not_configured"}))),S([])}};return{aiKeys:r,customKeys:c,saveAIKeys:async()=>{try{console.log("[useAIKeys] Starting to save AI keys..."),console.log("[useAIKeys] Current aiKeys:",r),console.log("[useAIKeys] Current customKeys:",c);const T=r.map(async K=>{K.key?(console.log(`[useAIKeys] Saving fixed key ${K.id}`),await St.saveEncryptedKey(K.id,K.key)):(console.log(`[useAIKeys] Removing fixed key ${K.id}`),await St.removeKey(K.id))});await Promise.all(T);const q=c.map(async K=>{K.key?(console.log(`[useAIKeys] Saving custom key ${K.id}`),await St.saveEncryptedKey(K.id,K.key)):(console.log(`[useAIKeys] Removing custom key ${K.id}`),await St.removeKey(K.id))});await Promise.all(q);const B=c.map(K=>({id:K.id,name:K.name,isFixed:!1,isFree:!1}));console.log("[useAIKeys] Saving custom keys metadata:",B),await chrome.storage.local.set({customKeys:B}),d(K=>K.map(Q=>({...Q,status:Q.key?"configured":"not_configured"}))),S(K=>K.map(Q=>({...Q,status:Q.key?"configured":"not_configured"}))),console.log("[useAIKeys] AI keys saved successfully"),alert(u("options.settings.aiKeys.messages.saved"))}catch(T){console.error("Failed to save AI keys:",T),alert(u("options.settings.aiKeys.messages.saveError"))}},testAIKeys:async()=>{d(T=>T.map(q=>({...q,status:"testing"}))),S(T=>T.map(q=>({...q,status:"testing"}))),setTimeout(()=>{d(T=>T.map(q=>({...q,status:q.key?"configured":"not_configured"}))),S(T=>T.map(q=>({...q,status:q.key?"configured":"not_configured"}))),alert(u("options.settings.aiKeys.messages.testComplete"))},2e3)},addCustomKey:()=>{const T={id:`custom-${Date.now()}`,name:`Пользовательский ключ ${c.length+1}`,key:"",status:"not_configured"};S(q=>[...q,T])},removeCustomKey:async T=>{try{await St.removeKey(T),S(q=>q.filter(B=>B.id!==T))}catch(q){console.error("Failed to remove custom key:",q),S(B=>B.filter(K=>K.id!==T))}},updateKey:(T,q,B=!1)=>{console.log(`[useAIKeys] Updating key ${T} with value:`,q?"present":"empty"),B?S(K=>K.map(Q=>Q.id===T?{...Q,key:q,status:q?"configured":"not_configured"}:Q)):d(K=>K.map(Q=>Q.id===T?{...Q,key:q,status:q?"configured":"not_configured"}:Q)),A.current[T]&&clearTimeout(A.current[T]),A.current[T]=setTimeout(async()=>{try{if(console.log(`[useAIKeys] Auto-saving key ${T} after delay`),q?await St.saveEncryptedKey(T,q):await St.removeKey(T),B){const Q=(await chrome.storage.local.get(["customKeys"])).customKeys||[],ce=Q.map(D=>D.id===T?{...D,name:D.name}:D);ce.findIndex(D=>D.id===T)===-1&&q&&ce.push({id:T,name:`Пользовательский ключ ${Q.length+1}`,isFixed:!1,isFree:!1}),await chrome.storage.local.set({customKeys:ce.filter(D=>q||D.id!==T)})}console.log(`[useAIKeys] Key ${T} auto-saved successfully`)}catch(K){console.error(`[useAIKeys] Failed to auto-save key ${T}:`,K)}finally{delete A.current[T]}},1e3)},updateCustomKeyName:(T,q)=>{S(B=>B.map(K=>K.id===T?{...K,name:q,status:K.key?"configured":"not_configured"}:K))},getStatusText:T=>{switch(T){case"configured":return u("options.settings.aiKeys.status.configured");case"not_configured":return u("options.settings.aiKeys.status.notConfigured");case"testing":return u("options.settings.aiKeys.status.testing");default:return u("options.settings.aiKeys.status.notConfigured")}},getStatusClass:T=>{switch(T){case"configured":return"status-configured";case"not_configured":return"status-not-configured";case"testing":return"status-testing";default:return""}},getAPIKeyForMCP:async T=>{try{return await St.getDecryptedKey(T)}catch(q){return console.error("Failed to get API key for MCP:",q),null}},isKeyConfigured:async T=>{try{return await St.keyExists(T)}catch(q){return console.error("Failed to check if key is configured:",q),!1}}}},Xh=(...u)=>u.filter(Boolean).join(" "),kh=({value:u,manifest:r,disabled:d,onSave:c,locale:S,t:A,globalAIKeys:z,pluginSettings:ee})=>{const[R,v]=V.useState("basic_analysis"),[O,W]=V.useState("ru"),[$,I]=V.useState(""),F=(K,Q)=>{try{const ce=r?.options?.prompts;return!ce||!((ce[K]||{})[Q]||{}).LLM?.default?"default":K==="basic_analysis"?"gemini-flash-lite":K==="deep_analysis"?"gemini-pro":"default"}catch{return"default"}},ie=(K,Q)=>{try{const ce=r?.options?.prompts;return ce?!!((ce[K]||{})[Q]||{}).LLM?.default:!1}catch{return!1}},ae=()=>{try{const K=r?.options?.prompts;if(!K)return"";const w=((K[R]||{})[O]||{}).default||"";return typeof w=="object"?JSON.stringify(w,null,2):w}catch{return""}},T=()=>{try{const ce=((u||{})[R]||{})[O];return typeof ce=="string"?ce:ce==null?"":JSON.stringify(ce,null,2)}catch{return""}};V.useEffect(()=>{I(T())},[R,O,u]);const q=()=>{I(ae())},B=()=>{try{const K={...u};K[R]||(K[R]={ru:"",en:""}),K[R][O]=$,c(K)}catch(K){console.error("Failed to save custom prompt:",K)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:A("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:A("options.plugins.prompts.type")}),y.jsxs("select",{value:R,onChange:K=>v(K.target.value),disabled:d,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"basic_analysis",children:A("options.plugins.prompts.basic_analysis")}),y.jsx("option",{value:"deep_analysis",children:A("options.plugins.prompts.deep_analysis")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:A("options.plugins.prompts.language")}),y.jsxs("select",{value:O,onChange:K=>W(K.target.value),disabled:d,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:A("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:A("options.plugins.prompts.english")})]})]})]}),y.jsx(qh,{promptType:R,language:O,globalAIKeys:z,defaultLLMCurl:F(R,O),hasDefaultLLM:ie(R,O),onLLMChange:(K,Q)=>{console.log(`LLM changed for ${R} ${O}:`,K,Q)}}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:A("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:ae(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:q,disabled:d,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:d?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:A("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:A("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:$,onChange:K=>I(K.target.value),disabled:d,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:B,disabled:d,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:d?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:A("options.plugins.prompts.save")})})]})})},Qh=u=>{const{selectedPlugin:r,locale:d="en",onUpdateSetting:c}=u,{t:S}=ng(d),{aiKeys:A}=Yh(),{settings:z}=ig(),[ee,R]=V.useState(null),[v,O]=V.useState(null),W=w=>w?typeof w=="string"?w:w[d]||w.ru||w.en||"":"";if(V.useEffect(()=>{r?.manifest?.options&&F()},[r?.id]),!r||typeof r!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:S("options_plugins_details_title")}),y.jsx("p",{children:S("options.plugins.details.selectPlugin")})]});const $=r.settings||{enabled:!0,autorun:!1},I=r.manifest?.host_permissions||[],F=async()=>{if(!(!r||!r.manifest?.options||v!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const D=Object.keys(r.manifest.options).map(De=>`${r.id}_${De}`),le=await chrome.storage.local.get(D),ve={};Object.entries(le).forEach(([De,he])=>{const Ne=De.replace(`${r.id}_`,"");he!==void 0&&(ve[Ne]=he)}),O(ve)}}catch(w){console.warn("Failed to load custom settings from chrome.storage.local:",w)}},ie=async(w,D)=>{if(r)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const le=`${r.id}_${w}`;await chrome.storage.local.set({[le]:D}),O(ve=>({...ve,[w]:D})),w==="prompts"&&await ae()}else console.warn("chrome.storage.local is not available")}catch(le){throw console.error(`Failed to save setting ${w} to chrome.storage.local:`,le),le}},ae=async()=>{if(r)try{const w=`${r.id}_prompts`,D=await chrome.storage.local.get([w]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${r.id}`),console.log(` Storage key: ${w}`),console.log(" Сохраненные промпты:",D[w]),D[w]){const le=D[w];console.log(" Структура промптов:"),console.log(` - basic_analysis.ru: ${le.basic_analysis?.ru?"✓":"✗"} (${le.basic_analysis?.ru?.length||0} символов)`),console.log(` - basic_analysis.en: ${le.basic_analysis?.en?"✓":"✗"} (${le.basic_analysis?.en?.length||0} символов)`),console.log(` - deep_analysis.ru: ${le.deep_analysis?.ru?"✓":"✗"} (${le.deep_analysis?.ru?.length||0} символов)`),console.log(` - deep_analysis.en: ${le.deep_analysis?.en?"✓":"✗"} (${le.deep_analysis?.en?.length||0} символов)`)}}catch(w){console.error("Ошибка диагностики промптов:",w)}},T=(w,D)=>{if(v&&v[w]!==void 0)return v[w];if(w==="prompts"&&typeof D=="object"&&D!==null){const le=D,ve={basic_analysis:{ru:{},en:{}},deep_analysis:{ru:{},en:{}}};return le.basic_analysis?.ru?.default&&(ve.basic_analysis.ru=le.basic_analysis.ru.default),le.basic_analysis?.en?.default&&(ve.basic_analysis.en=le.basic_analysis.en.default),le.deep_analysis?.ru?.default&&(ve.deep_analysis.ru=le.deep_analysis.ru.default),le.deep_analysis?.en?.default&&(ve.deep_analysis.en=le.deep_analysis.en.default),ve}return D},q=(w,D)=>{const le=T(w,D.default),ve=ee===w||!($.enabled??!0),De=W(D.label),he=W(D.description);if(w==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(kh,{value:le,manifest:r.manifest,disabled:ve,onSave:Ne=>B(w,Ne),locale:d,t:S,globalAIKeys:A,pluginSettings:z})},w);if(D.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:le,disabled:ve,onChange:Ne=>B(w,Ne),label:y.jsxs(y.Fragment,{children:[De,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"})]})})},w);if(D.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[De,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"}),y.jsx("select",{id:w,name:w,value:le,disabled:ve,onChange:Ne=>B(w,Ne.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:D.values?.map(Ne=>y.jsx("option",{value:Ne,children:W(D.labels?.[Ne])||Ne},Ne))})]})},w);if(D.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[De,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"text",id:w,name:w,value:le,disabled:ve,onChange:Ne=>B(w,Ne.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},w);if(D.type==="number"){const Ne=lt=>{const we=parseFloat(lt.target.value);if(isNaN(we))return;let M=we;D.min!==void 0&&MD.max&&(M=D.max),B(w,M)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[De,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"number",id:w,name:w,value:le,disabled:ve,onChange:Ne,min:D.min,max:D.max,step:D.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},w)}return null},B=async(w,D)=>{if(!r)return;const le=r.manifest?.options;if(le&&le[w]){v===null&&await F();try{R(w),await ie(w,D)}catch(ve){console.error(`Failed to update custom setting ${w}:`,ve)}finally{R(null)}}else if(c)try{R(w),await c(r.id,w,D)}catch(ve){console.error(`Failed to update setting ${w}:`,ve)}finally{R(null)}},Q=Object.entries(r.manifest?.options??{}).map(([w,D])=>q(w,D)).filter(w=>w!==null),ce=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:$.enabled??!0,disabled:ee==="enabled",onChange:w=>B("enabled",w),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:$.autorun??!1,disabled:ee==="autorun"||!($.enabled??!0),onChange:w=>B("autorun",w),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(Lh,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:r.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",r.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:Xh("status-badge",$.enabled?"status-active":"status-inactive"),children:$.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",r.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",r.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:r.description})]}),I.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:I.map((w,D)=>y.jsx("li",{children:w},D))})]}),Array.isArray(r.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(r.manifest?.permissions??[]).map((w,D)=>y.jsx("li",{children:w},D))})]}):null,y.jsx(ce,{}),r.manifest?.options&&Object.keys(r.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),Q]}):null]})]})})},Vh=({plugin:u,onUpdateSetting:r})=>{const d=r?async(S,A,z)=>{try{return await r(S,A,z),!0}catch(ee){return console.error(`Failed to update setting ${A}:`,ee),!1}}:void 0,c={selectedPlugin:{...u,description:u.description||"Описание не указано",icon:u.icon||"",manifest:u.manifest||{}},locale:"ru",onUpdateSetting:d};return y.jsx(Qh,{...c})},Kd=function(u){if(!u)return"unknown-page";try{const r=new URL(u);return r.search="",r.hash="",r.toString()}catch{return u}},Zh=({pluginId:u,pageKey:r,debounceMs:d=1e3})=>{const[c,S]=V.useState(""),[A,z]=V.useState(!1),[ee,R]=V.useState(!1),[v,O]=V.useState(null),[W,$]=V.useState(""),I=V.useRef(null),F=V.useRef(""),ie=V.useCallback(async B=>{if(B!==F.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:u,pageKey:r,text:B});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:B}),F.current=B,z(!0),O(null),$(B),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:u,pageKey:r,text:B})}catch(K){console.error("[useLazyChatSync] Error saving draft:",K),O("Ошибка сохранения черновика"),z(!1)}}},[u,r]),ae=V.useCallback(async()=>{R(!0),O(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:u,pageKey:r});try{const B=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r});B?.draftText?(S(B.draftText),F.current=B.draftText,z(!0),$(B.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:u,pageKey:r,draft:B.draftText})):(S(""),F.current="",z(!1),$(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:u,pageKey:r}))}catch(B){console.error("[useLazyChatSync] Error loading draft:",B),O("Ошибка загрузки черновика")}finally{R(!1)}},[u,r]),T=V.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:u,pageKey:r});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:""}),F.current="",z(!1),O(null),$(""),S(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:u,pageKey:r})}catch(B){console.error("[useLazyChatSync] Error clearing draft:",B),O("Ошибка очистки черновика")}},[u,r]),q=V.useCallback(B=>{S(B),I.current&&clearTimeout(I.current),B.length===0?T():I.current=setTimeout(()=>{ie(B)},d),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:u,pageKey:r,text:B})},[d,ie,T,u,r]);return V.useEffect(()=>()=>{I.current&&clearTimeout(I.current)},[]),V.useEffect(()=>{ae()},[ae]),V.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",r),z(!1),R(!1),O(null),F.current="",I.current&&(clearTimeout(I.current),I.current=null),ae()},[r,ae]),{message:c,setMessage:q,isDraftSaved:A,isDraftLoading:ee,draftError:v,loadDraft:ae,clearDraft:T,draftText:W}};var cs={exports:{}},Jh=cs.exports,Bd;function $h(){return Bd||(Bd=1,(function(u,r){(function(d,c){c()})(Jh,function(){function d(v,O){return typeof O>"u"?O={autoBom:!1}:typeof O!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),O={autoBom:!O}),O.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(v.type)?new Blob(["\uFEFF",v],{type:v.type}):v}function c(v,O,W){var $=new XMLHttpRequest;$.open("GET",v),$.responseType="blob",$.onload=function(){R($.response,O,W)},$.onerror=function(){console.error("could not download file")},$.send()}function S(v){var O=new XMLHttpRequest;O.open("HEAD",v,!1);try{O.send()}catch{}return 200<=O.status&&299>=O.status}function A(v){try{v.dispatchEvent(new MouseEvent("click"))}catch{var O=document.createEvent("MouseEvents");O.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),v.dispatchEvent(O)}}var z=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof ss=="object"&&ss.global===ss?ss:void 0,ee=z.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),R=z.saveAs||(typeof window!="object"||window!==z?function(){}:"download"in HTMLAnchorElement.prototype&&!ee?function(v,O,W){var $=z.URL||z.webkitURL,I=document.createElement("a");O=O||v.name||"download",I.download=O,I.rel="noopener",typeof v=="string"?(I.href=v,I.origin===location.origin?A(I):S(I.href)?c(v,O,W):A(I,I.target="_blank")):(I.href=$.createObjectURL(v),setTimeout(function(){$.revokeObjectURL(I.href)},4e4),setTimeout(function(){A(I)},0))}:"msSaveOrOpenBlob"in navigator?function(v,O,W){if(O=O||v.name||"download",typeof v!="string")navigator.msSaveOrOpenBlob(d(v,W),O);else if(S(v))c(v,O,W);else{var $=document.createElement("a");$.href=v,$.target="_blank",setTimeout(function(){A($)})}}:function(v,O,W,$){if($=$||open("","_blank"),$&&($.document.title=$.document.body.innerText="downloading..."),typeof v=="string")return c(v,O,W);var I=v.type==="application/octet-stream",F=/constructor/i.test(z.HTMLElement)||z.safari,ie=/CriOS\/[\d]+/.test(navigator.userAgent);if((ie||I&&F||ee)&&typeof FileReader<"u"){var ae=new FileReader;ae.onloadend=function(){var B=ae.result;B=ie?B:B.replace(/^data:[^;]*;/,"data:attachment/file;"),$?$.location.href=B:location=B,$=null},ae.readAsDataURL(v)}else{var T=z.URL||z.webkitURL,q=T.createObjectURL(v);$?$.location=q:location.href=q,$=null,setTimeout(function(){T.revokeObjectURL(q)},4e4)}});z.saveAs=R.saveAs=R,u.exports=R})})(cs)),cs.exports}var Wh=$h(),qa;(function(u){u.Local="local",u.Sync="sync",u.Managed="managed",u.Session="session"})(qa||(qa={}));var Go;(function(u){u.ExtensionPagesOnly="TRUSTED_CONTEXTS",u.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(Go||(Go={}));const La=globalThis.chrome,Ld=async(u,r)=>{const d=S=>typeof S=="function",c=S=>S instanceof Promise;return d(u)?(c(u),u(r)):u};let qd=!1;const Yd=u=>{if(La&&!La.storage[u])throw new Error(`"storage" permission in manifest.ts: "storage ${u}" isn't defined`)},sg=(u,r,d)=>{let c=null,S=!1,A=[];const z=d?.storageEnum??qa.Local,ee=d?.liveUpdate??!1,R=d?.serialization?.serialize??(ae=>ae),v=d?.serialization?.deserialize??(ae=>ae);qd===!1&&z===qa.Session&&d?.sessionAccessForContentScripts===!0&&(Yd(z),La?.storage[z].setAccessLevel({accessLevel:Go.ExtensionPagesAndContentScripts}).catch(ae=>{console.error(ae),console.error("Please call .setAccessLevel() into different context, like a background script.")}),qd=!0);const O=async()=>{Yd(z);const ae=await La?.storage[z].get([u]);return ae?v(ae[u])??r:r},W=async ae=>{S||(c=await O()),c=await Ld(ae,c),await La?.storage[z].set({[u]:R(c)}),F()},$=ae=>(A=[...A,ae],()=>{A=A.filter(T=>T!==ae)}),I=()=>c,F=()=>{A.forEach(ae=>ae())},ie=async ae=>{if(ae[u]===void 0)return;const T=v(ae[u].newValue);c!==T&&(c=await Ld(T,c),F())};return O().then(ae=>{c=ae,S=!0,F()}),ee&&La?.storage[z].onChanged.addListener(ie),{get:O,set:W,getSnapshot:I,subscribe:$}},Xd=sg("theme-storage-key",{theme:"system",isLight:ug()},{storageEnum:qa.Local,liveUpdate:!0});function ug(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Oo={...Xd,toggle:async()=>{await Xd.set(u=>{let r;switch(u.theme){case"light":r="dark";break;case"dark":r="system";break;case"system":default:r="light";break}const d=r==="system"?ug():r==="light";return{theme:r,isLight:d}})}},Do=sg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:qa.Local,liveUpdate:!0}),kd={...Do,setAlignment:async u=>{await Do.set({alignment:u})},getAlignment:async()=>(await Do.get()).alignment},Fh=({plugin:u,currentView:r,isRunning:d,isPaused:c,currentTabUrl:S,onStart:A,onPause:z,onStop:ee,onClose:R})=>{const[v,O]=V.useState("chat"),[W,$]=V.useState(Kd(S)),[I,F]=V.useState("left");V.useEffect(()=>{const U=Kd(S);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:W,newPageKey:U,currentTabUrl:S,timestamp:new Date().toISOString()}),$(U)},[S]),V.useEffect(()=>{const U=async()=>{const p=await kd.getAlignment();F(p)};return U(),kd.subscribe(()=>{U()})},[]);const{message:ie,setMessage:ae,isDraftSaved:T,isDraftLoading:q,draftError:B,loadDraft:K,clearDraft:Q,draftText:ce}=Zh({pluginId:u.id,pageKey:W,debounceMs:1e3}),[w,D]=V.useState([]),[le,ve]=V.useState(!1),[De,he]=V.useState(null),[Ne,lt]=V.useState(60),[we,M]=V.useState(!1),J=V.useRef(null),k=V.useRef(null);V.useEffect(()=>{},[d]);const Te=()=>{A()},g=u.name||(typeof u.manifest?.name=="string"?u.manifest.name:"")||u.id,N=u.id,te=V.useCallback(async U=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...U,messageId:h};try{return await chrome.runtime.sendMessage(p)}catch(Y){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",Y),Y}},[]),Z=V.useCallback(U=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...U,messageId:h};chrome.runtime.sendMessage(p)},[]),ne=V.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const U={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},h={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},Y=H=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:H,hasMessages:H&&"messages"in H,hasChat:H&&"chat"in H,messagesValue:H?.messages,chatValue:H?.chat,isMessagesArray:Array.isArray(H?.messages),isChatArray:Array.isArray(H?.chat),responseType:typeof H,responseKeys:H?Object.keys(H):"response is null/undefined",timestamp:new Date().toISOString()}),H===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let P=null;const X=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],fe=(_e,be)=>{let xe=_e;for(const qe of be)if(xe&&typeof xe=="object"&&qe in xe)xe=xe[qe];else return;return xe};if(Array.isArray(H))P=H,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:P.length,firstMessage:P[0]?{id:P[0].id,content:P[0].content||P[0].text,role:P[0].role,timestamp:P[0].timestamp}:"no messages"});else if(H&&typeof H=="object"){if(H.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",H.error),he(`Ошибка от background: ${H.error}`),D([]);return}for(const{path:_e,description:be}of X){const xe=fe(H,_e);if(Array.isArray(xe)){P=xe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${be}':`,{length:P.length,firstMessage:P[0]?{id:P[0].id,content:P[0].content||P[0].text,role:P[0].role,timestamp:P[0].timestamp}:"no messages"});break}}P||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof H,responseKeys:Object.keys(H),responseSample:JSON.stringify(H).substring(0,500),timestamp:new Date().toISOString()}),P=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:H,responseType:typeof H,responseStringified:JSON.stringify(H).substring(0,200),timestamp:new Date().toISOString()}),P=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:P,isArray:Array.isArray(P),length:P?.length,firstMessage:P?.[0],firstMessageType:P?.[0]?typeof P[0]:"none"}),Array.isArray(P)&&P.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",P.length);const _e=P.filter(be=>!be||typeof be!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",be),!1):!0).map((be,xe)=>{try{let qe=be.content||be.text||"";typeof qe=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",qe),qe=JSON.stringify(qe)):qe==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),qe=""):qe=String(qe);const rl={id:be.id||String(be.timestamp||Date.now()+xe),text:qe,isUser:be.role?be.role==="user":!!be.isUser,timestamp:be.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${xe}:`,{id:rl.id,textLength:rl.text.length,textType:typeof rl.text,isUser:rl.isUser}),rl}catch(qe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${xe}:`,qe,be),{id:`error_${Date.now()}_${xe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:P.length,convertedCount:_e.length,firstConverted:_e[0],allConverted:_e.map(be=>{try{const xe=typeof be.text=="string"?be.text.substring(0,50):String(be.text||"").substring(0,50);return{id:be.id,text:xe,isUser:be.isUser,textType:typeof be.text}}catch(xe){return console.warn("[PluginControlPanel] Error processing message text:",xe,be),{id:be.id,text:"[ERROR: invalid text]",isUser:be.isUser,textType:typeof be.text}}})}),D(_e)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),Y(U)}catch(H){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",H)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),Y(h)}catch(H){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",H)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),Y(p)}catch(H){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",H)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);V.useCallback(U=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:U,hasMessages:U&&"messages"in U,hasChat:U&&"chat"in U,messagesValue:U?.messages,chatValue:U?.chat,isMessagesArray:Array.isArray(U?.messages),isChatArray:Array.isArray(U?.chat),responseType:typeof U,responseKeys:U?Object.keys(U):"response is null/undefined",timestamp:new Date().toISOString()}),U===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let h=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],Y=(H,P)=>{let X=H;for(const fe of P)if(X&&typeof X=="object"&&fe in X)X=X[fe];else return;return X};if(Array.isArray(U))h=U,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});else if(U&&typeof U=="object"){if(U.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",U.error),he(`Ошибка от background: ${U.error}`),D([]);return}for(const{path:H,description:P}of p){const X=Y(U,H);if(Array.isArray(X)){h=X,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${P}':`,{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});break}}h||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof U,responseKeys:Object.keys(U),responseSample:JSON.stringify(U).substring(0,500),timestamp:new Date().toISOString()}),h=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:U,responseType:typeof U,responseStringified:JSON.stringify(U).substring(0,200),timestamp:new Date().toISOString()}),h=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:h,isArray:Array.isArray(h),length:h?.length,firstMessage:h?.[0],firstMessageType:h?.[0]?typeof h[0]:"none"}),Array.isArray(h)&&h.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",h.length);const H=h.filter(P=>!P||typeof P!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",P),!1):!0).map((P,X)=>{try{let fe=P.content||P.text||"",_e=P.timestamp||Date.now();if(typeof fe=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",fe),fe=JSON.stringify(fe);else if(fe==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),fe="";else{fe=String(fe),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${X}:`,fe);try{const xe=JSON.parse(fe);typeof xe=="object"&&xe!==null&&"content"in xe&&(console.log("[PluginControlPanel] Распарсен JSON из content:",xe),fe=String(xe.content||""),xe.timestamp&&typeof xe.timestamp=="number"&&(_e=xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${X}:`,fe)}const be={id:P.id||String(_e+X),text:fe,isUser:P.role?P.role==="user":!!P.isUser,timestamp:_e};return console.log(`[PluginControlPanel] Конвертировано сообщение ${X}:`,{id:be.id,textLength:be.text.length,textType:typeof be.text,isUser:be.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${X}:`,be.text),be}catch(fe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${X}:`,fe,P),{id:`error_${Date.now()}_${X}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:h.length,convertedCount:H.length,firstConverted:H[0],allConverted:H.map(P=>{try{const X=typeof P.text=="string"?P.text.substring(0,50):String(P.text||"").substring(0,50);return{id:P.id,text:X,isUser:P.isUser,textType:typeof P.text}}catch(X){return console.warn("[PluginControlPanel] Error processing message text:",X,P),{id:P.id,text:"[ERROR: invalid text]",isUser:P.isUser,textType:typeof P.text}}})}),D(H)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const ye=V.useCallback(async()=>{ve(!0),he(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:N,pageKey:W,currentTabUrl:S,timestamp:new Date().toISOString(),isRunning:d,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const U=await te({type:"GET_PLUGIN_CHAT",pluginId:N,pageKey:W});console.log("[PluginControlPanel] loadChat - получен ответ от background:",U),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:le,messagesCount:w.length,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),U?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",U.error),he(`Ошибка загрузки чата: ${U.error}`),D([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(h=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:h,hasMessages:h&&"messages"in h,hasChat:h&&"chat"in h,messagesValue:h?.messages,chatValue:h?.chat,isMessagesArray:Array.isArray(h?.messages),isChatArray:Array.isArray(h?.chat),responseType:typeof h,responseKeys:h?Object.keys(h):"response is null/undefined",timestamp:new Date().toISOString()}),h===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let p=null;const Y=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],H=(P,X)=>{let fe=P;for(const _e of X)if(fe&&typeof fe=="object"&&_e in fe)fe=fe[_e];else return;return fe};if(Array.isArray(h))p=h,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(h&&typeof h=="object"){if(h.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",h.error),he(`Ошибка от background: ${h.error}`),D([]);return}for(const{path:P,description:X}of Y){const fe=H(h,P);if(Array.isArray(fe)){p=fe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${X}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof h,responseKeys:Object.keys(h),responseSample:JSON.stringify(h).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:h,responseType:typeof h,responseStringified:JSON.stringify(h).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const P=p.filter(X=>!X||typeof X!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",X),!1):!0).map((X,fe)=>{try{let _e=X.content||X.text||"",be=X.timestamp||Date.now();if(typeof _e=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",_e),_e=JSON.stringify(_e);else if(_e==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),_e="";else{_e=String(_e);try{const qe=JSON.parse(_e);typeof qe=="object"&&qe!==null&&"content"in qe&&(console.log("[PluginControlPanel] Распаршен JSON из content:",qe),_e=String(qe.content||""),qe.timestamp&&typeof qe.timestamp=="number"&&(be=qe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const xe={id:X.id||String(be+fe),text:_e,isUser:X.role?X.role==="user":!!X.isUser,timestamp:be};return console.log(`[PluginControlPanel] Конвертировано сообщение ${fe}:`,{id:xe.id,textLength:xe.text.length,textType:typeof xe.text,isUser:xe.isUser}),xe}catch(_e){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${fe}:`,_e,X),{id:`error_${Date.now()}_${fe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:P.length,firstConverted:P[0],allConverted:P.map(X=>{try{const fe=typeof X.text=="string"?X.text.substring(0,50):String(X.text||"").substring(0,50);return{id:X.id,text:fe,isUser:X.isUser,textType:typeof X.text}}catch(fe){return console.warn("[PluginControlPanel] Error processing message text:",fe,X),{id:X.id,text:"[ERROR: invalid text]",isUser:X.isUser,textType:typeof X.text}}})}),D(P)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(U),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(U){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",U),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:le,messagesCount:w.length,errorType:typeof U,errorMessage:U instanceof Error?U.message:String(U),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:U,errorType:typeof U,errorMessage:U instanceof Error?U.message:String(U),errorStack:U instanceof Error?U.stack:"No stack trace",errorName:U instanceof Error?U.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:N,pageKey:W}),ve(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let h="Ошибка связи с background";U instanceof Error?(h+=`: ${U.message}`,U.name==="TypeError"&&U.message.includes("substring")&&(h+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:U,stack:U.stack,context:{pluginId:N,pageKey:W}}))):h+=`: ${String(U)}`,he(h),D([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[N,W,te,S,d,c]);V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:N,pageKey:W,currentTabUrl:S,timestamp:new Date().toISOString()}),ye().catch(U=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",U)})},[ye]),V.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),ye().catch(U=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",U)}),K()},[W,ye,K]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:N,pageKey:W,timestamp:new Date().toISOString()});const U=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===N&&p.pageKey===W&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:le,messagesCount:w.length,currentPageKey:W,pluginId:N,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),ye().catch(Y=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",Y)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:le,messagesCount:w.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),he(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,H=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const X=JSON.parse(Y);typeof X=="object"&&X!==null&&"content"in X&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",X),Y=String(X.content||""),X.timestamp&&typeof X.timestamp=="number"&&(H=X.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const P={id:p.message.id||`pyodide_${H}_${Math.random()}`,text:Y,isUser:!1,timestamp:H};console.log("[PluginControlPanel] Adding Pyodide message to chat:",P),D(X=>[...X,P]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",Y,p);const H={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(P=>[...P,H])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},h=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(U),chrome.runtime.onMessage.removeListener(h),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(U),chrome.runtime.onMessage.removeListener(h)}},[N,W,Z,ye]),V.useEffect(()=>{r==="chat"&&K()},[r,K]),V.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:N,pageKey:W,draftText:ce,message:ie,currentView:r,isRunning:d,isPaused:c})}),V.useEffect(()=>{const U=console.error;return console.error=(...h)=>{const p=h.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",h),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),U.apply(console,h)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=U,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),V.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const U=h=>{const p=h.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,H=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const X=JSON.parse(Y);typeof X=="object"&&X!==null&&"content"in X&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",X),Y=String(X.content||""),X.timestamp&&typeof X.timestamp=="number"&&(H=X.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const P={id:p.message.id||`pyodide_${H}_${Math.random()}`,text:Y,isUser:!1,timestamp:H};console.log("[PluginControlPanel] Adding Pyodide message to chat:",P),D(X=>[...X,P]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",Y,p);const H={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(P=>[...P,H])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",U),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",U),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),V.useEffect(()=>{typeof ce=="string"&&(ae(ce),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",ce))},[ce,ae]);const de=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:ie}),!ie.trim())return;const U={id:Date.now().toString(),text:ie.trim(),timestamp:Date.now()};ae(""),he(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),Z({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:N,pageKey:W,message:{role:"user",content:U.text,timestamp:U.timestamp}}),Q()};V.useEffect(()=>{const U=p=>{if(!we)return;const Y=document.querySelector(".chat-view");if(!Y)return;const H=Y.getBoundingClientRect(),P=H.bottom-p.clientY,X=100,fe=H.height-80;P>=X&&P<=fe&<(H.height-P)},h=()=>{M(!1),document.body.style.cursor="",document.body.style.userSelect=""};return we&&(document.addEventListener("mousemove",U),document.addEventListener("mouseup",h)),()=>{document.removeEventListener("mousemove",U),document.removeEventListener("mouseup",h)}},[we]),V.useEffect(()=>{J.current?.scrollIntoView({behavior:"smooth"})},[w]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:w.length,firstMessage:w[0]?{id:w[0].id,text:typeof w[0].text=="string"?w[0].text.substring(0,50):String(w[0].text||"").substring(0,50),isUser:w[0].isUser,timestamp:w[0].timestamp,textType:typeof w[0].text}:null,allMessages:w.map(U=>{try{const h=typeof U.text=="string"?U.text.substring(0,30):String(U.text||"").substring(0,30);return{id:U.id,text:h,isUser:U.isUser,textType:typeof U.text}}catch(h){return console.warn("[PluginControlPanel] Error in message logging:",h,U),{id:U.id,text:"[LOGGING ERROR]",isUser:U.isUser,textType:typeof U.text}}}),timestamp:new Date().toISOString()})},[w]),V.useEffect(()=>{r==="chat"&&setTimeout(()=>k.current?.focus(),100)},[r]);const ze=U=>{ae(U.target.value);const h=U.target;h.style.height="auto";const p=Math.min(Math.max(h.scrollHeight,60),200);h.style.height=`${p}px`,lt(p)},je=U=>{U.key==="Enter"&&!U.shiftKey&&(U.preventDefault(),de())},ut=()=>{ve(!0),he(null),Z({type:"DELETE_PLUGIN_CHAT",pluginId:N,pageKey:W}),D([]),Q()},zt=()=>{const U=JSON.stringify(w,null,2),h=new Blob([U],{type:"application/json"});Wh.saveAs(h,`plugin-chat-${N}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":I},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:u.iconUrl||`plugins/${u.id}/${u.icon||"icon.svg"}`,alt:`${g} icon`,onError:U=>{const h=typeof g=="string"&&g.length>0?g.charAt(0):"P";U.currentTarget.src=`data:image/svg+xml;utf8,${h}`}}),y.jsx("h3",{children:g})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${d?"stop-mode":"start-mode"}`,onClick:Te,disabled:d||c,title:d?"Остановить":"Запустить",children:d?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:z,disabled:!d||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:ee,disabled:!d,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:R,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${v==="chat"?"active":""}`,onClick:()=>O("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${v==="details"?"active":""}`,onClick:()=>O("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[v==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:ut,disabled:le||!!De,children:le?"Очистка...":De?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:zt,disabled:le||!!De,children:le?"Экспорт...":De?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ne,disabled:le,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[le&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),De&&y.jsx("div",{className:"chat-error",children:De}),!le&&!De&&w.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:w.map((U,h)=>{console.log("[PluginControlPanel] render message:",h,U);let p=U.text,Y=U.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",h,":",U.text);try{const H=JSON.parse(p);if(typeof H=="object"&&H!==null&&"content"in H){console.log("[PluginControlPanel] Парсинг JSON в рендере:",H);let P=H.content;typeof P=="object"?p=JSON.stringify(P):p=String(P||""),H.timestamp&&typeof H.timestamp=="number"&&(Y=H.timestamp)}else if(typeof H=="string")p=H;else if(typeof H=="object"&&H!==null){const P=Object.values(H).filter(X=>typeof X=="string");P.length>0?p=String(P[0]):p=JSON.stringify(H)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",h,":",p),y.jsx("div",{className:`chat-message ${U.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(Y).toLocaleTimeString()})]})},U.id||h)})}),y.jsx("div",{ref:J})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:k,className:"message-textarea",value:ie,onChange:ze,onKeyPress:je,placeholder:"Напишите сообщение...",style:{height:`${Ne}px`}}),y.jsx("button",{className:"send-btn",onClick:de,disabled:!ie.trim(),children:"📤"}),y.jsx(By,{isDraftSaved:T,isDraftLoading:q,draftError:B,messageLength:ie.length,minLength:10,maxLength:1e3})]})]}),v==="details"&&y.jsx(Vh,{plugin:u})]})]})},Ih=({toasts:u,onRemove:r})=>y.jsx("div",{className:"toast-container",children:u.map(d=>y.jsx(ep,{toast:d,onRemove:r},d.id))}),ep=({toast:u,onRemove:r})=>{const[d,c]=V.useState(!1),[S,A]=V.useState(!1);V.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),u.duration>0){const ee=setTimeout(()=>{z()},u.duration);return()=>clearTimeout(ee)}},[u.duration]);const z=V.useCallback(()=>{A(!0),setTimeout(()=>{r(u.id)},300)},[u.id,r]);return y.jsx("div",{className:`toast toast-${u.type} ${d?"toast-visible":""} ${S?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:u.message}),y.jsx("button",{className:"toast-close",onClick:z,"aria-label":"Закрыть уведомление",children:"×"})]})})},tp=({theme:u,isLight:r,onToggle:d,isInSidebar:c=!1})=>{const S=()=>{switch(u){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},A=()=>{switch(u){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},z={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:d,style:z,title:A(),children:S()})};function og(u){var r,d,c="";if(typeof u=="string"||typeof u=="number")c+=u;else if(typeof u=="object")if(Array.isArray(u)){var S=u.length;for(r=0;r{const r=ip(u),{conflictingClassGroups:d,conflictingClassGroupModifiers:c}=u;return{getClassGroupId:z=>{const ee=z.split(Yo);return ee[0]===""&&ee.length!==1&&ee.shift(),cg(ee,r)||np(z)},getConflictingClassGroupIds:(z,ee)=>{const R=d[z]||[];return ee&&c[z]?[...R,...c[z]]:R}}},cg=(u,r)=>{if(u.length===0)return r.classGroupId;const d=u[0],c=r.nextPart.get(d),S=c?cg(u.slice(1),c):void 0;if(S)return S;if(r.validators.length===0)return;const A=u.join(Yo);return r.validators.find(({validator:z})=>z(A))?.classGroupId},Qd=/^\[(.+)\]$/,np=u=>{if(Qd.test(u)){const r=Qd.exec(u)[1],d=r?.substring(0,r.indexOf(":"));if(d)return"arbitrary.."+d}},ip=u=>{const{theme:r,classGroups:d}=u,c={nextPart:new Map,validators:[]};for(const S in d)Ko(d[S],c,S,r);return c},Ko=(u,r,d,c)=>{u.forEach(S=>{if(typeof S=="string"){const A=S===""?r:Vd(r,S);A.classGroupId=d;return}if(typeof S=="function"){if(sp(S)){Ko(S(c),r,d,c);return}r.validators.push({validator:S,classGroupId:d});return}Object.entries(S).forEach(([A,z])=>{Ko(z,Vd(r,A),d,c)})})},Vd=(u,r)=>{let d=u;return r.split(Yo).forEach(c=>{d.nextPart.has(c)||d.nextPart.set(c,{nextPart:new Map,validators:[]}),d=d.nextPart.get(c)}),d},sp=u=>u.isThemeGetter,up=u=>{if(u<1)return{get:()=>{},set:()=>{}};let r=0,d=new Map,c=new Map;const S=(A,z)=>{d.set(A,z),r++,r>u&&(r=0,c=d,d=new Map)};return{get(A){let z=d.get(A);if(z!==void 0)return z;if((z=c.get(A))!==void 0)return S(A,z),z},set(A,z){d.has(A)?d.set(A,z):S(A,z)}}},Bo="!",Lo=":",op=Lo.length,cp=u=>{const{prefix:r,experimentalParseClassName:d}=u;let c=S=>{const A=[];let z=0,ee=0,R=0,v;for(let F=0;FR?v-R:void 0;return{modifiers:A,hasImportantModifier:$,baseClassName:W,maybePostfixModifierPosition:I}};if(r){const S=r+Lo,A=c;c=z=>z.startsWith(S)?A(z.substring(S.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:z,maybePostfixModifierPosition:void 0}}if(d){const S=c;c=A=>d({className:A,parseClassName:S})}return c},rp=u=>u.endsWith(Bo)?u.substring(0,u.length-1):u.startsWith(Bo)?u.substring(1):u,fp=u=>{const r=Object.fromEntries(u.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const S=[];let A=[];return c.forEach(z=>{z[0]==="["||r[z]?(S.push(...A.sort(),z),A=[]):A.push(z)}),S.push(...A.sort()),S}},dp=u=>({cache:up(u.cacheSize),parseClassName:cp(u),sortModifiers:fp(u),...ap(u)}),gp=/\s+/,mp=(u,r)=>{const{parseClassName:d,getClassGroupId:c,getConflictingClassGroupIds:S,sortModifiers:A}=r,z=[],ee=u.trim().split(gp);let R="";for(let v=ee.length-1;v>=0;v-=1){const O=ee[v],{isExternal:W,modifiers:$,hasImportantModifier:I,baseClassName:F,maybePostfixModifierPosition:ie}=d(O);if(W){R=O+(R.length>0?" "+R:R);continue}let ae=!!ie,T=c(ae?F.substring(0,ie):F);if(!T){if(!ae){R=O+(R.length>0?" "+R:R);continue}if(T=c(F),!T){R=O+(R.length>0?" "+R:R);continue}ae=!1}const q=A($).join(":"),B=I?q+Bo:q,K=B+T;if(z.includes(K))continue;z.push(K);const Q=S(T,ae);for(let ce=0;ce0?" "+R:R)}return R};function yp(){let u=0,r,d,c="";for(;u{if(typeof u=="string")return u;let r,d="";for(let c=0;cW(O),u());return d=dp(v),c=d.cache.get,S=d.cache.set,A=ee,ee(R)}function ee(R){const v=c(R);if(v)return v;const O=mp(R,d);return S(R,O),O}return function(){return A(yp.apply(null,arguments))}}const tt=u=>{const r=d=>d[u]||[];return r.isThemeGetter=!0,r},fg=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,dg=/^\((?:(\w[\w-]*):)?(.+)\)$/i,pp=/^\d+\/\d+$/,vp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,bp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Sp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,_p=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,xp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,Ba=u=>pp.test(u),Ae=u=>!!u&&!Number.isNaN(Number(u)),jl=u=>!!u&&Number.isInteger(Number(u)),zo=u=>u.endsWith("%")&&Ae(u.slice(0,-1)),cl=u=>vp.test(u),Ep=()=>!0,Ap=u=>bp.test(u)&&!Sp.test(u),gg=()=>!1,Tp=u=>_p.test(u),Cp=u=>xp.test(u),Mp=u=>!ue(u)&&!oe(u),Op=u=>Ya(u,hg,gg),ue=u=>fg.test(u),ea=u=>Ya(u,pg,Ap),Ro=u=>Ya(u,Up,Ae),Zd=u=>Ya(u,mg,gg),Dp=u=>Ya(u,yg,Cp),us=u=>Ya(u,vg,Tp),oe=u=>dg.test(u),kn=u=>Xa(u,pg),zp=u=>Xa(u,Np),Jd=u=>Xa(u,mg),Rp=u=>Xa(u,hg),wp=u=>Xa(u,yg),os=u=>Xa(u,vg,!0),Ya=(u,r,d)=>{const c=fg.exec(u);return c?c[1]?r(c[1]):d(c[2]):!1},Xa=(u,r,d=!1)=>{const c=dg.exec(u);return c?c[1]?r(c[1]):d:!1},mg=u=>u==="position"||u==="percentage",yg=u=>u==="image"||u==="url",hg=u=>u==="length"||u==="size"||u==="bg-size",pg=u=>u==="length",Up=u=>u==="number",Np=u=>u==="family-name",vg=u=>u==="shadow",jp=()=>{const u=tt("color"),r=tt("font"),d=tt("text"),c=tt("font-weight"),S=tt("tracking"),A=tt("leading"),z=tt("breakpoint"),ee=tt("container"),R=tt("spacing"),v=tt("radius"),O=tt("shadow"),W=tt("inset-shadow"),$=tt("text-shadow"),I=tt("drop-shadow"),F=tt("blur"),ie=tt("perspective"),ae=tt("aspect"),T=tt("ease"),q=tt("animate"),B=()=>["auto","avoid","all","avoid-page","page","left","right","column"],K=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],Q=()=>[...K(),oe,ue],ce=()=>["auto","hidden","clip","visible","scroll"],w=()=>["auto","contain","none"],D=()=>[oe,ue,R],le=()=>[Ba,"full","auto",...D()],ve=()=>[jl,"none","subgrid",oe,ue],De=()=>["auto",{span:["full",jl,oe,ue]},jl,oe,ue],he=()=>[jl,"auto",oe,ue],Ne=()=>["auto","min","max","fr",oe,ue],lt=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],we=()=>["start","end","center","stretch","center-safe","end-safe"],M=()=>["auto",...D()],J=()=>[Ba,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...D()],k=()=>[u,oe,ue],Te=()=>[...K(),Jd,Zd,{position:[oe,ue]}],g=()=>["no-repeat",{repeat:["","x","y","space","round"]}],N=()=>["auto","cover","contain",Rp,Op,{size:[oe,ue]}],te=()=>[zo,kn,ea],Z=()=>["","none","full",v,oe,ue],ne=()=>["",Ae,kn,ea],ye=()=>["solid","dashed","dotted","double"],de=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ze=()=>[Ae,zo,Jd,Zd],je=()=>["","none",F,oe,ue],ut=()=>["none",Ae,oe,ue],zt=()=>["none",Ae,oe,ue],U=()=>[Ae,oe,ue],h=()=>[Ba,"full",...D()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[cl],breakpoint:[cl],color:[Ep],container:[cl],"drop-shadow":[cl],ease:["in","out","in-out"],font:[Mp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[cl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[cl],shadow:[cl],spacing:["px",Ae],text:[cl],"text-shadow":[cl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",Ba,ue,oe,ae]}],container:["container"],columns:[{columns:[Ae,ue,oe,ee]}],"break-after":[{"break-after":B()}],"break-before":[{"break-before":B()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:Q()}],overflow:[{overflow:ce()}],"overflow-x":[{"overflow-x":ce()}],"overflow-y":[{"overflow-y":ce()}],overscroll:[{overscroll:w()}],"overscroll-x":[{"overscroll-x":w()}],"overscroll-y":[{"overscroll-y":w()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:le()}],"inset-x":[{"inset-x":le()}],"inset-y":[{"inset-y":le()}],start:[{start:le()}],end:[{end:le()}],top:[{top:le()}],right:[{right:le()}],bottom:[{bottom:le()}],left:[{left:le()}],visibility:["visible","invisible","collapse"],z:[{z:[jl,"auto",oe,ue]}],basis:[{basis:[Ba,"full","auto",ee,...D()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Ae,Ba,"auto","initial","none",ue]}],grow:[{grow:["",Ae,oe,ue]}],shrink:[{shrink:["",Ae,oe,ue]}],order:[{order:[jl,"first","last","none",oe,ue]}],"grid-cols":[{"grid-cols":ve()}],"col-start-end":[{col:De()}],"col-start":[{"col-start":he()}],"col-end":[{"col-end":he()}],"grid-rows":[{"grid-rows":ve()}],"row-start-end":[{row:De()}],"row-start":[{"row-start":he()}],"row-end":[{"row-end":he()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":Ne()}],"auto-rows":[{"auto-rows":Ne()}],gap:[{gap:D()}],"gap-x":[{"gap-x":D()}],"gap-y":[{"gap-y":D()}],"justify-content":[{justify:[...lt(),"normal"]}],"justify-items":[{"justify-items":[...we(),"normal"]}],"justify-self":[{"justify-self":["auto",...we()]}],"align-content":[{content:["normal",...lt()]}],"align-items":[{items:[...we(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...we(),{baseline:["","last"]}]}],"place-content":[{"place-content":lt()}],"place-items":[{"place-items":[...we(),"baseline"]}],"place-self":[{"place-self":["auto",...we()]}],p:[{p:D()}],px:[{px:D()}],py:[{py:D()}],ps:[{ps:D()}],pe:[{pe:D()}],pt:[{pt:D()}],pr:[{pr:D()}],pb:[{pb:D()}],pl:[{pl:D()}],m:[{m:M()}],mx:[{mx:M()}],my:[{my:M()}],ms:[{ms:M()}],me:[{me:M()}],mt:[{mt:M()}],mr:[{mr:M()}],mb:[{mb:M()}],ml:[{ml:M()}],"space-x":[{"space-x":D()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":D()}],"space-y-reverse":["space-y-reverse"],size:[{size:J()}],w:[{w:[ee,"screen",...J()]}],"min-w":[{"min-w":[ee,"screen","none",...J()]}],"max-w":[{"max-w":[ee,"screen","none","prose",{screen:[z]},...J()]}],h:[{h:["screen","lh",...J()]}],"min-h":[{"min-h":["screen","lh","none",...J()]}],"max-h":[{"max-h":["screen","lh",...J()]}],"font-size":[{text:["base",d,kn,ea]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,oe,Ro]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",zo,ue]}],"font-family":[{font:[zp,ue,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[S,oe,ue]}],"line-clamp":[{"line-clamp":[Ae,"none",oe,Ro]}],leading:[{leading:[A,...D()]}],"list-image":[{"list-image":["none",oe,ue]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",oe,ue]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:k()}],"text-color":[{text:k()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ye(),"wavy"]}],"text-decoration-thickness":[{decoration:[Ae,"from-font","auto",oe,ea]}],"text-decoration-color":[{decoration:k()}],"underline-offset":[{"underline-offset":[Ae,"auto",oe,ue]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:D()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",oe,ue]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",oe,ue]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Te()}],"bg-repeat":[{bg:g()}],"bg-size":[{bg:N()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},jl,oe,ue],radial:["",oe,ue],conic:[jl,oe,ue]},wp,Dp]}],"bg-color":[{bg:k()}],"gradient-from-pos":[{from:te()}],"gradient-via-pos":[{via:te()}],"gradient-to-pos":[{to:te()}],"gradient-from":[{from:k()}],"gradient-via":[{via:k()}],"gradient-to":[{to:k()}],rounded:[{rounded:Z()}],"rounded-s":[{"rounded-s":Z()}],"rounded-e":[{"rounded-e":Z()}],"rounded-t":[{"rounded-t":Z()}],"rounded-r":[{"rounded-r":Z()}],"rounded-b":[{"rounded-b":Z()}],"rounded-l":[{"rounded-l":Z()}],"rounded-ss":[{"rounded-ss":Z()}],"rounded-se":[{"rounded-se":Z()}],"rounded-ee":[{"rounded-ee":Z()}],"rounded-es":[{"rounded-es":Z()}],"rounded-tl":[{"rounded-tl":Z()}],"rounded-tr":[{"rounded-tr":Z()}],"rounded-br":[{"rounded-br":Z()}],"rounded-bl":[{"rounded-bl":Z()}],"border-w":[{border:ne()}],"border-w-x":[{"border-x":ne()}],"border-w-y":[{"border-y":ne()}],"border-w-s":[{"border-s":ne()}],"border-w-e":[{"border-e":ne()}],"border-w-t":[{"border-t":ne()}],"border-w-r":[{"border-r":ne()}],"border-w-b":[{"border-b":ne()}],"border-w-l":[{"border-l":ne()}],"divide-x":[{"divide-x":ne()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ne()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ye(),"hidden","none"]}],"divide-style":[{divide:[...ye(),"hidden","none"]}],"border-color":[{border:k()}],"border-color-x":[{"border-x":k()}],"border-color-y":[{"border-y":k()}],"border-color-s":[{"border-s":k()}],"border-color-e":[{"border-e":k()}],"border-color-t":[{"border-t":k()}],"border-color-r":[{"border-r":k()}],"border-color-b":[{"border-b":k()}],"border-color-l":[{"border-l":k()}],"divide-color":[{divide:k()}],"outline-style":[{outline:[...ye(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Ae,oe,ue]}],"outline-w":[{outline:["",Ae,kn,ea]}],"outline-color":[{outline:k()}],shadow:[{shadow:["","none",O,os,us]}],"shadow-color":[{shadow:k()}],"inset-shadow":[{"inset-shadow":["none",W,os,us]}],"inset-shadow-color":[{"inset-shadow":k()}],"ring-w":[{ring:ne()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:k()}],"ring-offset-w":[{"ring-offset":[Ae,ea]}],"ring-offset-color":[{"ring-offset":k()}],"inset-ring-w":[{"inset-ring":ne()}],"inset-ring-color":[{"inset-ring":k()}],"text-shadow":[{"text-shadow":["none",$,os,us]}],"text-shadow-color":[{"text-shadow":k()}],opacity:[{opacity:[Ae,oe,ue]}],"mix-blend":[{"mix-blend":[...de(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":de()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Ae]}],"mask-image-linear-from-pos":[{"mask-linear-from":ze()}],"mask-image-linear-to-pos":[{"mask-linear-to":ze()}],"mask-image-linear-from-color":[{"mask-linear-from":k()}],"mask-image-linear-to-color":[{"mask-linear-to":k()}],"mask-image-t-from-pos":[{"mask-t-from":ze()}],"mask-image-t-to-pos":[{"mask-t-to":ze()}],"mask-image-t-from-color":[{"mask-t-from":k()}],"mask-image-t-to-color":[{"mask-t-to":k()}],"mask-image-r-from-pos":[{"mask-r-from":ze()}],"mask-image-r-to-pos":[{"mask-r-to":ze()}],"mask-image-r-from-color":[{"mask-r-from":k()}],"mask-image-r-to-color":[{"mask-r-to":k()}],"mask-image-b-from-pos":[{"mask-b-from":ze()}],"mask-image-b-to-pos":[{"mask-b-to":ze()}],"mask-image-b-from-color":[{"mask-b-from":k()}],"mask-image-b-to-color":[{"mask-b-to":k()}],"mask-image-l-from-pos":[{"mask-l-from":ze()}],"mask-image-l-to-pos":[{"mask-l-to":ze()}],"mask-image-l-from-color":[{"mask-l-from":k()}],"mask-image-l-to-color":[{"mask-l-to":k()}],"mask-image-x-from-pos":[{"mask-x-from":ze()}],"mask-image-x-to-pos":[{"mask-x-to":ze()}],"mask-image-x-from-color":[{"mask-x-from":k()}],"mask-image-x-to-color":[{"mask-x-to":k()}],"mask-image-y-from-pos":[{"mask-y-from":ze()}],"mask-image-y-to-pos":[{"mask-y-to":ze()}],"mask-image-y-from-color":[{"mask-y-from":k()}],"mask-image-y-to-color":[{"mask-y-to":k()}],"mask-image-radial":[{"mask-radial":[oe,ue]}],"mask-image-radial-from-pos":[{"mask-radial-from":ze()}],"mask-image-radial-to-pos":[{"mask-radial-to":ze()}],"mask-image-radial-from-color":[{"mask-radial-from":k()}],"mask-image-radial-to-color":[{"mask-radial-to":k()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":K()}],"mask-image-conic-pos":[{"mask-conic":[Ae]}],"mask-image-conic-from-pos":[{"mask-conic-from":ze()}],"mask-image-conic-to-pos":[{"mask-conic-to":ze()}],"mask-image-conic-from-color":[{"mask-conic-from":k()}],"mask-image-conic-to-color":[{"mask-conic-to":k()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Te()}],"mask-repeat":[{mask:g()}],"mask-size":[{mask:N()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",oe,ue]}],filter:[{filter:["","none",oe,ue]}],blur:[{blur:je()}],brightness:[{brightness:[Ae,oe,ue]}],contrast:[{contrast:[Ae,oe,ue]}],"drop-shadow":[{"drop-shadow":["","none",I,os,us]}],"drop-shadow-color":[{"drop-shadow":k()}],grayscale:[{grayscale:["",Ae,oe,ue]}],"hue-rotate":[{"hue-rotate":[Ae,oe,ue]}],invert:[{invert:["",Ae,oe,ue]}],saturate:[{saturate:[Ae,oe,ue]}],sepia:[{sepia:["",Ae,oe,ue]}],"backdrop-filter":[{"backdrop-filter":["","none",oe,ue]}],"backdrop-blur":[{"backdrop-blur":je()}],"backdrop-brightness":[{"backdrop-brightness":[Ae,oe,ue]}],"backdrop-contrast":[{"backdrop-contrast":[Ae,oe,ue]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Ae,oe,ue]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Ae,oe,ue]}],"backdrop-invert":[{"backdrop-invert":["",Ae,oe,ue]}],"backdrop-opacity":[{"backdrop-opacity":[Ae,oe,ue]}],"backdrop-saturate":[{"backdrop-saturate":[Ae,oe,ue]}],"backdrop-sepia":[{"backdrop-sepia":["",Ae,oe,ue]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":D()}],"border-spacing-x":[{"border-spacing-x":D()}],"border-spacing-y":[{"border-spacing-y":D()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",oe,ue]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Ae,"initial",oe,ue]}],ease:[{ease:["linear","initial",T,oe,ue]}],delay:[{delay:[Ae,oe,ue]}],animate:[{animate:["none",q,oe,ue]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[ie,oe,ue]}],"perspective-origin":[{"perspective-origin":Q()}],rotate:[{rotate:ut()}],"rotate-x":[{"rotate-x":ut()}],"rotate-y":[{"rotate-y":ut()}],"rotate-z":[{"rotate-z":ut()}],scale:[{scale:zt()}],"scale-x":[{"scale-x":zt()}],"scale-y":[{"scale-y":zt()}],"scale-z":[{"scale-z":zt()}],"scale-3d":["scale-3d"],skew:[{skew:U()}],"skew-x":[{"skew-x":U()}],"skew-y":[{"skew-y":U()}],transform:[{transform:[oe,ue,"","none","gpu","cpu"]}],"transform-origin":[{origin:Q()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:h()}],"translate-x":[{"translate-x":h()}],"translate-y":[{"translate-y":h()}],"translate-z":[{"translate-z":h()}],"translate-none":["translate-none"],accent:[{accent:k()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:k()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",oe,ue]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":D()}],"scroll-mx":[{"scroll-mx":D()}],"scroll-my":[{"scroll-my":D()}],"scroll-ms":[{"scroll-ms":D()}],"scroll-me":[{"scroll-me":D()}],"scroll-mt":[{"scroll-mt":D()}],"scroll-mr":[{"scroll-mr":D()}],"scroll-mb":[{"scroll-mb":D()}],"scroll-ml":[{"scroll-ml":D()}],"scroll-p":[{"scroll-p":D()}],"scroll-px":[{"scroll-px":D()}],"scroll-py":[{"scroll-py":D()}],"scroll-ps":[{"scroll-ps":D()}],"scroll-pe":[{"scroll-pe":D()}],"scroll-pt":[{"scroll-pt":D()}],"scroll-pr":[{"scroll-pr":D()}],"scroll-pb":[{"scroll-pb":D()}],"scroll-pl":[{"scroll-pl":D()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",oe,ue]}],fill:[{fill:["none",...k()]}],"stroke-w":[{stroke:[Ae,kn,ea,Ro]}],stroke:[{stroke:["none",...k()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},Hp=hp(jp),$d=(...u)=>Hp(lp(u));var wo,Wd;function Pp(){if(Wd)return wo;Wd=1;var u=function(q){return r(q)&&!d(q)};function r(T){return!!T&&typeof T=="object"}function d(T){var q=Object.prototype.toString.call(T);return q==="[object RegExp]"||q==="[object Date]"||A(T)}var c=typeof Symbol=="function"&&Symbol.for,S=c?Symbol.for("react.element"):60103;function A(T){return T.$$typeof===S}function z(T){return Array.isArray(T)?[]:{}}function ee(T,q){return q.clone!==!1&&q.isMergeableObject(T)?ie(z(T),T,q):T}function R(T,q,B){return T.concat(q).map(function(K){return ee(K,B)})}function v(T,q){if(!q.customMerge)return ie;var B=q.customMerge(T);return typeof B=="function"?B:ie}function O(T){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(T).filter(function(q){return Object.propertyIsEnumerable.call(T,q)}):[]}function W(T){return Object.keys(T).concat(O(T))}function $(T,q){try{return q in T}catch{return!1}}function I(T,q){return $(T,q)&&!(Object.hasOwnProperty.call(T,q)&&Object.propertyIsEnumerable.call(T,q))}function F(T,q,B){var K={};return B.isMergeableObject(T)&&W(T).forEach(function(Q){K[Q]=ee(T[Q],B)}),W(q).forEach(function(Q){I(T,Q)||($(T,Q)&&B.isMergeableObject(q[Q])?K[Q]=v(Q,B)(T[Q],q[Q],B):K[Q]=ee(q[Q],B))}),K}function ie(T,q,B){B=B||{},B.arrayMerge=B.arrayMerge||R,B.isMergeableObject=B.isMergeableObject||u,B.cloneUnlessOtherwiseSpecified=ee;var K=Array.isArray(q),Q=Array.isArray(T),ce=K===Q;return ce?K?B.arrayMerge(T,q,B):F(T,q,B):ee(q,B)}ie.all=function(q,B){if(!Array.isArray(q))throw new Error("first argument should be an array");return q.reduce(function(K,Q){return ie(K,Q,B)},{})};var ae=ie;return wo=ae,wo}Pp();var Uo={exports:{}},Qn={},No={exports:{}},jo={};/** + */var Hd;function Ly(){if(Hd)return Se;Hd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),T=Symbol.for("react.consumer"),z=Symbol.for("react.context"),ee=Symbol.for("react.forward_ref"),R=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),F=Symbol.iterator;function W(g){return g===null||typeof g!="object"?null:(g=F&&g[F]||g["@@iterator"],typeof g=="function"?g:null)}var I={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},$=Object.assign,ie={};function ae(g,N,te){this.props=g,this.context=N,this.refs=ie,this.updater=te||I}ae.prototype.isReactComponent={},ae.prototype.setState=function(g,N){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,N,"setState")},ae.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function A(){}A.prototype=ae.prototype;function K(g,N,te){this.props=g,this.context=N,this.refs=ie,this.updater=te||I}var B=K.prototype=new A;B.constructor=K,$(B,ae.prototype),B.isPureReactComponent=!0;var L=Array.isArray,V={H:null,A:null,T:null,S:null,V:null},ce=Object.prototype.hasOwnProperty;function w(g,N,te,Z,ne,ye){return te=ye.ref,{$$typeof:u,type:g,key:N,ref:te!==void 0?te:null,props:ye}}function D(g,N){return w(g.type,N,void 0,void 0,void 0,g.props)}function le(g){return typeof g=="object"&&g!==null&&g.$$typeof===u}function ve(g){var N={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(te){return N[te]})}var De=/\/+/g;function he(g,N){return typeof g=="object"&&g!==null&&g.key!=null?ve(""+g.key):N.toString(36)}function Ne(){}function lt(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(Ne,Ne):(g.status="pending",g.then(function(N){g.status==="pending"&&(g.status="fulfilled",g.value=N)},function(N){g.status==="pending"&&(g.status="rejected",g.reason=N)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function we(g,N,te,Z,ne){var ye=typeof g;(ye==="undefined"||ye==="boolean")&&(g=null);var de=!1;if(g===null)de=!0;else switch(ye){case"bigint":case"string":case"number":de=!0;break;case"object":switch(g.$$typeof){case u:case r:de=!0;break;case O:return de=g._init,we(de(g._payload),N,te,Z,ne)}}if(de)return ne=ne(g),de=Z===""?"."+he(g,0):Z,L(ne)?(te="",de!=null&&(te=de.replace(De,"$&/")+"/"),we(ne,N,te,"",function(ut){return ut})):ne!=null&&(le(ne)&&(ne=D(ne,te+(ne.key==null||g&&g.key===ne.key?"":(""+ne.key).replace(De,"$&/")+"/")+de)),N.push(ne)),1;de=0;var ze=Z===""?".":Z+":";if(L(g))for(var je=0;je>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},Yy="Platform Settings",Xy="Available plugins",ky="AI API Keys",Qy="Free",Vy="Custom API keys",Zy="+ Add new key",Jy="Enter API key",$y="Enter key name",Wy="Save all keys",Fy="Test connections",Iy="General Settings",eh="Automatic plugin updates",th="Show notifications",lh="Interface theme:",ah="Light",nh="Dark",ih="System",sh="Security",uh="Verify plugin signatures",oh="Isolated execution mode",ch="Performance",rh="Maximum number of active plugins:",fh="Cache plugin data",dh="Plugin Details",gh={options:qy,options_settings_title:Yy,options_plugins_title:Xy,options_settings_aiKeys_title:ky,options_settings_aiKeys_badges_free:Qy,options_settings_aiKeys_customKeys_title:Vy,options_settings_aiKeys_customKeys_addButton:Zy,options_settings_aiKeys_customKeys_keyPlaceholder:Jy,options_settings_aiKeys_customKeys_namePlaceholder:$y,options_settings_aiKeys_actions_save:Wy,options_settings_aiKeys_actions_test:Fy,options_settings_general_title:Iy,options_settings_general_autoUpdate:eh,options_settings_general_showNotifications:th,options_settings_general_theme:lh,options_settings_general_theme_light:ah,options_settings_general_theme_dark:nh,options_settings_general_theme_system:ih,options_settings_security_title:sh,options_settings_security_checkSignatures:uh,options_settings_security_isolatedMode:oh,options_settings_performance_title:ch,options_settings_performance_maxPlugins:rh,options_settings_performance_cacheData:fh,options_plugins_details_title:dh},mh={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",basic_analysis:"Базовый",deep_analysis:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},yh="Настройки Платформы",hh="Доступные плагины",ph="API Ключи нейросетей",vh="Бесплатный",bh="Пользовательские API ключи",Sh="+ Добавить новый ключ",_h="Введите API ключ",xh="Введите название ключа",Eh="Сохранить все ключи",Ah="Тестировать подключения",Th="Общие настройки",Ch="Автоматическое обновление плагинов",Mh="Показывать уведомления",Oh="Тема интерфейса:",Dh="Светлая",zh="Тёмная",Rh="Системная",wh="Безопасность",Uh="Проверять подписи плагинов",Nh="Изолированный режим выполнения",jh="Производительность",Hh="Максимальное количество активных плагинов:",Ph="Кэширование данных плагинов",Gh="Детали плагина",Kh={options:mh,options_settings_title:yh,options_plugins_title:hh,options_settings_aiKeys_title:ph,options_settings_aiKeys_badges_free:vh,options_settings_aiKeys_customKeys_title:bh,options_settings_aiKeys_customKeys_addButton:Sh,options_settings_aiKeys_customKeys_keyPlaceholder:_h,options_settings_aiKeys_customKeys_namePlaceholder:xh,options_settings_aiKeys_actions_save:Eh,options_settings_aiKeys_actions_test:Ah,options_settings_general_title:Th,options_settings_general_autoUpdate:Ch,options_settings_general_showNotifications:Mh,options_settings_general_theme:Oh,options_settings_general_theme_light:Dh,options_settings_general_theme_dark:zh,options_settings_general_theme_system:Rh,options_settings_security_title:wh,options_settings_security_checkSignatures:Uh,options_settings_security_isolatedMode:Nh,options_settings_performance_title:jh,options_settings_performance_maxPlugins:Hh,options_settings_performance_cacheData:Ph,options_plugins_details_title:Gh},Gd={en:gh,ru:Kh},ng=(u="en")=>({t:Q.useMemo(()=>{const d=Gd[u]||{},c=(S,T)=>T.split(".").reduce((z,ee)=>z&&z[ee]?z[ee]:void 0,S);return S=>{const T=c(d,S);return T!==void 0?T:S}},[u,Gd]),locale:u}),To=({checked:u,onChange:r,disabled:d,label:c,iconOn:S,iconOff:T})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:d?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:u,disabled:d,onChange:z=>r(z.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:u?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:u?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:u?S:T})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),Bh=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),Lh=({children:u})=>{const[r,d]=rs.useState(null),c=rs.useCallback(()=>d(null),[]);if(r)return y.jsx(Bh,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return d(S),null}};class Co{static ALGORITHM="AES-GCM";static KEY_LENGTH=256;static IV_LENGTH=12;static async getEncryptionKey(){try{const r=await chrome.storage.local.get(["encryptionKey"]);if(r.encryptionKey){const T=new Uint8Array(r.encryptionKey);return await crypto.subtle.importKey("raw",T,this.ALGORITHM,!1,["encrypt","decrypt"])}const d=await crypto.subtle.generateKey({name:this.ALGORITHM,length:this.KEY_LENGTH},!0,["encrypt","decrypt"]),c=await crypto.subtle.exportKey("raw",d),S=new Uint8Array(c);return await chrome.storage.local.set({encryptionKey:Array.from(S)}),d}catch(r){throw console.error("Failed to get/create encryption key:",r),new Error("Не удалось инициализировать шифрование")}}static async encrypt(r){try{const d=await this.getEncryptionKey(),c=crypto.getRandomValues(new Uint8Array(this.IV_LENGTH)),S=new TextEncoder().encode(r),T=await crypto.subtle.encrypt({name:this.ALGORITHM,iv:c},d,S),z=new Uint8Array(T),ee=new Uint8Array(c.length+z.length);return ee.set(c),ee.set(z,c.length),btoa(String.fromCharCode(...ee))}catch(d){throw console.error("Encryption failed:",d),new Error("Ошибка шифрования")}}static async decrypt(r){try{const d=await this.getEncryptionKey(),c=new Uint8Array(atob(r).split("").map(ee=>ee.charCodeAt(0))),S=c.slice(0,this.IV_LENGTH),T=c.slice(this.IV_LENGTH),z=await crypto.subtle.decrypt({name:this.ALGORITHM,iv:S},d,T);return new TextDecoder().decode(z)}catch(d){throw console.error("Decryption failed:",d),new Error("Ошибка расшифрования")}}static validateAPIKey(r){return!r||typeof r!="string"?{isValid:!1,error:"Ключ не может быть пустым"}:r.length<10?{isValid:!1,error:"Ключ слишком короткий"}:r.length>200?{isValid:!1,error:"Ключ слишком длинный"}:/[<>\"'&]/.test(r)?{isValid:!1,error:"Ключ содержит недопустимые символы"}:{isValid:!0}}}class mt{static async saveEncryptedKey(r,d){try{const c=Co.validateAPIKey(d);if(!c.isValid)throw new Error(c.error);const S=await Co.encrypt(d),T=await this.getAllEncryptedKeys();T[r]=S,await chrome.storage.local.set({encryptedApiKeys:T})}catch(c){throw console.error("Failed to save encrypted API key:",c),c}}static async getDecryptedKey(r){try{const c=(await this.getAllEncryptedKeys())[r];return c?await Co.decrypt(c):null}catch(d){return console.error("Failed to get decrypted API key:",d),null}}static async removeKey(r){try{const d=await this.getAllEncryptedKeys();delete d[r],await chrome.storage.local.set({encryptedApiKeys:d})}catch(d){throw console.error("Failed to remove API key:",d),d}}static async getAllEncryptedKeys(){try{return(await chrome.storage.local.get(["encryptedApiKeys"])).encryptedApiKeys||{}}catch(r){return console.error("Failed to get encrypted keys:",r),{}}}static async getAllKeyIds(){try{const r=await this.getAllEncryptedKeys();return Object.keys(r)}catch(r){return console.error("Failed to get key IDs:",r),[]}}static async keyExists(r){try{const d=await this.getAllEncryptedKeys();return r in d}catch(d){return console.error("Failed to check key existence:",d),!1}}}const Mo="plugin-ozon-analyzer-settings",cl={basic_analysis:{ru:{llm:"",custom_prompt:"Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки."},en:{llm:"",custom_prompt:"Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages."}},deep_analysis:{ru:{llm:"",custom_prompt:"Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению."},en:{llm:"",custom_prompt:"Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations."}},api_keys:{default:""}},ig=()=>{const[u,r]=Q.useState(cl),[d,c]=Q.useState(!0);Q.useEffect(()=>{S()},[]);const S=async()=>{try{c(!0);const $=(await chrome.storage.local.get([Mo]))[Mo];if($){const ie={...$.api_keys};$.api_keys?.default&&(ie.default=await mt.getDecryptedKey("ozon-analyzer-default")||""),r({...cl,...$,basic_analysis:{ru:{...cl.basic_analysis.ru,...$.basic_analysis?.ru},en:{...cl.basic_analysis.en,...$.basic_analysis?.en}},deep_analysis:{ru:{...cl.deep_analysis.ru,...$.deep_analysis?.ru},en:{...cl.deep_analysis.en,...$.deep_analysis?.en}},api_keys:ie})}else r(cl)}catch(I){console.error("Failed to load plugin settings:",I),r(cl)}finally{c(!1)}},T=async I=>{try{const $={...I};I.api_keys?.default?await mt.saveEncryptedKey("ozon-analyzer-default",I.api_keys.default):await mt.removeKey("ozon-analyzer-default"),$.api_keys={default:""},await chrome.storage.local.set({[Mo]:$}),r(I)}catch($){throw console.error("Failed to save plugin settings:",$),$}};return{settings:u,isLoading:d,updateBasicAnalysisSettings:async(I,$)=>{const ie={...$,basic_analysis:{...$.basic_analysis,[I]:$}};await T(ie)},updateDeepAnalysisSettings:async(I,$)=>{const ie={...$,deep_analysis:{...$.deep_analysis,[I]:$}};await T(ie)},updateAPIKey:async I=>{const $={...u,api_keys:{default:I}};await T($)},getBasicAnalysisSettings:I=>u.basic_analysis[I],getDeepAnalysisSettings:I=>u.deep_analysis[I],getAPIKey:()=>u.api_keys?.default||"",resetToDefaults:async()=>{await T(cl)},saveSettings:T,loadSettings:S}},qh=({promptType:u,language:r,globalAIKeys:d,defaultLLMCurl:c,hasDefaultLLM:S,onLLMChange:T})=>{const{settings:z,updateBasicAnalysisSettings:ee,updateDeepAnalysisSettings:R}=ig(),[v,O]=Q.useState(c),[F,W]=Q.useState(""),I=Q.useRef();Q.useEffect(()=>{(async()=>{const K=`ozon-analyzer-${u}-${r}`,B=await mt.getDecryptedKey(K)||"";W(B)})()},[u,r]),Q.useEffect(()=>{const K=(u==="basic_analysis"?z.basic_analysis[r]:z.deep_analysis[r]).llm;let B=K;K||(S?B="default":B=""),O(B)},[u,r,z,S]);const $=async A=>{O(A),await(u==="basic_analysis"?ee:R)(r,{llm:A,custom_prompt:u==="basic_analysis"?z.basic_analysis[r].custom_prompt:z.deep_analysis[r].custom_prompt}),T(A,A==="default"?F:void 0)},ie=A=>{W(A),I.current&&clearTimeout(I.current),I.current=setTimeout(async()=>{try{const K=`ozon-analyzer-${u}-${r}`;await mt.saveEncryptedKey(K,A),T(v,A)}catch(K){console.error("Failed to save API key:",K)}},500)},ae=()=>[{value:"default",label:"Default LLM"},...d.map(K=>({value:K.id,label:K.name}))];return y.jsxs("div",{style:{marginBottom:"16px"},children:[y.jsxs("label",{style:{display:"block",marginBottom:"8px",fontWeight:"bold"},children:["Выбор нейросети для ",u==="basic_analysis"?"базового":"глубокого"," анализа:"]}),y.jsx("select",{value:v,onChange:A=>$(A.target.value),placeholder:"Выберите нейросеть",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",marginBottom:"8px"},children:ae().map(A=>y.jsx("option",{value:A.value,children:A.label},A.value))}),v==="default"&&y.jsxs("div",{children:[y.jsx("label",{style:{display:"block",marginBottom:"4px",fontSize:"14px"},children:"API ключ:"}),y.jsx("input",{type:"password",value:F,onChange:A=>ie(A.target.value),placeholder:"Введите API ключ",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})]})},Yh=()=>{const{t:u}=ng("ru"),[r,d]=Q.useState([{id:"gemini-flash-lite",name:"Google Gemini Flash Lite - Базовый анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0},{id:"gemini-pro",name:"Gemini 2.5 Pro - Глубокий анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0}]),[c,S]=Q.useState([]),T=Q.useRef({});Q.useEffect(()=>(z(),()=>{Object.values(T.current).forEach(A=>{clearTimeout(A)}),T.current={}}),[]);const z=async()=>{try{console.log("[useAIKeys] Starting to load AI keys...");const K=["gemini-flash-lite","gemini-pro"].map(async V=>{const ce=await mt.getDecryptedKey(V);return console.log(`[useAIKeys] Loaded key ${V}:`,ce?"present":"empty"),{keyId:V,decryptedKey:ce}}),B=await Promise.all(K);console.log("[useAIKeys] Fixed keys results:",B),d(V=>V.map(ce=>{const w=B.find(D=>D.keyId===ce.id);return console.log(`[useAIKeys] Setting key ${ce.id}:`,w?.decryptedKey?"configured":"not_configured"),{...ce,key:w?.decryptedKey||"",status:w?.decryptedKey?"configured":"not_configured"}}));const L=await chrome.storage.local.get(["customKeys"]);if(console.log("[useAIKeys] Custom keys metadata:",L.customKeys),L.customKeys){const V=await Promise.all(L.customKeys.map(async ce=>{const w=await mt.getDecryptedKey(ce.id);return console.log(`[useAIKeys] Custom key ${ce.id}:`,w?"present":"empty"),{...ce,key:w||"",status:w?"configured":"not_configured"}}));console.log("[useAIKeys] Setting custom keys:",V),S(V)}else console.log("[useAIKeys] No custom keys found"),S([]);console.log("[useAIKeys] AI keys loaded successfully")}catch(A){console.error("Failed to load AI keys:",A),d(K=>K.map(B=>({...B,key:"",status:"not_configured"}))),S([])}};return{aiKeys:r,customKeys:c,saveAIKeys:async()=>{try{console.log("[useAIKeys] Starting to save AI keys..."),console.log("[useAIKeys] Current aiKeys:",r),console.log("[useAIKeys] Current customKeys:",c);const A=r.map(async L=>{L.key?(console.log(`[useAIKeys] Saving fixed key ${L.id}`),await mt.saveEncryptedKey(L.id,L.key)):(console.log(`[useAIKeys] Removing fixed key ${L.id}`),await mt.removeKey(L.id))});await Promise.all(A);const K=c.map(async L=>{L.key?(console.log(`[useAIKeys] Saving custom key ${L.id}`),await mt.saveEncryptedKey(L.id,L.key)):(console.log(`[useAIKeys] Removing custom key ${L.id}`),await mt.removeKey(L.id))});await Promise.all(K);const B=c.map(L=>({id:L.id,name:L.name,isFixed:!1,isFree:!1}));console.log("[useAIKeys] Saving custom keys metadata:",B),await chrome.storage.local.set({customKeys:B}),d(L=>L.map(V=>({...V,status:V.key?"configured":"not_configured"}))),S(L=>L.map(V=>({...V,status:V.key?"configured":"not_configured"}))),console.log("[useAIKeys] AI keys saved successfully"),alert(u("options.settings.aiKeys.messages.saved"))}catch(A){console.error("Failed to save AI keys:",A),alert(u("options.settings.aiKeys.messages.saveError"))}},testAIKeys:async()=>{d(A=>A.map(K=>({...K,status:"testing"}))),S(A=>A.map(K=>({...K,status:"testing"}))),setTimeout(()=>{d(A=>A.map(K=>({...K,status:K.key?"configured":"not_configured"}))),S(A=>A.map(K=>({...K,status:K.key?"configured":"not_configured"}))),alert(u("options.settings.aiKeys.messages.testComplete"))},2e3)},addCustomKey:()=>{const A={id:`custom-${Date.now()}`,name:`Пользовательский ключ ${c.length+1}`,key:"",status:"not_configured"};S(K=>[...K,A])},removeCustomKey:async A=>{try{await mt.removeKey(A),S(K=>K.filter(B=>B.id!==A))}catch(K){console.error("Failed to remove custom key:",K),S(B=>B.filter(L=>L.id!==A))}},updateKey:(A,K,B=!1)=>{console.log(`[useAIKeys] Updating key ${A} with value:`,K?"present":"empty"),B?S(L=>L.map(V=>V.id===A?{...V,key:K,status:K?"configured":"not_configured"}:V)):d(L=>L.map(V=>V.id===A?{...V,key:K,status:K?"configured":"not_configured"}:V)),T.current[A]&&clearTimeout(T.current[A]),T.current[A]=setTimeout(async()=>{try{if(console.log(`[useAIKeys] Auto-saving key ${A} after delay`),K?await mt.saveEncryptedKey(A,K):await mt.removeKey(A),B){const V=(await chrome.storage.local.get(["customKeys"])).customKeys||[],ce=V.map(D=>D.id===A?{...D,name:D.name}:D);ce.findIndex(D=>D.id===A)===-1&&K&&ce.push({id:A,name:`Пользовательский ключ ${V.length+1}`,isFixed:!1,isFree:!1}),await chrome.storage.local.set({customKeys:ce.filter(D=>K||D.id!==A)})}console.log(`[useAIKeys] Key ${A} auto-saved successfully`)}catch(L){console.error(`[useAIKeys] Failed to auto-save key ${A}:`,L)}finally{delete T.current[A]}},1e3)},updateCustomKeyName:(A,K)=>{S(B=>B.map(L=>L.id===A?{...L,name:K,status:L.key?"configured":"not_configured"}:L))},getStatusText:A=>{switch(A){case"configured":return u("options.settings.aiKeys.status.configured");case"not_configured":return u("options.settings.aiKeys.status.notConfigured");case"testing":return u("options.settings.aiKeys.status.testing");default:return u("options.settings.aiKeys.status.notConfigured")}},getStatusClass:A=>{switch(A){case"configured":return"status-configured";case"not_configured":return"status-not-configured";case"testing":return"status-testing";default:return""}},getAPIKeyForMCP:async A=>{try{return await mt.getDecryptedKey(A)}catch(K){return console.error("Failed to get API key for MCP:",K),null}},isKeyConfigured:async A=>{try{return await mt.keyExists(A)}catch(K){return console.error("Failed to check if key is configured:",K),!1}}}},Xh=(...u)=>u.filter(Boolean).join(" "),kh=({value:u,manifest:r,disabled:d,onSave:c,locale:S,t:T,globalAIKeys:z,pluginSettings:ee})=>{const[R,v]=Q.useState("basic_analysis"),[O,F]=Q.useState("ru"),[W,I]=Q.useState(""),$=(L,V)=>{try{const ce=r?.options?.prompts;return!ce||!((ce[L]||{})[V]||{}).LLM?.default?"default":L==="basic_analysis"?"gemini-flash-lite":L==="deep_analysis"?"gemini-pro":"default"}catch{return"default"}},ie=(L,V)=>{try{const ce=r?.options?.prompts;return ce?!!((ce[L]||{})[V]||{}).LLM?.default:!1}catch{return!1}},ae=()=>{try{const L=r?.options?.prompts;if(!L)return"";const w=((L[R]||{})[O]||{}).default||"";return typeof w=="object"?JSON.stringify(w,null,2):w}catch{return""}},A=()=>{try{const ce=((u||{})[R]||{})[O];return typeof ce=="string"?ce:ce==null?"":JSON.stringify(ce,null,2)}catch{return""}};Q.useEffect(()=>{I(A())},[R,O,u]);const K=()=>{I(ae())},B=()=>{try{const L={...u};L[R]||(L[R]={ru:"",en:""}),L[R][O]=W,c(L)}catch(L){console.error("Failed to save custom prompt:",L)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:T("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:T("options.plugins.prompts.type")}),y.jsxs("select",{value:R,onChange:L=>v(L.target.value),disabled:d,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"basic_analysis",children:T("options.plugins.prompts.basic_analysis")}),y.jsx("option",{value:"deep_analysis",children:T("options.plugins.prompts.deep_analysis")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:T("options.plugins.prompts.language")}),y.jsxs("select",{value:O,onChange:L=>F(L.target.value),disabled:d,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:T("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:T("options.plugins.prompts.english")})]})]})]}),y.jsx(qh,{promptType:R,language:O,globalAIKeys:z,defaultLLMCurl:$(R,O),hasDefaultLLM:ie(R,O),onLLMChange:(L,V)=>{console.log(`LLM changed for ${R} ${O}:`,L,V)}}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:T("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:ae(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:K,disabled:d,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:d?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:T("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:T("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:W,onChange:L=>I(L.target.value),disabled:d,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:B,disabled:d,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:d?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:T("options.plugins.prompts.save")})})]})})},Qh=u=>{const{selectedPlugin:r,locale:d="en",onUpdateSetting:c}=u,{t:S}=ng(d),{aiKeys:T}=Yh(),{settings:z}=ig(),[ee,R]=Q.useState(null),[v,O]=Q.useState(null),F=w=>w?typeof w=="string"?w:w[d]||w.ru||w.en||"":"";if(Q.useEffect(()=>{r?.manifest?.options&&$()},[r?.id]),!r||typeof r!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:S("options_plugins_details_title")}),y.jsx("p",{children:S("options.plugins.details.selectPlugin")})]});const W=r.settings||{enabled:!0,autorun:!1},I=r.manifest?.host_permissions||[],$=async()=>{if(!(!r||!r.manifest?.options||v!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const D=Object.keys(r.manifest.options).map(De=>`${r.id}_${De}`),le=await chrome.storage.local.get(D),ve={};Object.entries(le).forEach(([De,he])=>{const Ne=De.replace(`${r.id}_`,"");he!==void 0&&(ve[Ne]=he)}),O(ve)}}catch(w){console.warn("Failed to load custom settings from chrome.storage.local:",w)}},ie=async(w,D)=>{if(r)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const le=`${r.id}_${w}`;await chrome.storage.local.set({[le]:D}),O(ve=>({...ve,[w]:D})),w==="prompts"&&await ae()}else console.warn("chrome.storage.local is not available")}catch(le){throw console.error(`Failed to save setting ${w} to chrome.storage.local:`,le),le}},ae=async()=>{if(r)try{const w=`${r.id}_prompts`,D=await chrome.storage.local.get([w]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${r.id}`),console.log(` Storage key: ${w}`),console.log(" Сохраненные промпты:",D[w]),D[w]){const le=D[w];console.log(" Структура промптов:"),console.log(` - basic_analysis.ru: ${le.basic_analysis?.ru?"✓":"✗"} (${le.basic_analysis?.ru?.length||0} символов)`),console.log(` - basic_analysis.en: ${le.basic_analysis?.en?"✓":"✗"} (${le.basic_analysis?.en?.length||0} символов)`),console.log(` - deep_analysis.ru: ${le.deep_analysis?.ru?"✓":"✗"} (${le.deep_analysis?.ru?.length||0} символов)`),console.log(` - deep_analysis.en: ${le.deep_analysis?.en?"✓":"✗"} (${le.deep_analysis?.en?.length||0} символов)`)}}catch(w){console.error("Ошибка диагностики промптов:",w)}},A=(w,D)=>{if(v&&v[w]!==void 0)return v[w];if(w==="prompts"&&typeof D=="object"&&D!==null){const le=D,ve={basic_analysis:{ru:{},en:{}},deep_analysis:{ru:{},en:{}}};return le.basic_analysis?.ru?.default&&(ve.basic_analysis.ru=le.basic_analysis.ru.default),le.basic_analysis?.en?.default&&(ve.basic_analysis.en=le.basic_analysis.en.default),le.deep_analysis?.ru?.default&&(ve.deep_analysis.ru=le.deep_analysis.ru.default),le.deep_analysis?.en?.default&&(ve.deep_analysis.en=le.deep_analysis.en.default),ve}return D},K=(w,D)=>{const le=A(w,D.default),ve=ee===w||!(W.enabled??!0),De=F(D.label),he=F(D.description);if(w==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(kh,{value:le,manifest:r.manifest,disabled:ve,onSave:Ne=>B(w,Ne),locale:d,t:S,globalAIKeys:T,pluginSettings:z})},w);if(D.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:le,disabled:ve,onChange:Ne=>B(w,Ne),label:y.jsxs(y.Fragment,{children:[De,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"})]})})},w);if(D.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[De,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"}),y.jsx("select",{id:w,name:w,value:le,disabled:ve,onChange:Ne=>B(w,Ne.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:D.values?.map(Ne=>y.jsx("option",{value:Ne,children:F(D.labels?.[Ne])||Ne},Ne))})]})},w);if(D.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[De,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"text",id:w,name:w,value:le,disabled:ve,onChange:Ne=>B(w,Ne.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},w);if(D.type==="number"){const Ne=lt=>{const we=parseFloat(lt.target.value);if(isNaN(we))return;let M=we;D.min!==void 0&&MD.max&&(M=D.max),B(w,M)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[De,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"number",id:w,name:w,value:le,disabled:ve,onChange:Ne,min:D.min,max:D.max,step:D.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},w)}return null},B=async(w,D)=>{if(!r)return;const le=r.manifest?.options;if(le&&le[w]){v===null&&await $();try{R(w),await ie(w,D)}catch(ve){console.error(`Failed to update custom setting ${w}:`,ve)}finally{R(null)}}else if(c)try{R(w),await c(r.id,w,D)}catch(ve){console.error(`Failed to update setting ${w}:`,ve)}finally{R(null)}},V=Object.entries(r.manifest?.options??{}).map(([w,D])=>K(w,D)).filter(w=>w!==null),ce=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:W.enabled??!0,disabled:ee==="enabled",onChange:w=>B("enabled",w),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(To,{checked:W.autorun??!1,disabled:ee==="autorun"||!(W.enabled??!0),onChange:w=>B("autorun",w),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(Lh,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:r.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",r.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:Xh("status-badge",W.enabled?"status-active":"status-inactive"),children:W.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",r.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",r.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:r.description})]}),I.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:I.map((w,D)=>y.jsx("li",{children:w},D))})]}),Array.isArray(r.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(r.manifest?.permissions??[]).map((w,D)=>y.jsx("li",{children:w},D))})]}):null,y.jsx(ce,{}),r.manifest?.options&&Object.keys(r.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),V]}):null]})]})})},Vh=({plugin:u,onUpdateSetting:r})=>{const d=r?async(S,T,z)=>{try{return await r(S,T,z),!0}catch(ee){return console.error(`Failed to update setting ${T}:`,ee),!1}}:void 0,c={selectedPlugin:{...u,description:u.description||"Описание не указано",icon:u.icon||"",manifest:u.manifest||{}},locale:"ru",onUpdateSetting:d};return y.jsx(Qh,{...c})},Kd=function(u){if(!u)return"unknown-page";try{const r=new URL(u);return r.search="",r.hash="",r.toString()}catch{return u}},Zh=({pluginId:u,pageKey:r,debounceMs:d=1e3})=>{const[c,S]=Q.useState(""),[T,z]=Q.useState(!1),[ee,R]=Q.useState(!1),[v,O]=Q.useState(null),[F,W]=Q.useState(""),I=Q.useRef(null),$=Q.useRef(""),ie=Q.useCallback(async B=>{if(B!==$.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:u,pageKey:r,text:B});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:B}),$.current=B,z(!0),O(null),W(B),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:u,pageKey:r,text:B})}catch(L){console.error("[useLazyChatSync] Error saving draft:",L),O("Ошибка сохранения черновика"),z(!1)}}},[u,r]),ae=Q.useCallback(async()=>{R(!0),O(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:u,pageKey:r});try{const B=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r});B?.draftText?(S(B.draftText),$.current=B.draftText,z(!0),W(B.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:u,pageKey:r,draft:B.draftText})):(S(""),$.current="",z(!1),W(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:u,pageKey:r}))}catch(B){console.error("[useLazyChatSync] Error loading draft:",B),O("Ошибка загрузки черновика")}finally{R(!1)}},[u,r]),A=Q.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:u,pageKey:r});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:""}),$.current="",z(!1),O(null),W(""),S(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:u,pageKey:r})}catch(B){console.error("[useLazyChatSync] Error clearing draft:",B),O("Ошибка очистки черновика")}},[u,r]),K=Q.useCallback(B=>{S(B),I.current&&clearTimeout(I.current),B.length===0?A():I.current=setTimeout(()=>{ie(B)},d),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:u,pageKey:r,text:B})},[d,ie,A,u,r]);return Q.useEffect(()=>()=>{I.current&&clearTimeout(I.current)},[]),Q.useEffect(()=>{ae()},[ae]),Q.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",r),z(!1),R(!1),O(null),$.current="",I.current&&(clearTimeout(I.current),I.current=null),ae()},[r,ae]),{message:c,setMessage:K,isDraftSaved:T,isDraftLoading:ee,draftError:v,loadDraft:ae,clearDraft:A,draftText:F}};var cs={exports:{}},Jh=cs.exports,Bd;function $h(){return Bd||(Bd=1,(function(u,r){(function(d,c){c()})(Jh,function(){function d(v,O){return typeof O>"u"?O={autoBom:!1}:typeof O!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),O={autoBom:!O}),O.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(v.type)?new Blob(["\uFEFF",v],{type:v.type}):v}function c(v,O,F){var W=new XMLHttpRequest;W.open("GET",v),W.responseType="blob",W.onload=function(){R(W.response,O,F)},W.onerror=function(){console.error("could not download file")},W.send()}function S(v){var O=new XMLHttpRequest;O.open("HEAD",v,!1);try{O.send()}catch{}return 200<=O.status&&299>=O.status}function T(v){try{v.dispatchEvent(new MouseEvent("click"))}catch{var O=document.createEvent("MouseEvents");O.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),v.dispatchEvent(O)}}var z=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof ss=="object"&&ss.global===ss?ss:void 0,ee=z.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),R=z.saveAs||(typeof window!="object"||window!==z?function(){}:"download"in HTMLAnchorElement.prototype&&!ee?function(v,O,F){var W=z.URL||z.webkitURL,I=document.createElement("a");O=O||v.name||"download",I.download=O,I.rel="noopener",typeof v=="string"?(I.href=v,I.origin===location.origin?T(I):S(I.href)?c(v,O,F):T(I,I.target="_blank")):(I.href=W.createObjectURL(v),setTimeout(function(){W.revokeObjectURL(I.href)},4e4),setTimeout(function(){T(I)},0))}:"msSaveOrOpenBlob"in navigator?function(v,O,F){if(O=O||v.name||"download",typeof v!="string")navigator.msSaveOrOpenBlob(d(v,F),O);else if(S(v))c(v,O,F);else{var W=document.createElement("a");W.href=v,W.target="_blank",setTimeout(function(){T(W)})}}:function(v,O,F,W){if(W=W||open("","_blank"),W&&(W.document.title=W.document.body.innerText="downloading..."),typeof v=="string")return c(v,O,F);var I=v.type==="application/octet-stream",$=/constructor/i.test(z.HTMLElement)||z.safari,ie=/CriOS\/[\d]+/.test(navigator.userAgent);if((ie||I&&$||ee)&&typeof FileReader<"u"){var ae=new FileReader;ae.onloadend=function(){var B=ae.result;B=ie?B:B.replace(/^data:[^;]*;/,"data:attachment/file;"),W?W.location.href=B:location=B,W=null},ae.readAsDataURL(v)}else{var A=z.URL||z.webkitURL,K=A.createObjectURL(v);W?W.location=K:location.href=K,W=null,setTimeout(function(){A.revokeObjectURL(K)},4e4)}});z.saveAs=R.saveAs=R,u.exports=R})})(cs)),cs.exports}var Wh=$h(),Ya;(function(u){u.Local="local",u.Sync="sync",u.Managed="managed",u.Session="session"})(Ya||(Ya={}));var Go;(function(u){u.ExtensionPagesOnly="TRUSTED_CONTEXTS",u.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(Go||(Go={}));const qa=globalThis.chrome,Ld=async(u,r)=>{const d=S=>typeof S=="function",c=S=>S instanceof Promise;return d(u)?(c(u),u(r)):u};let qd=!1;const Yd=u=>{if(qa&&!qa.storage[u])throw new Error(`"storage" permission in manifest.ts: "storage ${u}" isn't defined`)},sg=(u,r,d)=>{let c=null,S=!1,T=[];const z=d?.storageEnum??Ya.Local,ee=d?.liveUpdate??!1,R=d?.serialization?.serialize??(ae=>ae),v=d?.serialization?.deserialize??(ae=>ae);qd===!1&&z===Ya.Session&&d?.sessionAccessForContentScripts===!0&&(Yd(z),qa?.storage[z].setAccessLevel({accessLevel:Go.ExtensionPagesAndContentScripts}).catch(ae=>{console.error(ae),console.error("Please call .setAccessLevel() into different context, like a background script.")}),qd=!0);const O=async()=>{Yd(z);const ae=await qa?.storage[z].get([u]);return ae?v(ae[u])??r:r},F=async ae=>{S||(c=await O()),c=await Ld(ae,c),await qa?.storage[z].set({[u]:R(c)}),$()},W=ae=>(T=[...T,ae],()=>{T=T.filter(A=>A!==ae)}),I=()=>c,$=()=>{T.forEach(ae=>ae())},ie=async ae=>{if(ae[u]===void 0)return;const A=v(ae[u].newValue);c!==A&&(c=await Ld(A,c),$())};return O().then(ae=>{c=ae,S=!0,$()}),ee&&qa?.storage[z].onChanged.addListener(ie),{get:O,set:F,getSnapshot:I,subscribe:W}},Xd=sg("theme-storage-key",{theme:"system",isLight:ug()},{storageEnum:Ya.Local,liveUpdate:!0});function ug(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Oo={...Xd,toggle:async()=>{await Xd.set(u=>{let r;switch(u.theme){case"light":r="dark";break;case"dark":r="system";break;case"system":default:r="light";break}const d=r==="system"?ug():r==="light";return{theme:r,isLight:d}})}},Do=sg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Ya.Local,liveUpdate:!0}),kd={...Do,setAlignment:async u=>{await Do.set({alignment:u})},getAlignment:async()=>(await Do.get()).alignment},Fh=({plugin:u,currentView:r,isRunning:d,isPaused:c,currentTabUrl:S,onStart:T,onPause:z,onStop:ee,onClose:R})=>{const[v,O]=Q.useState("chat"),[F,W]=Q.useState(Kd(S)),[I,$]=Q.useState("left");Q.useEffect(()=>{const U=Kd(S);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:F,newPageKey:U,currentTabUrl:S,timestamp:new Date().toISOString()}),W(U)},[S]),Q.useEffect(()=>{const U=async()=>{const p=await kd.getAlignment();$(p)};return U(),kd.subscribe(()=>{U()})},[]);const{message:ie,setMessage:ae,isDraftSaved:A,isDraftLoading:K,draftError:B,loadDraft:L,clearDraft:V,draftText:ce}=Zh({pluginId:u.id,pageKey:F,debounceMs:1e3}),[w,D]=Q.useState([]),[le,ve]=Q.useState(!1),[De,he]=Q.useState(null),[Ne,lt]=Q.useState(60),[we,M]=Q.useState(!1),J=Q.useRef(null),k=Q.useRef(null);Q.useEffect(()=>{},[d]);const Te=()=>{T()},g=u.name||(typeof u.manifest?.name=="string"?u.manifest.name:"")||u.id,N=u.id,te=Q.useCallback(async U=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...U,messageId:h};try{return await chrome.runtime.sendMessage(p)}catch(Y){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",Y),Y}},[]),Z=Q.useCallback(U=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...U,messageId:h};chrome.runtime.sendMessage(p)},[]),ne=Q.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const U={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},h={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},Y=H=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:H,hasMessages:H&&"messages"in H,hasChat:H&&"chat"in H,messagesValue:H?.messages,chatValue:H?.chat,isMessagesArray:Array.isArray(H?.messages),isChatArray:Array.isArray(H?.chat),responseType:typeof H,responseKeys:H?Object.keys(H):"response is null/undefined",timestamp:new Date().toISOString()}),H===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let P=null;const X=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],fe=(_e,be)=>{let xe=_e;for(const qe of be)if(xe&&typeof xe=="object"&&qe in xe)xe=xe[qe];else return;return xe};if(Array.isArray(H))P=H,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:P.length,firstMessage:P[0]?{id:P[0].id,content:P[0].content||P[0].text,role:P[0].role,timestamp:P[0].timestamp}:"no messages"});else if(H&&typeof H=="object"){if(H.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",H.error),he(`Ошибка от background: ${H.error}`),D([]);return}for(const{path:_e,description:be}of X){const xe=fe(H,_e);if(Array.isArray(xe)){P=xe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${be}':`,{length:P.length,firstMessage:P[0]?{id:P[0].id,content:P[0].content||P[0].text,role:P[0].role,timestamp:P[0].timestamp}:"no messages"});break}}P||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof H,responseKeys:Object.keys(H),responseSample:JSON.stringify(H).substring(0,500),timestamp:new Date().toISOString()}),P=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:H,responseType:typeof H,responseStringified:JSON.stringify(H).substring(0,200),timestamp:new Date().toISOString()}),P=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:P,isArray:Array.isArray(P),length:P?.length,firstMessage:P?.[0],firstMessageType:P?.[0]?typeof P[0]:"none"}),Array.isArray(P)&&P.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",P.length);const _e=P.filter(be=>!be||typeof be!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",be),!1):!0).map((be,xe)=>{try{let qe=be.content||be.text||"";typeof qe=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",qe),qe=JSON.stringify(qe)):qe==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),qe=""):qe=String(qe);const fl={id:be.id||String(be.timestamp||Date.now()+xe),text:qe,isUser:be.role?be.role==="user":!!be.isUser,timestamp:be.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${xe}:`,{id:fl.id,textLength:fl.text.length,textType:typeof fl.text,isUser:fl.isUser}),fl}catch(qe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${xe}:`,qe,be),{id:`error_${Date.now()}_${xe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:P.length,convertedCount:_e.length,firstConverted:_e[0],allConverted:_e.map(be=>{try{const xe=typeof be.text=="string"?be.text.substring(0,50):String(be.text||"").substring(0,50);return{id:be.id,text:xe,isUser:be.isUser,textType:typeof be.text}}catch(xe){return console.warn("[PluginControlPanel] Error processing message text:",xe,be),{id:be.id,text:"[ERROR: invalid text]",isUser:be.isUser,textType:typeof be.text}}})}),D(_e)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),Y(U)}catch(H){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",H)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),Y(h)}catch(H){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",H)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),Y(p)}catch(H){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",H)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);Q.useCallback(U=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:U,hasMessages:U&&"messages"in U,hasChat:U&&"chat"in U,messagesValue:U?.messages,chatValue:U?.chat,isMessagesArray:Array.isArray(U?.messages),isChatArray:Array.isArray(U?.chat),responseType:typeof U,responseKeys:U?Object.keys(U):"response is null/undefined",timestamp:new Date().toISOString()}),U===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let h=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],Y=(H,P)=>{let X=H;for(const fe of P)if(X&&typeof X=="object"&&fe in X)X=X[fe];else return;return X};if(Array.isArray(U))h=U,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});else if(U&&typeof U=="object"){if(U.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",U.error),he(`Ошибка от background: ${U.error}`),D([]);return}for(const{path:H,description:P}of p){const X=Y(U,H);if(Array.isArray(X)){h=X,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${P}':`,{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});break}}h||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof U,responseKeys:Object.keys(U),responseSample:JSON.stringify(U).substring(0,500),timestamp:new Date().toISOString()}),h=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:U,responseType:typeof U,responseStringified:JSON.stringify(U).substring(0,200),timestamp:new Date().toISOString()}),h=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:h,isArray:Array.isArray(h),length:h?.length,firstMessage:h?.[0],firstMessageType:h?.[0]?typeof h[0]:"none"}),Array.isArray(h)&&h.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",h.length);const H=h.filter(P=>!P||typeof P!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",P),!1):!0).map((P,X)=>{try{let fe=P.content||P.text||"",_e=P.timestamp||Date.now();if(typeof fe=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",fe),fe=JSON.stringify(fe);else if(fe==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),fe="";else{fe=String(fe),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${X}:`,fe);try{const xe=JSON.parse(fe);typeof xe=="object"&&xe!==null&&"content"in xe&&(console.log("[PluginControlPanel] Распарсен JSON из content:",xe),fe=String(xe.content||""),xe.timestamp&&typeof xe.timestamp=="number"&&(_e=xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${X}:`,fe)}const be={id:P.id||String(_e+X),text:fe,isUser:P.role?P.role==="user":!!P.isUser,timestamp:_e};return console.log(`[PluginControlPanel] Конвертировано сообщение ${X}:`,{id:be.id,textLength:be.text.length,textType:typeof be.text,isUser:be.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${X}:`,be.text),be}catch(fe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${X}:`,fe,P),{id:`error_${Date.now()}_${X}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:h.length,convertedCount:H.length,firstConverted:H[0],allConverted:H.map(P=>{try{const X=typeof P.text=="string"?P.text.substring(0,50):String(P.text||"").substring(0,50);return{id:P.id,text:X,isUser:P.isUser,textType:typeof P.text}}catch(X){return console.warn("[PluginControlPanel] Error processing message text:",X,P),{id:P.id,text:"[ERROR: invalid text]",isUser:P.isUser,textType:typeof P.text}}})}),D(H)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const ye=Q.useCallback(async()=>{ve(!0),he(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:N,pageKey:F,currentTabUrl:S,timestamp:new Date().toISOString(),isRunning:d,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const U=await te({type:"GET_PLUGIN_CHAT",pluginId:N,pageKey:F});console.log("[PluginControlPanel] loadChat - получен ответ от background:",U),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:le,messagesCount:w.length,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),U?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",U.error),he(`Ошибка загрузки чата: ${U.error}`),D([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(h=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:h,hasMessages:h&&"messages"in h,hasChat:h&&"chat"in h,messagesValue:h?.messages,chatValue:h?.chat,isMessagesArray:Array.isArray(h?.messages),isChatArray:Array.isArray(h?.chat),responseType:typeof h,responseKeys:h?Object.keys(h):"response is null/undefined",timestamp:new Date().toISOString()}),h===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let p=null;const Y=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],H=(P,X)=>{let fe=P;for(const _e of X)if(fe&&typeof fe=="object"&&_e in fe)fe=fe[_e];else return;return fe};if(Array.isArray(h))p=h,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(h&&typeof h=="object"){if(h.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",h.error),he(`Ошибка от background: ${h.error}`),D([]);return}for(const{path:P,description:X}of Y){const fe=H(h,P);if(Array.isArray(fe)){p=fe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${X}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof h,responseKeys:Object.keys(h),responseSample:JSON.stringify(h).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:h,responseType:typeof h,responseStringified:JSON.stringify(h).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const P=p.filter(X=>!X||typeof X!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",X),!1):!0).map((X,fe)=>{try{let _e=X.content||X.text||"",be=X.timestamp||Date.now();if(typeof _e=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",_e),_e=JSON.stringify(_e);else if(_e==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),_e="";else{_e=String(_e);try{const qe=JSON.parse(_e);typeof qe=="object"&&qe!==null&&"content"in qe&&(console.log("[PluginControlPanel] Распаршен JSON из content:",qe),_e=String(qe.content||""),qe.timestamp&&typeof qe.timestamp=="number"&&(be=qe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const xe={id:X.id||String(be+fe),text:_e,isUser:X.role?X.role==="user":!!X.isUser,timestamp:be};return console.log(`[PluginControlPanel] Конвертировано сообщение ${fe}:`,{id:xe.id,textLength:xe.text.length,textType:typeof xe.text,isUser:xe.isUser}),xe}catch(_e){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${fe}:`,_e,X),{id:`error_${Date.now()}_${fe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:P.length,firstConverted:P[0],allConverted:P.map(X=>{try{const fe=typeof X.text=="string"?X.text.substring(0,50):String(X.text||"").substring(0,50);return{id:X.id,text:fe,isUser:X.isUser,textType:typeof X.text}}catch(fe){return console.warn("[PluginControlPanel] Error processing message text:",fe,X),{id:X.id,text:"[ERROR: invalid text]",isUser:X.isUser,textType:typeof X.text}}})}),D(P)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(U),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(U){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",U),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:le,messagesCount:w.length,errorType:typeof U,errorMessage:U instanceof Error?U.message:String(U),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:U,errorType:typeof U,errorMessage:U instanceof Error?U.message:String(U),errorStack:U instanceof Error?U.stack:"No stack trace",errorName:U instanceof Error?U.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:N,pageKey:F}),ve(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let h="Ошибка связи с background";U instanceof Error?(h+=`: ${U.message}`,U.name==="TypeError"&&U.message.includes("substring")&&(h+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:U,stack:U.stack,context:{pluginId:N,pageKey:F}}))):h+=`: ${String(U)}`,he(h),D([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[N,F,te,S,d,c]);Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:N,pageKey:F,currentTabUrl:S,timestamp:new Date().toISOString()}),ye().catch(U=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",U)})},[ye]),Q.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),ye().catch(U=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",U)}),L()},[F,ye,L]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:N,pageKey:F,timestamp:new Date().toISOString()});const U=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===N&&p.pageKey===F&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:le,messagesCount:w.length,currentPageKey:F,pluginId:N,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),ye().catch(Y=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",Y)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:le,messagesCount:w.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),ve(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),he(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,H=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const X=JSON.parse(Y);typeof X=="object"&&X!==null&&"content"in X&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",X),Y=String(X.content||""),X.timestamp&&typeof X.timestamp=="number"&&(H=X.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const P={id:p.message.id||`pyodide_${H}_${Math.random()}`,text:Y,isUser:!1,timestamp:H};console.log("[PluginControlPanel] Adding Pyodide message to chat:",P),D(X=>[...X,P]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",Y,p);const H={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(P=>[...P,H])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},h=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(U),chrome.runtime.onMessage.removeListener(h),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(U),chrome.runtime.onMessage.removeListener(h)}},[N,F,Z,ye]),Q.useEffect(()=>{r==="chat"&&L()},[r,L]),Q.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:N,pageKey:F,draftText:ce,message:ie,currentView:r,isRunning:d,isPaused:c})}),Q.useEffect(()=>{const U=console.error;return console.error=(...h)=>{const p=h.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",h),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),U.apply(console,h)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=U,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),Q.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const U=h=>{const p=h.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,H=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const X=JSON.parse(Y);typeof X=="object"&&X!==null&&"content"in X&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",X),Y=String(X.content||""),X.timestamp&&typeof X.timestamp=="number"&&(H=X.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const P={id:p.message.id||`pyodide_${H}_${Math.random()}`,text:Y,isUser:!1,timestamp:H};console.log("[PluginControlPanel] Adding Pyodide message to chat:",P),D(X=>[...X,P]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",Y,p);const H={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(P=>[...P,H])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",U),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",U),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),Q.useEffect(()=>{typeof ce=="string"&&(ae(ce),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",ce))},[ce,ae]);const de=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:ie}),!ie.trim())return;const U={id:Date.now().toString(),text:ie.trim(),timestamp:Date.now()};ae(""),he(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),Z({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:N,pageKey:F,message:{role:"user",content:U.text,timestamp:U.timestamp}}),V()};Q.useEffect(()=>{const U=p=>{if(!we)return;const Y=document.querySelector(".chat-view");if(!Y)return;const H=Y.getBoundingClientRect(),P=H.bottom-p.clientY,X=100,fe=H.height-80;P>=X&&P<=fe&<(H.height-P)},h=()=>{M(!1),document.body.style.cursor="",document.body.style.userSelect=""};return we&&(document.addEventListener("mousemove",U),document.addEventListener("mouseup",h)),()=>{document.removeEventListener("mousemove",U),document.removeEventListener("mouseup",h)}},[we]),Q.useEffect(()=>{J.current?.scrollIntoView({behavior:"smooth"})},[w]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:w.length,firstMessage:w[0]?{id:w[0].id,text:typeof w[0].text=="string"?w[0].text.substring(0,50):String(w[0].text||"").substring(0,50),isUser:w[0].isUser,timestamp:w[0].timestamp,textType:typeof w[0].text}:null,allMessages:w.map(U=>{try{const h=typeof U.text=="string"?U.text.substring(0,30):String(U.text||"").substring(0,30);return{id:U.id,text:h,isUser:U.isUser,textType:typeof U.text}}catch(h){return console.warn("[PluginControlPanel] Error in message logging:",h,U),{id:U.id,text:"[LOGGING ERROR]",isUser:U.isUser,textType:typeof U.text}}}),timestamp:new Date().toISOString()})},[w]),Q.useEffect(()=>{r==="chat"&&setTimeout(()=>k.current?.focus(),100)},[r]);const ze=U=>{ae(U.target.value);const h=U.target;h.style.height="auto";const p=Math.min(Math.max(h.scrollHeight,60),200);h.style.height=`${p}px`,lt(p)},je=U=>{U.key==="Enter"&&!U.shiftKey&&(U.preventDefault(),de())},ut=()=>{ve(!0),he(null),Z({type:"DELETE_PLUGIN_CHAT",pluginId:N,pageKey:F}),D([]),V()},zt=()=>{const U=JSON.stringify(w,null,2),h=new Blob([U],{type:"application/json"});Wh.saveAs(h,`plugin-chat-${N}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":I},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:u.iconUrl||`plugins/${u.id}/${u.icon||"icon.svg"}`,alt:`${g} icon`,onError:U=>{const h=typeof g=="string"&&g.length>0?g.charAt(0):"P";U.currentTarget.src=`data:image/svg+xml;utf8,${h}`}}),y.jsx("h3",{children:g})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${d?"stop-mode":"start-mode"}`,onClick:Te,disabled:d||c,title:d?"Остановить":"Запустить",children:d?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:z,disabled:!d||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:ee,disabled:!d,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:R,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${v==="chat"?"active":""}`,onClick:()=>O("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${v==="details"?"active":""}`,onClick:()=>O("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[v==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:ut,disabled:le||!!De,children:le?"Очистка...":De?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:zt,disabled:le||!!De,children:le?"Экспорт...":De?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ne,disabled:le,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[le&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),De&&y.jsx("div",{className:"chat-error",children:De}),!le&&!De&&w.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:w.map((U,h)=>{console.log("[PluginControlPanel] render message:",h,U);let p=U.text,Y=U.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",h,":",U.text);try{const H=JSON.parse(p);if(typeof H=="object"&&H!==null&&"content"in H){console.log("[PluginControlPanel] Парсинг JSON в рендере:",H);let P=H.content;typeof P=="object"?p=JSON.stringify(P):p=String(P||""),H.timestamp&&typeof H.timestamp=="number"&&(Y=H.timestamp)}else if(typeof H=="string")p=H;else if(typeof H=="object"&&H!==null){const P=Object.values(H).filter(X=>typeof X=="string");P.length>0?p=String(P[0]):p=JSON.stringify(H)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",h,":",p),y.jsx("div",{className:`chat-message ${U.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(Y).toLocaleTimeString()})]})},U.id||h)})}),y.jsx("div",{ref:J})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:k,className:"message-textarea",value:ie,onChange:ze,onKeyPress:je,placeholder:"Напишите сообщение...",style:{height:`${Ne}px`}}),y.jsx("button",{className:"send-btn",onClick:de,disabled:!ie.trim(),children:"📤"}),y.jsx(By,{isDraftSaved:A,isDraftLoading:K,draftError:B,messageLength:ie.length,minLength:10,maxLength:1e3})]})]}),v==="details"&&y.jsx(Vh,{plugin:u})]})]})},Ih=({toasts:u,onRemove:r})=>y.jsx("div",{className:"toast-container",children:u.map(d=>y.jsx(ep,{toast:d,onRemove:r},d.id))}),ep=({toast:u,onRemove:r})=>{const[d,c]=Q.useState(!1),[S,T]=Q.useState(!1);Q.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),u.duration>0){const ee=setTimeout(()=>{z()},u.duration);return()=>clearTimeout(ee)}},[u.duration]);const z=Q.useCallback(()=>{T(!0),setTimeout(()=>{r(u.id)},300)},[u.id,r]);return y.jsx("div",{className:`toast toast-${u.type} ${d?"toast-visible":""} ${S?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:u.message}),y.jsx("button",{className:"toast-close",onClick:z,"aria-label":"Закрыть уведомление",children:"×"})]})})},tp=({theme:u,isLight:r,onToggle:d,isInSidebar:c=!1})=>{const S=()=>{switch(u){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},T=()=>{switch(u){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},z={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:d,style:z,title:T(),children:S()})};function og(u){var r,d,c="";if(typeof u=="string"||typeof u=="number")c+=u;else if(typeof u=="object")if(Array.isArray(u)){var S=u.length;for(r=0;r{const r=ip(u),{conflictingClassGroups:d,conflictingClassGroupModifiers:c}=u;return{getClassGroupId:z=>{const ee=z.split(Yo);return ee[0]===""&&ee.length!==1&&ee.shift(),cg(ee,r)||np(z)},getConflictingClassGroupIds:(z,ee)=>{const R=d[z]||[];return ee&&c[z]?[...R,...c[z]]:R}}},cg=(u,r)=>{if(u.length===0)return r.classGroupId;const d=u[0],c=r.nextPart.get(d),S=c?cg(u.slice(1),c):void 0;if(S)return S;if(r.validators.length===0)return;const T=u.join(Yo);return r.validators.find(({validator:z})=>z(T))?.classGroupId},Qd=/^\[(.+)\]$/,np=u=>{if(Qd.test(u)){const r=Qd.exec(u)[1],d=r?.substring(0,r.indexOf(":"));if(d)return"arbitrary.."+d}},ip=u=>{const{theme:r,classGroups:d}=u,c={nextPart:new Map,validators:[]};for(const S in d)Ko(d[S],c,S,r);return c},Ko=(u,r,d,c)=>{u.forEach(S=>{if(typeof S=="string"){const T=S===""?r:Vd(r,S);T.classGroupId=d;return}if(typeof S=="function"){if(sp(S)){Ko(S(c),r,d,c);return}r.validators.push({validator:S,classGroupId:d});return}Object.entries(S).forEach(([T,z])=>{Ko(z,Vd(r,T),d,c)})})},Vd=(u,r)=>{let d=u;return r.split(Yo).forEach(c=>{d.nextPart.has(c)||d.nextPart.set(c,{nextPart:new Map,validators:[]}),d=d.nextPart.get(c)}),d},sp=u=>u.isThemeGetter,up=u=>{if(u<1)return{get:()=>{},set:()=>{}};let r=0,d=new Map,c=new Map;const S=(T,z)=>{d.set(T,z),r++,r>u&&(r=0,c=d,d=new Map)};return{get(T){let z=d.get(T);if(z!==void 0)return z;if((z=c.get(T))!==void 0)return S(T,z),z},set(T,z){d.has(T)?d.set(T,z):S(T,z)}}},Bo="!",Lo=":",op=Lo.length,cp=u=>{const{prefix:r,experimentalParseClassName:d}=u;let c=S=>{const T=[];let z=0,ee=0,R=0,v;for(let $=0;$R?v-R:void 0;return{modifiers:T,hasImportantModifier:W,baseClassName:F,maybePostfixModifierPosition:I}};if(r){const S=r+Lo,T=c;c=z=>z.startsWith(S)?T(z.substring(S.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:z,maybePostfixModifierPosition:void 0}}if(d){const S=c;c=T=>d({className:T,parseClassName:S})}return c},rp=u=>u.endsWith(Bo)?u.substring(0,u.length-1):u.startsWith(Bo)?u.substring(1):u,fp=u=>{const r=Object.fromEntries(u.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const S=[];let T=[];return c.forEach(z=>{z[0]==="["||r[z]?(S.push(...T.sort(),z),T=[]):T.push(z)}),S.push(...T.sort()),S}},dp=u=>({cache:up(u.cacheSize),parseClassName:cp(u),sortModifiers:fp(u),...ap(u)}),gp=/\s+/,mp=(u,r)=>{const{parseClassName:d,getClassGroupId:c,getConflictingClassGroupIds:S,sortModifiers:T}=r,z=[],ee=u.trim().split(gp);let R="";for(let v=ee.length-1;v>=0;v-=1){const O=ee[v],{isExternal:F,modifiers:W,hasImportantModifier:I,baseClassName:$,maybePostfixModifierPosition:ie}=d(O);if(F){R=O+(R.length>0?" "+R:R);continue}let ae=!!ie,A=c(ae?$.substring(0,ie):$);if(!A){if(!ae){R=O+(R.length>0?" "+R:R);continue}if(A=c($),!A){R=O+(R.length>0?" "+R:R);continue}ae=!1}const K=T(W).join(":"),B=I?K+Bo:K,L=B+A;if(z.includes(L))continue;z.push(L);const V=S(A,ae);for(let ce=0;ce0?" "+R:R)}return R};function yp(){let u=0,r,d,c="";for(;u{if(typeof u=="string")return u;let r,d="";for(let c=0;cF(O),u());return d=dp(v),c=d.cache.get,S=d.cache.set,T=ee,ee(R)}function ee(R){const v=c(R);if(v)return v;const O=mp(R,d);return S(R,O),O}return function(){return T(yp.apply(null,arguments))}}const tt=u=>{const r=d=>d[u]||[];return r.isThemeGetter=!0,r},fg=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,dg=/^\((?:(\w[\w-]*):)?(.+)\)$/i,pp=/^\d+\/\d+$/,vp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,bp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Sp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,_p=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,xp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,La=u=>pp.test(u),Ae=u=>!!u&&!Number.isNaN(Number(u)),Hl=u=>!!u&&Number.isInteger(Number(u)),zo=u=>u.endsWith("%")&&Ae(u.slice(0,-1)),rl=u=>vp.test(u),Ep=()=>!0,Ap=u=>bp.test(u)&&!Sp.test(u),gg=()=>!1,Tp=u=>_p.test(u),Cp=u=>xp.test(u),Mp=u=>!ue(u)&&!oe(u),Op=u=>Xa(u,hg,gg),ue=u=>fg.test(u),ta=u=>Xa(u,pg,Ap),Ro=u=>Xa(u,Up,Ae),Zd=u=>Xa(u,mg,gg),Dp=u=>Xa(u,yg,Cp),us=u=>Xa(u,vg,Tp),oe=u=>dg.test(u),kn=u=>ka(u,pg),zp=u=>ka(u,Np),Jd=u=>ka(u,mg),Rp=u=>ka(u,hg),wp=u=>ka(u,yg),os=u=>ka(u,vg,!0),Xa=(u,r,d)=>{const c=fg.exec(u);return c?c[1]?r(c[1]):d(c[2]):!1},ka=(u,r,d=!1)=>{const c=dg.exec(u);return c?c[1]?r(c[1]):d:!1},mg=u=>u==="position"||u==="percentage",yg=u=>u==="image"||u==="url",hg=u=>u==="length"||u==="size"||u==="bg-size",pg=u=>u==="length",Up=u=>u==="number",Np=u=>u==="family-name",vg=u=>u==="shadow",jp=()=>{const u=tt("color"),r=tt("font"),d=tt("text"),c=tt("font-weight"),S=tt("tracking"),T=tt("leading"),z=tt("breakpoint"),ee=tt("container"),R=tt("spacing"),v=tt("radius"),O=tt("shadow"),F=tt("inset-shadow"),W=tt("text-shadow"),I=tt("drop-shadow"),$=tt("blur"),ie=tt("perspective"),ae=tt("aspect"),A=tt("ease"),K=tt("animate"),B=()=>["auto","avoid","all","avoid-page","page","left","right","column"],L=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],V=()=>[...L(),oe,ue],ce=()=>["auto","hidden","clip","visible","scroll"],w=()=>["auto","contain","none"],D=()=>[oe,ue,R],le=()=>[La,"full","auto",...D()],ve=()=>[Hl,"none","subgrid",oe,ue],De=()=>["auto",{span:["full",Hl,oe,ue]},Hl,oe,ue],he=()=>[Hl,"auto",oe,ue],Ne=()=>["auto","min","max","fr",oe,ue],lt=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],we=()=>["start","end","center","stretch","center-safe","end-safe"],M=()=>["auto",...D()],J=()=>[La,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...D()],k=()=>[u,oe,ue],Te=()=>[...L(),Jd,Zd,{position:[oe,ue]}],g=()=>["no-repeat",{repeat:["","x","y","space","round"]}],N=()=>["auto","cover","contain",Rp,Op,{size:[oe,ue]}],te=()=>[zo,kn,ta],Z=()=>["","none","full",v,oe,ue],ne=()=>["",Ae,kn,ta],ye=()=>["solid","dashed","dotted","double"],de=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ze=()=>[Ae,zo,Jd,Zd],je=()=>["","none",$,oe,ue],ut=()=>["none",Ae,oe,ue],zt=()=>["none",Ae,oe,ue],U=()=>[Ae,oe,ue],h=()=>[La,"full",...D()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[rl],breakpoint:[rl],color:[Ep],container:[rl],"drop-shadow":[rl],ease:["in","out","in-out"],font:[Mp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[rl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[rl],shadow:[rl],spacing:["px",Ae],text:[rl],"text-shadow":[rl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",La,ue,oe,ae]}],container:["container"],columns:[{columns:[Ae,ue,oe,ee]}],"break-after":[{"break-after":B()}],"break-before":[{"break-before":B()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:V()}],overflow:[{overflow:ce()}],"overflow-x":[{"overflow-x":ce()}],"overflow-y":[{"overflow-y":ce()}],overscroll:[{overscroll:w()}],"overscroll-x":[{"overscroll-x":w()}],"overscroll-y":[{"overscroll-y":w()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:le()}],"inset-x":[{"inset-x":le()}],"inset-y":[{"inset-y":le()}],start:[{start:le()}],end:[{end:le()}],top:[{top:le()}],right:[{right:le()}],bottom:[{bottom:le()}],left:[{left:le()}],visibility:["visible","invisible","collapse"],z:[{z:[Hl,"auto",oe,ue]}],basis:[{basis:[La,"full","auto",ee,...D()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Ae,La,"auto","initial","none",ue]}],grow:[{grow:["",Ae,oe,ue]}],shrink:[{shrink:["",Ae,oe,ue]}],order:[{order:[Hl,"first","last","none",oe,ue]}],"grid-cols":[{"grid-cols":ve()}],"col-start-end":[{col:De()}],"col-start":[{"col-start":he()}],"col-end":[{"col-end":he()}],"grid-rows":[{"grid-rows":ve()}],"row-start-end":[{row:De()}],"row-start":[{"row-start":he()}],"row-end":[{"row-end":he()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":Ne()}],"auto-rows":[{"auto-rows":Ne()}],gap:[{gap:D()}],"gap-x":[{"gap-x":D()}],"gap-y":[{"gap-y":D()}],"justify-content":[{justify:[...lt(),"normal"]}],"justify-items":[{"justify-items":[...we(),"normal"]}],"justify-self":[{"justify-self":["auto",...we()]}],"align-content":[{content:["normal",...lt()]}],"align-items":[{items:[...we(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...we(),{baseline:["","last"]}]}],"place-content":[{"place-content":lt()}],"place-items":[{"place-items":[...we(),"baseline"]}],"place-self":[{"place-self":["auto",...we()]}],p:[{p:D()}],px:[{px:D()}],py:[{py:D()}],ps:[{ps:D()}],pe:[{pe:D()}],pt:[{pt:D()}],pr:[{pr:D()}],pb:[{pb:D()}],pl:[{pl:D()}],m:[{m:M()}],mx:[{mx:M()}],my:[{my:M()}],ms:[{ms:M()}],me:[{me:M()}],mt:[{mt:M()}],mr:[{mr:M()}],mb:[{mb:M()}],ml:[{ml:M()}],"space-x":[{"space-x":D()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":D()}],"space-y-reverse":["space-y-reverse"],size:[{size:J()}],w:[{w:[ee,"screen",...J()]}],"min-w":[{"min-w":[ee,"screen","none",...J()]}],"max-w":[{"max-w":[ee,"screen","none","prose",{screen:[z]},...J()]}],h:[{h:["screen","lh",...J()]}],"min-h":[{"min-h":["screen","lh","none",...J()]}],"max-h":[{"max-h":["screen","lh",...J()]}],"font-size":[{text:["base",d,kn,ta]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,oe,Ro]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",zo,ue]}],"font-family":[{font:[zp,ue,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[S,oe,ue]}],"line-clamp":[{"line-clamp":[Ae,"none",oe,Ro]}],leading:[{leading:[T,...D()]}],"list-image":[{"list-image":["none",oe,ue]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",oe,ue]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:k()}],"text-color":[{text:k()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ye(),"wavy"]}],"text-decoration-thickness":[{decoration:[Ae,"from-font","auto",oe,ta]}],"text-decoration-color":[{decoration:k()}],"underline-offset":[{"underline-offset":[Ae,"auto",oe,ue]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:D()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",oe,ue]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",oe,ue]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Te()}],"bg-repeat":[{bg:g()}],"bg-size":[{bg:N()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},Hl,oe,ue],radial:["",oe,ue],conic:[Hl,oe,ue]},wp,Dp]}],"bg-color":[{bg:k()}],"gradient-from-pos":[{from:te()}],"gradient-via-pos":[{via:te()}],"gradient-to-pos":[{to:te()}],"gradient-from":[{from:k()}],"gradient-via":[{via:k()}],"gradient-to":[{to:k()}],rounded:[{rounded:Z()}],"rounded-s":[{"rounded-s":Z()}],"rounded-e":[{"rounded-e":Z()}],"rounded-t":[{"rounded-t":Z()}],"rounded-r":[{"rounded-r":Z()}],"rounded-b":[{"rounded-b":Z()}],"rounded-l":[{"rounded-l":Z()}],"rounded-ss":[{"rounded-ss":Z()}],"rounded-se":[{"rounded-se":Z()}],"rounded-ee":[{"rounded-ee":Z()}],"rounded-es":[{"rounded-es":Z()}],"rounded-tl":[{"rounded-tl":Z()}],"rounded-tr":[{"rounded-tr":Z()}],"rounded-br":[{"rounded-br":Z()}],"rounded-bl":[{"rounded-bl":Z()}],"border-w":[{border:ne()}],"border-w-x":[{"border-x":ne()}],"border-w-y":[{"border-y":ne()}],"border-w-s":[{"border-s":ne()}],"border-w-e":[{"border-e":ne()}],"border-w-t":[{"border-t":ne()}],"border-w-r":[{"border-r":ne()}],"border-w-b":[{"border-b":ne()}],"border-w-l":[{"border-l":ne()}],"divide-x":[{"divide-x":ne()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ne()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ye(),"hidden","none"]}],"divide-style":[{divide:[...ye(),"hidden","none"]}],"border-color":[{border:k()}],"border-color-x":[{"border-x":k()}],"border-color-y":[{"border-y":k()}],"border-color-s":[{"border-s":k()}],"border-color-e":[{"border-e":k()}],"border-color-t":[{"border-t":k()}],"border-color-r":[{"border-r":k()}],"border-color-b":[{"border-b":k()}],"border-color-l":[{"border-l":k()}],"divide-color":[{divide:k()}],"outline-style":[{outline:[...ye(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Ae,oe,ue]}],"outline-w":[{outline:["",Ae,kn,ta]}],"outline-color":[{outline:k()}],shadow:[{shadow:["","none",O,os,us]}],"shadow-color":[{shadow:k()}],"inset-shadow":[{"inset-shadow":["none",F,os,us]}],"inset-shadow-color":[{"inset-shadow":k()}],"ring-w":[{ring:ne()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:k()}],"ring-offset-w":[{"ring-offset":[Ae,ta]}],"ring-offset-color":[{"ring-offset":k()}],"inset-ring-w":[{"inset-ring":ne()}],"inset-ring-color":[{"inset-ring":k()}],"text-shadow":[{"text-shadow":["none",W,os,us]}],"text-shadow-color":[{"text-shadow":k()}],opacity:[{opacity:[Ae,oe,ue]}],"mix-blend":[{"mix-blend":[...de(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":de()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Ae]}],"mask-image-linear-from-pos":[{"mask-linear-from":ze()}],"mask-image-linear-to-pos":[{"mask-linear-to":ze()}],"mask-image-linear-from-color":[{"mask-linear-from":k()}],"mask-image-linear-to-color":[{"mask-linear-to":k()}],"mask-image-t-from-pos":[{"mask-t-from":ze()}],"mask-image-t-to-pos":[{"mask-t-to":ze()}],"mask-image-t-from-color":[{"mask-t-from":k()}],"mask-image-t-to-color":[{"mask-t-to":k()}],"mask-image-r-from-pos":[{"mask-r-from":ze()}],"mask-image-r-to-pos":[{"mask-r-to":ze()}],"mask-image-r-from-color":[{"mask-r-from":k()}],"mask-image-r-to-color":[{"mask-r-to":k()}],"mask-image-b-from-pos":[{"mask-b-from":ze()}],"mask-image-b-to-pos":[{"mask-b-to":ze()}],"mask-image-b-from-color":[{"mask-b-from":k()}],"mask-image-b-to-color":[{"mask-b-to":k()}],"mask-image-l-from-pos":[{"mask-l-from":ze()}],"mask-image-l-to-pos":[{"mask-l-to":ze()}],"mask-image-l-from-color":[{"mask-l-from":k()}],"mask-image-l-to-color":[{"mask-l-to":k()}],"mask-image-x-from-pos":[{"mask-x-from":ze()}],"mask-image-x-to-pos":[{"mask-x-to":ze()}],"mask-image-x-from-color":[{"mask-x-from":k()}],"mask-image-x-to-color":[{"mask-x-to":k()}],"mask-image-y-from-pos":[{"mask-y-from":ze()}],"mask-image-y-to-pos":[{"mask-y-to":ze()}],"mask-image-y-from-color":[{"mask-y-from":k()}],"mask-image-y-to-color":[{"mask-y-to":k()}],"mask-image-radial":[{"mask-radial":[oe,ue]}],"mask-image-radial-from-pos":[{"mask-radial-from":ze()}],"mask-image-radial-to-pos":[{"mask-radial-to":ze()}],"mask-image-radial-from-color":[{"mask-radial-from":k()}],"mask-image-radial-to-color":[{"mask-radial-to":k()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":L()}],"mask-image-conic-pos":[{"mask-conic":[Ae]}],"mask-image-conic-from-pos":[{"mask-conic-from":ze()}],"mask-image-conic-to-pos":[{"mask-conic-to":ze()}],"mask-image-conic-from-color":[{"mask-conic-from":k()}],"mask-image-conic-to-color":[{"mask-conic-to":k()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Te()}],"mask-repeat":[{mask:g()}],"mask-size":[{mask:N()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",oe,ue]}],filter:[{filter:["","none",oe,ue]}],blur:[{blur:je()}],brightness:[{brightness:[Ae,oe,ue]}],contrast:[{contrast:[Ae,oe,ue]}],"drop-shadow":[{"drop-shadow":["","none",I,os,us]}],"drop-shadow-color":[{"drop-shadow":k()}],grayscale:[{grayscale:["",Ae,oe,ue]}],"hue-rotate":[{"hue-rotate":[Ae,oe,ue]}],invert:[{invert:["",Ae,oe,ue]}],saturate:[{saturate:[Ae,oe,ue]}],sepia:[{sepia:["",Ae,oe,ue]}],"backdrop-filter":[{"backdrop-filter":["","none",oe,ue]}],"backdrop-blur":[{"backdrop-blur":je()}],"backdrop-brightness":[{"backdrop-brightness":[Ae,oe,ue]}],"backdrop-contrast":[{"backdrop-contrast":[Ae,oe,ue]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Ae,oe,ue]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Ae,oe,ue]}],"backdrop-invert":[{"backdrop-invert":["",Ae,oe,ue]}],"backdrop-opacity":[{"backdrop-opacity":[Ae,oe,ue]}],"backdrop-saturate":[{"backdrop-saturate":[Ae,oe,ue]}],"backdrop-sepia":[{"backdrop-sepia":["",Ae,oe,ue]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":D()}],"border-spacing-x":[{"border-spacing-x":D()}],"border-spacing-y":[{"border-spacing-y":D()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",oe,ue]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Ae,"initial",oe,ue]}],ease:[{ease:["linear","initial",A,oe,ue]}],delay:[{delay:[Ae,oe,ue]}],animate:[{animate:["none",K,oe,ue]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[ie,oe,ue]}],"perspective-origin":[{"perspective-origin":V()}],rotate:[{rotate:ut()}],"rotate-x":[{"rotate-x":ut()}],"rotate-y":[{"rotate-y":ut()}],"rotate-z":[{"rotate-z":ut()}],scale:[{scale:zt()}],"scale-x":[{"scale-x":zt()}],"scale-y":[{"scale-y":zt()}],"scale-z":[{"scale-z":zt()}],"scale-3d":["scale-3d"],skew:[{skew:U()}],"skew-x":[{"skew-x":U()}],"skew-y":[{"skew-y":U()}],transform:[{transform:[oe,ue,"","none","gpu","cpu"]}],"transform-origin":[{origin:V()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:h()}],"translate-x":[{"translate-x":h()}],"translate-y":[{"translate-y":h()}],"translate-z":[{"translate-z":h()}],"translate-none":["translate-none"],accent:[{accent:k()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:k()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",oe,ue]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":D()}],"scroll-mx":[{"scroll-mx":D()}],"scroll-my":[{"scroll-my":D()}],"scroll-ms":[{"scroll-ms":D()}],"scroll-me":[{"scroll-me":D()}],"scroll-mt":[{"scroll-mt":D()}],"scroll-mr":[{"scroll-mr":D()}],"scroll-mb":[{"scroll-mb":D()}],"scroll-ml":[{"scroll-ml":D()}],"scroll-p":[{"scroll-p":D()}],"scroll-px":[{"scroll-px":D()}],"scroll-py":[{"scroll-py":D()}],"scroll-ps":[{"scroll-ps":D()}],"scroll-pe":[{"scroll-pe":D()}],"scroll-pt":[{"scroll-pt":D()}],"scroll-pr":[{"scroll-pr":D()}],"scroll-pb":[{"scroll-pb":D()}],"scroll-pl":[{"scroll-pl":D()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",oe,ue]}],fill:[{fill:["none",...k()]}],"stroke-w":[{stroke:[Ae,kn,ta,Ro]}],stroke:[{stroke:["none",...k()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},Hp=hp(jp),$d=(...u)=>Hp(lp(u));var wo,Wd;function Pp(){if(Wd)return wo;Wd=1;var u=function(K){return r(K)&&!d(K)};function r(A){return!!A&&typeof A=="object"}function d(A){var K=Object.prototype.toString.call(A);return K==="[object RegExp]"||K==="[object Date]"||T(A)}var c=typeof Symbol=="function"&&Symbol.for,S=c?Symbol.for("react.element"):60103;function T(A){return A.$$typeof===S}function z(A){return Array.isArray(A)?[]:{}}function ee(A,K){return K.clone!==!1&&K.isMergeableObject(A)?ie(z(A),A,K):A}function R(A,K,B){return A.concat(K).map(function(L){return ee(L,B)})}function v(A,K){if(!K.customMerge)return ie;var B=K.customMerge(A);return typeof B=="function"?B:ie}function O(A){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(A).filter(function(K){return Object.propertyIsEnumerable.call(A,K)}):[]}function F(A){return Object.keys(A).concat(O(A))}function W(A,K){try{return K in A}catch{return!1}}function I(A,K){return W(A,K)&&!(Object.hasOwnProperty.call(A,K)&&Object.propertyIsEnumerable.call(A,K))}function $(A,K,B){var L={};return B.isMergeableObject(A)&&F(A).forEach(function(V){L[V]=ee(A[V],B)}),F(K).forEach(function(V){I(A,V)||(W(A,V)&&B.isMergeableObject(K[V])?L[V]=v(V,B)(A[V],K[V],B):L[V]=ee(K[V],B))}),L}function ie(A,K,B){B=B||{},B.arrayMerge=B.arrayMerge||R,B.isMergeableObject=B.isMergeableObject||u,B.cloneUnlessOtherwiseSpecified=ee;var L=Array.isArray(K),V=Array.isArray(A),ce=L===V;return ce?L?B.arrayMerge(A,K,B):$(A,K,B):ee(K,B)}ie.all=function(K,B){if(!Array.isArray(K))throw new Error("first argument should be an array");return K.reduce(function(L,V){return ie(L,V,B)},{})};var ae=ie;return wo=ae,wo}Pp();var Uo={exports:{}},Qn={},No={exports:{}},jo={};/** * @license React * scheduler.production.js * @@ -22,7 +22,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Fd;function Gp(){return Fd||(Fd=1,(function(u){function r(M,J){var k=M.length;M.push(J);e:for(;0>>1,g=M[Te];if(0>>1;TeS(Z,k))neS(ye,Z)?(M[Te]=ye,M[ne]=k,Te=ne):(M[Te]=Z,M[te]=k,Te=te);else if(neS(ye,k))M[Te]=ye,M[ne]=k,Te=ne;else break e}}return J}function S(M,J){var k=M.sortIndex-J.sortIndex;return k!==0?k:M.id-J.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var A=performance;u.unstable_now=function(){return A.now()}}else{var z=Date,ee=z.now();u.unstable_now=function(){return z.now()-ee}}var R=[],v=[],O=1,W=null,$=3,I=!1,F=!1,ie=!1,ae=!1,T=typeof setTimeout=="function"?setTimeout:null,q=typeof clearTimeout=="function"?clearTimeout:null,B=typeof setImmediate<"u"?setImmediate:null;function K(M){for(var J=d(v);J!==null;){if(J.callback===null)c(v);else if(J.startTime<=M)c(v),J.sortIndex=J.expirationTime,r(R,J);else break;J=d(v)}}function Q(M){if(ie=!1,K(M),!F)if(d(R)!==null)F=!0,ce||(ce=!0,he());else{var J=d(v);J!==null&&we(Q,J.startTime-M)}}var ce=!1,w=-1,D=5,le=-1;function ve(){return ae?!0:!(u.unstable_now()-leM&&ve());){var Te=W.callback;if(typeof Te=="function"){W.callback=null,$=W.priorityLevel;var g=Te(W.expirationTime<=M);if(M=u.unstable_now(),typeof g=="function"){W.callback=g,K(M),J=!0;break t}W===d(R)&&c(R),K(M)}else c(R);W=d(R)}if(W!==null)J=!0;else{var N=d(v);N!==null&&we(Q,N.startTime-M),J=!1}}break e}finally{W=null,$=k,I=!1}J=void 0}}finally{J?he():ce=!1}}}var he;if(typeof B=="function")he=function(){B(De)};else if(typeof MessageChannel<"u"){var Ne=new MessageChannel,lt=Ne.port2;Ne.port1.onmessage=De,he=function(){lt.postMessage(null)}}else he=function(){T(De,0)};function we(M,J){w=T(function(){M(u.unstable_now())},J)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(M){M.callback=null},u.unstable_forceFrameRate=function(M){0>M||125Te?(M.sortIndex=k,r(v,M),d(R)===null&&M===d(v)&&(ie?(q(w),w=-1):ie=!0,we(Q,k-Te))):(M.sortIndex=g,r(R,M),F||I||(F=!0,ce||(ce=!0,he()))),M},u.unstable_shouldYield=ve,u.unstable_wrapCallback=function(M){var J=$;return function(){var k=$;$=J;try{return M.apply(this,arguments)}finally{$=k}}}})(jo)),jo}var Id;function Kp(){return Id||(Id=1,No.exports=Gp()),No.exports}var Ho={exports:{}},gt={};/** + */var Fd;function Gp(){return Fd||(Fd=1,(function(u){function r(M,J){var k=M.length;M.push(J);e:for(;0>>1,g=M[Te];if(0>>1;TeS(Z,k))neS(ye,Z)?(M[Te]=ye,M[ne]=k,Te=ne):(M[Te]=Z,M[te]=k,Te=te);else if(neS(ye,k))M[Te]=ye,M[ne]=k,Te=ne;else break e}}return J}function S(M,J){var k=M.sortIndex-J.sortIndex;return k!==0?k:M.id-J.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var T=performance;u.unstable_now=function(){return T.now()}}else{var z=Date,ee=z.now();u.unstable_now=function(){return z.now()-ee}}var R=[],v=[],O=1,F=null,W=3,I=!1,$=!1,ie=!1,ae=!1,A=typeof setTimeout=="function"?setTimeout:null,K=typeof clearTimeout=="function"?clearTimeout:null,B=typeof setImmediate<"u"?setImmediate:null;function L(M){for(var J=d(v);J!==null;){if(J.callback===null)c(v);else if(J.startTime<=M)c(v),J.sortIndex=J.expirationTime,r(R,J);else break;J=d(v)}}function V(M){if(ie=!1,L(M),!$)if(d(R)!==null)$=!0,ce||(ce=!0,he());else{var J=d(v);J!==null&&we(V,J.startTime-M)}}var ce=!1,w=-1,D=5,le=-1;function ve(){return ae?!0:!(u.unstable_now()-leM&&ve());){var Te=F.callback;if(typeof Te=="function"){F.callback=null,W=F.priorityLevel;var g=Te(F.expirationTime<=M);if(M=u.unstable_now(),typeof g=="function"){F.callback=g,L(M),J=!0;break t}F===d(R)&&c(R),L(M)}else c(R);F=d(R)}if(F!==null)J=!0;else{var N=d(v);N!==null&&we(V,N.startTime-M),J=!1}}break e}finally{F=null,W=k,I=!1}J=void 0}}finally{J?he():ce=!1}}}var he;if(typeof B=="function")he=function(){B(De)};else if(typeof MessageChannel<"u"){var Ne=new MessageChannel,lt=Ne.port2;Ne.port1.onmessage=De,he=function(){lt.postMessage(null)}}else he=function(){A(De,0)};function we(M,J){w=A(function(){M(u.unstable_now())},J)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(M){M.callback=null},u.unstable_forceFrameRate=function(M){0>M||125Te?(M.sortIndex=k,r(v,M),d(R)===null&&M===d(v)&&(ie?(K(w),w=-1):ie=!0,we(V,k-Te))):(M.sortIndex=g,r(R,M),$||I||($=!0,ce||(ce=!0,he()))),M},u.unstable_shouldYield=ve,u.unstable_wrapCallback=function(M){var J=W;return function(){var k=W;W=J;try{return M.apply(this,arguments)}finally{W=k}}}})(jo)),jo}var Id;function Kp(){return Id||(Id=1,No.exports=Gp()),No.exports}var Ho={exports:{}},gt={};/** * @license React * react-dom.production.js * @@ -30,7 +30,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var eg;function Bp(){if(eg)return gt;eg=1;var u=qo();function r(R){var v="https://react.dev/errors/"+R;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Ho.exports=Bp(),Ho.exports}/** + */var eg;function Bp(){if(eg)return gt;eg=1;var u=qo();function r(R){var v="https://react.dev/errors/"+R;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Ho.exports=Bp(),Ho.exports}/** * @license React * react-dom-client.production.js * @@ -38,13 +38,13 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var lg;function qp(){if(lg)return Qn;lg=1;var u=Kp(),r=qo(),d=Lp();function c(e){var t="https://react.dev/errors/"+e;if(1g||(e.current=Te[g],Te[g]=null,g--)}function Z(e,t){g++,Te[g]=e.current,e.current=t}var ne=N(null),ye=N(null),de=N(null),ze=N(null);function je(e,t){switch(Z(de,t),Z(ye,e),Z(ne,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?od(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=od(t),e=cd(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}te(ne),Z(ne,e)}function ut(){te(ne),te(ye),te(de)}function zt(e){e.memoizedState!==null&&Z(ze,e);var t=ne.current,l=cd(t,e.type);t!==l&&(Z(ye,e),Z(ne,l))}function U(e){ye.current===e&&(te(ne),te(ye)),ze.current===e&&(te(ze),Gn._currentValue=k)}var h=Object.prototype.hasOwnProperty,p=u.unstable_scheduleCallback,Y=u.unstable_cancelCallback,H=u.unstable_shouldYield,P=u.unstable_requestPaint,X=u.unstable_now,fe=u.unstable_getCurrentPriorityLevel,_e=u.unstable_ImmediatePriority,be=u.unstable_UserBlockingPriority,xe=u.unstable_NormalPriority,qe=u.unstable_LowPriority,rl=u.unstable_IdlePriority,bg=u.log,Sg=u.unstable_setDisableYieldValue,ka=null,_t=null;function fl(e){if(typeof bg=="function"&&Sg(e),_t&&typeof _t.setStrictMode=="function")try{_t.setStrictMode(ka,e)}catch{}}var xt=Math.clz32?Math.clz32:Eg,_g=Math.log,xg=Math.LN2;function Eg(e){return e>>>=0,e===0?32:31-(_g(e)/xg|0)|0}var Vn=256,Zn=4194304;function Hl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Jn(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var n=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var o=a&134217727;return o!==0?(a=o&~i,a!==0?n=Hl(a):(s&=o,s!==0?n=Hl(s):l||(l=o&~e,l!==0&&(n=Hl(l))))):(o=a&~i,o!==0?n=Hl(o):s!==0?n=Hl(s):l||(l=a&~e,l!==0&&(n=Hl(l)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:n}function Qa(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Ag(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Xo(){var e=Vn;return Vn<<=1,(Vn&4194048)===0&&(Vn=256),e}function ko(){var e=Zn;return Zn<<=1,(Zn&62914560)===0&&(Zn=4194304),e}function fs(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Va(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Tg(e,t,l,a,n,i){var s=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,f=e.expirationTimes,x=e.hiddenUpdates;for(l=s&~l;0g||(e.current=Te[g],Te[g]=null,g--)}function Z(e,t){g++,Te[g]=e.current,e.current=t}var ne=N(null),ye=N(null),de=N(null),ze=N(null);function je(e,t){switch(Z(de,t),Z(ye,e),Z(ne,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?od(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=od(t),e=cd(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}te(ne),Z(ne,e)}function ut(){te(ne),te(ye),te(de)}function zt(e){e.memoizedState!==null&&Z(ze,e);var t=ne.current,l=cd(t,e.type);t!==l&&(Z(ye,e),Z(ne,l))}function U(e){ye.current===e&&(te(ne),te(ye)),ze.current===e&&(te(ze),Kn._currentValue=k)}var h=Object.prototype.hasOwnProperty,p=u.unstable_scheduleCallback,Y=u.unstable_cancelCallback,H=u.unstable_shouldYield,P=u.unstable_requestPaint,X=u.unstable_now,fe=u.unstable_getCurrentPriorityLevel,_e=u.unstable_ImmediatePriority,be=u.unstable_UserBlockingPriority,xe=u.unstable_NormalPriority,qe=u.unstable_LowPriority,fl=u.unstable_IdlePriority,bg=u.log,Sg=u.unstable_setDisableYieldValue,Qa=null,_t=null;function dl(e){if(typeof bg=="function"&&Sg(e),_t&&typeof _t.setStrictMode=="function")try{_t.setStrictMode(Qa,e)}catch{}}var xt=Math.clz32?Math.clz32:Eg,_g=Math.log,xg=Math.LN2;function Eg(e){return e>>>=0,e===0?32:31-(_g(e)/xg|0)|0}var Vn=256,Zn=4194304;function Pl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Jn(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var n=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var o=a&134217727;return o!==0?(a=o&~i,a!==0?n=Pl(a):(s&=o,s!==0?n=Pl(s):l||(l=o&~e,l!==0&&(n=Pl(l))))):(o=a&~i,o!==0?n=Pl(o):s!==0?n=Pl(s):l||(l=a&~e,l!==0&&(n=Pl(l)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:n}function Va(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Ag(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Xo(){var e=Vn;return Vn<<=1,(Vn&4194048)===0&&(Vn=256),e}function ko(){var e=Zn;return Zn<<=1,(Zn&62914560)===0&&(Zn=4194304),e}function fs(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Za(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Tg(e,t,l,a,n,i){var s=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,f=e.expirationTimes,x=e.hiddenUpdates;for(l=s&~l;0)":-1n||f[a]!==x[n]){var j=` -`+f[a].replace(" at new "," at ");return e.displayName&&j.includes("")&&(j=j.replace("",e.displayName)),j}while(1<=a&&0<=n);break}}}finally{ps=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?sa(l):""}function Rg(e){switch(e.tag){case 26:case 27:case 5:return sa(e.type);case 16:return sa("Lazy");case 13:return sa("Suspense");case 19:return sa("SuspenseList");case 0:case 15:return vs(e.type,!1);case 11:return vs(e.type.render,!1);case 1:return vs(e.type,!0);case 31:return sa("Activity");default:return""}}function tc(e){try{var t="";do t+=Rg(e),e=e.return;while(e);return t}catch(l){return` +`+f[a].replace(" at new "," at ");return e.displayName&&j.includes("")&&(j=j.replace("",e.displayName)),j}while(1<=a&&0<=n);break}}}finally{ps=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?ua(l):""}function Rg(e){switch(e.tag){case 26:case 27:case 5:return ua(e.type);case 16:return ua("Lazy");case 13:return ua("Suspense");case 19:return ua("SuspenseList");case 0:case 15:return vs(e.type,!1);case 11:return vs(e.type.render,!1);case 1:return vs(e.type,!0);case 31:return ua("Activity");default:return""}}function tc(e){try{var t="";do t+=Rg(e),e=e.return;while(e);return t}catch(l){return` Error generating stack: `+l.message+` -`+l.stack}}function Rt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function lc(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function wg(e){var t=lc(e)?"checked":"value",l=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),a=""+e[t];if(!e.hasOwnProperty(t)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var n=l.get,i=l.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return n.call(this)},set:function(s){a=""+s,i.call(this,s)}}),Object.defineProperty(e,t,{enumerable:l.enumerable}),{getValue:function(){return a},setValue:function(s){a=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Fn(e){e._valueTracker||(e._valueTracker=wg(e))}function ac(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var l=t.getValue(),a="";return e&&(a=lc(e)?e.checked?"true":"false":e.value),e=a,e!==l?(t.setValue(e),!0):!1}function In(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Ug=/[\n"\\]/g;function wt(e){return e.replace(Ug,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function bs(e,t,l,a,n,i,s,o){e.name="",s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.type=s:e.removeAttribute("type"),t!=null?s==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Rt(t)):e.value!==""+Rt(t)&&(e.value=""+Rt(t)):s!=="submit"&&s!=="reset"||e.removeAttribute("value"),t!=null?Ss(e,s,Rt(t)):l!=null?Ss(e,s,Rt(l)):a!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.name=""+Rt(o):e.removeAttribute("name")}function nc(e,t,l,a,n,i,s,o){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;l=l!=null?""+Rt(l):"",t=t!=null?""+Rt(t):l,o||t===e.value||(e.value=t),e.defaultValue=t}a=a??n,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=o?e.checked:!!a,e.defaultChecked=!!a,s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(e.name=s)}function Ss(e,t,l){t==="number"&&In(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function ua(e,t,l,a){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ts=!1;if(Jt)try{var Wa={};Object.defineProperty(Wa,"passive",{get:function(){Ts=!0}}),window.addEventListener("test",Wa,Wa),window.removeEventListener("test",Wa,Wa)}catch{Ts=!1}var gl=null,Cs=null,ti=null;function fc(){if(ti)return ti;var e,t=Cs,l=t.length,a,n="value"in gl?gl.value:gl.textContent,i=n.length;for(e=0;e=en),pc=" ",vc=!1;function bc(e,t){switch(e){case"keyup":return um.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Sc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var fa=!1;function cm(e,t){switch(e){case"compositionend":return Sc(t);case"keypress":return t.which!==32?null:(vc=!0,pc);case"textInput":return e=t.data,e===pc&&vc?null:e;default:return null}}function rm(e,t){if(fa)return e==="compositionend"||!Rs&&bc(e,t)?(e=fc(),ti=Cs=gl=null,fa=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Oc(l)}}function zc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?zc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Rc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=In(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=In(e.document)}return t}function Ns(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var vm=Jt&&"documentMode"in document&&11>=document.documentMode,da=null,js=null,nn=null,Hs=!1;function wc(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Hs||da==null||da!==In(a)||(a=da,"selectionStart"in a&&Ns(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),nn&&an(nn,a)||(nn=a,a=ki(js,"onSelect"),0>=s,n-=s,Wt=1<<32-xt(t)+n|l<i?i:8;var s=M.T,o={};M.T=o,_u(e,!1,t,l);try{var f=n(),x=M.S;if(x!==null&&x(o,f),f!==null&&typeof f=="object"&&typeof f.then=="function"){var j=Mm(f,a);Sn(e,t,j,Ot(e))}else Sn(e,t,a,Ot(e))}catch(L){Sn(e,t,{then:function(){},status:"rejected",reason:L},Ot())}finally{J.p=i,M.T=s}}function wm(){}function bu(e,t,l,a){if(e.tag!==5)throw Error(c(476));var n=Ur(e).queue;wr(e,n,t,k,l===null?wm:function(){return Nr(e),l(a)})}function Ur(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:k,baseState:k,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:tl,lastRenderedState:k},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:tl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Nr(e){var t=Ur(e).next.queue;Sn(e,t,{},Ot())}function Su(){return dt(Gn)}function jr(){return Fe().memoizedState}function Hr(){return Fe().memoizedState}function Um(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Ot();e=hl(l);var a=pl(t,e,l);a!==null&&(Dt(a,t,l),mn(a,t,l)),t={cache:$s()},e.payload=t;return}t=t.return}}function Nm(e,t,l){var a=Ot();l={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},Ti(e)?Gr(t,l):(l=Bs(e,t,l,a),l!==null&&(Dt(l,e,a),Kr(l,t,a)))}function Pr(e,t,l){var a=Ot();Sn(e,t,l,a)}function Sn(e,t,l,a){var n={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(Ti(e))Gr(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,o=i(s,l);if(n.hasEagerState=!0,n.eagerState=o,Et(o,s))return oi(e,t,n,0),Xe===null&&ui(),!1}catch{}finally{}if(l=Bs(e,t,n,a),l!==null)return Dt(l,e,a),Kr(l,t,a),!0}return!1}function _u(e,t,l,a){if(a={lane:2,revertLane:Iu(),action:a,hasEagerState:!1,eagerState:null,next:null},Ti(e)){if(t)throw Error(c(479))}else t=Bs(e,l,a,2),t!==null&&Dt(t,e,2)}function Ti(e){var t=e.alternate;return e===Ee||t!==null&&t===Ee}function Gr(e,t){xa=bi=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function Kr(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,Vo(e,l)}}var Ci={readContext:dt,use:_i,useCallback:Je,useContext:Je,useEffect:Je,useImperativeHandle:Je,useLayoutEffect:Je,useInsertionEffect:Je,useMemo:Je,useReducer:Je,useRef:Je,useState:Je,useDebugValue:Je,useDeferredValue:Je,useTransition:Je,useSyncExternalStore:Je,useId:Je,useHostTransitionStatus:Je,useFormState:Je,useActionState:Je,useOptimistic:Je,useMemoCache:Je,useCacheRefresh:Je},Br={readContext:dt,use:_i,useCallback:function(e,t){return pt().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:Er,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Ai(4194308,4,Mr.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Ai(4194308,4,e,t)},useInsertionEffect:function(e,t){Ai(4,2,e,t)},useMemo:function(e,t){var l=pt();t=t===void 0?null:t;var a=e();if(Jl){fl(!0);try{e()}finally{fl(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=pt();if(l!==void 0){var n=l(t);if(Jl){fl(!0);try{l(t)}finally{fl(!1)}}}else n=t;return a.memoizedState=a.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},a.queue=e,e=e.dispatch=Nm.bind(null,Ee,e),[a.memoizedState,e]},useRef:function(e){var t=pt();return e={current:e},t.memoizedState=e},useState:function(e){e=yu(e);var t=e.queue,l=Pr.bind(null,Ee,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:pu,useDeferredValue:function(e,t){var l=pt();return vu(l,e,t)},useTransition:function(){var e=yu(!1);return e=wr.bind(null,Ee,e.queue,!0,!1),pt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=Ee,n=pt();if(He){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Xe===null)throw Error(c(349));(Re&124)!==0||ur(a,t,l)}n.memoizedState=l;var i={value:l,getSnapshot:t};return n.queue=i,Er(cr.bind(null,a,i,e),[e]),a.flags|=2048,Aa(9,Ei(),or.bind(null,a,i,l,t),null),l},useId:function(){var e=pt(),t=Xe.identifierPrefix;if(He){var l=Ft,a=Wt;l=(a&~(1<<32-xt(a)-1)).toString(32)+l,t="«"+t+"R"+l,l=Si++,0me?(st=re,re=null):st=re.sibling;var Ue=E(b,re,_[me],G);if(Ue===null){re===null&&(re=st);break}e&&re&&Ue.alternate===null&&t(b,re),m=i(Ue,m,me),Ce===null?se=Ue:Ce.sibling=Ue,Ce=Ue,re=st}if(me===_.length)return l(b,re),He&&Yl(b,me),se;if(re===null){for(;me<_.length;me++)re=L(b,_[me],G),re!==null&&(m=i(re,m,me),Ce===null?se=re:Ce.sibling=re,Ce=re);return He&&Yl(b,me),se}for(re=a(re);me<_.length;me++)st=C(re,b,me,_[me],G),st!==null&&(e&&st.alternate!==null&&re.delete(st.key===null?me:st.key),m=i(st,m,me),Ce===null?se=st:Ce.sibling=st,Ce=st);return e&&re.forEach(function(Nl){return t(b,Nl)}),He&&Yl(b,me),se}function ge(b,m,_,G){if(_==null)throw Error(c(151));for(var se=null,Ce=null,re=m,me=m=0,st=null,Ue=_.next();re!==null&&!Ue.done;me++,Ue=_.next()){re.index>me?(st=re,re=null):st=re.sibling;var Nl=E(b,re,Ue.value,G);if(Nl===null){re===null&&(re=st);break}e&&re&&Nl.alternate===null&&t(b,re),m=i(Nl,m,me),Ce===null?se=Nl:Ce.sibling=Nl,Ce=Nl,re=st}if(Ue.done)return l(b,re),He&&Yl(b,me),se;if(re===null){for(;!Ue.done;me++,Ue=_.next())Ue=L(b,Ue.value,G),Ue!==null&&(m=i(Ue,m,me),Ce===null?se=Ue:Ce.sibling=Ue,Ce=Ue);return He&&Yl(b,me),se}for(re=a(re);!Ue.done;me++,Ue=_.next())Ue=C(re,b,me,Ue.value,G),Ue!==null&&(e&&Ue.alternate!==null&&re.delete(Ue.key===null?me:Ue.key),m=i(Ue,m,me),Ce===null?se=Ue:Ce.sibling=Ue,Ce=Ue);return e&&re.forEach(function(Hy){return t(b,Hy)}),He&&Yl(b,me),se}function Le(b,m,_,G){if(typeof _=="object"&&_!==null&&_.type===F&&_.key===null&&(_=_.props.children),typeof _=="object"&&_!==null){switch(_.$$typeof){case $:e:{for(var se=_.key;m!==null;){if(m.key===se){if(se=_.type,se===F){if(m.tag===7){l(b,m.sibling),G=n(m,_.props.children),G.return=b,b=G;break e}}else if(m.elementType===se||typeof se=="object"&&se!==null&&se.$$typeof===D&&qr(se)===m.type){l(b,m.sibling),G=n(m,_.props),xn(G,_),G.return=b,b=G;break e}l(b,m);break}else t(b,m);m=m.sibling}_.type===F?(G=Ll(_.props.children,b.mode,G,_.key),G.return=b,b=G):(G=ri(_.type,_.key,_.props,null,b.mode,G),xn(G,_),G.return=b,b=G)}return s(b);case I:e:{for(se=_.key;m!==null;){if(m.key===se)if(m.tag===4&&m.stateNode.containerInfo===_.containerInfo&&m.stateNode.implementation===_.implementation){l(b,m.sibling),G=n(m,_.children||[]),G.return=b,b=G;break e}else{l(b,m);break}else t(b,m);m=m.sibling}G=Ys(_,b.mode,G),G.return=b,b=G}return s(b);case D:return se=_._init,_=se(_._payload),Le(b,m,_,G)}if(we(_))return pe(b,m,_,G);if(he(_)){if(se=he(_),typeof se!="function")throw Error(c(150));return _=se.call(_),ge(b,m,_,G)}if(typeof _.then=="function")return Le(b,m,Mi(_),G);if(_.$$typeof===B)return Le(b,m,mi(b,_),G);Oi(b,_)}return typeof _=="string"&&_!==""||typeof _=="number"||typeof _=="bigint"?(_=""+_,m!==null&&m.tag===6?(l(b,m.sibling),G=n(m,_),G.return=b,b=G):(l(b,m),G=qs(_,b.mode,G),G.return=b,b=G),s(b)):l(b,m)}return function(b,m,_,G){try{_n=0;var se=Le(b,m,_,G);return Ta=null,se}catch(re){if(re===dn||re===hi)throw re;var Ce=At(29,re,null,b.mode);return Ce.lanes=G,Ce.return=b,Ce}finally{}}}var Ca=Yr(!0),Xr=Yr(!1),Pt=N(null),Xt=null;function bl(e){var t=e.alternate;Z(et,et.current&1),Z(Pt,e),Xt===null&&(t===null||_a.current!==null||t.memoizedState!==null)&&(Xt=e)}function kr(e){if(e.tag===22){if(Z(et,et.current),Z(Pt,e),Xt===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(Xt=e)}}else Sl()}function Sl(){Z(et,et.current),Z(Pt,Pt.current)}function ll(e){te(Pt),Xt===e&&(Xt=null),te(et)}var et=N(0);function Di(e){for(var t=e;t!==null;){if(t.tag===13){var l=t.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||fo(l)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function xu(e,t,l,a){t=e.memoizedState,l=l(a,t),l=l==null?t:O({},t,l),e.memoizedState=l,e.lanes===0&&(e.updateQueue.baseState=l)}var Eu={enqueueSetState:function(e,t,l){e=e._reactInternals;var a=Ot(),n=hl(a);n.payload=t,l!=null&&(n.callback=l),t=pl(e,n,a),t!==null&&(Dt(t,e,a),mn(t,e,a))},enqueueReplaceState:function(e,t,l){e=e._reactInternals;var a=Ot(),n=hl(a);n.tag=1,n.payload=t,l!=null&&(n.callback=l),t=pl(e,n,a),t!==null&&(Dt(t,e,a),mn(t,e,a))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var l=Ot(),a=hl(l);a.tag=2,t!=null&&(a.callback=t),t=pl(e,a,l),t!==null&&(Dt(t,e,l),mn(t,e,l))}};function Qr(e,t,l,a,n,i,s){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(a,i,s):t.prototype&&t.prototype.isPureReactComponent?!an(l,a)||!an(n,i):!0}function Vr(e,t,l,a){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(l,a),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(l,a),t.state!==e&&Eu.enqueueReplaceState(t,t.state,null)}function $l(e,t){var l=t;if("ref"in t){l={};for(var a in t)a!=="ref"&&(l[a]=t[a])}if(e=e.defaultProps){l===t&&(l=O({},l));for(var n in e)l[n]===void 0&&(l[n]=e[n])}return l}var zi=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Zr(e){zi(e)}function Jr(e){console.error(e)}function $r(e){zi(e)}function Ri(e,t){try{var l=e.onUncaughtError;l(t.value,{componentStack:t.stack})}catch(a){setTimeout(function(){throw a})}}function Wr(e,t,l){try{var a=e.onCaughtError;a(l.value,{componentStack:l.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(n){setTimeout(function(){throw n})}}function Au(e,t,l){return l=hl(l),l.tag=3,l.payload={element:null},l.callback=function(){Ri(e,t)},l}function Fr(e){return e=hl(e),e.tag=3,e}function Ir(e,t,l,a){var n=l.type.getDerivedStateFromError;if(typeof n=="function"){var i=a.value;e.payload=function(){return n(i)},e.callback=function(){Wr(t,l,a)}}var s=l.stateNode;s!==null&&typeof s.componentDidCatch=="function"&&(e.callback=function(){Wr(t,l,a),typeof n!="function"&&(Cl===null?Cl=new Set([this]):Cl.add(this));var o=a.stack;this.componentDidCatch(a.value,{componentStack:o!==null?o:""})})}function Hm(e,t,l,a,n){if(l.flags|=32768,a!==null&&typeof a=="object"&&typeof a.then=="function"){if(t=l.alternate,t!==null&&cn(t,l,n,!0),l=Pt.current,l!==null){switch(l.tag){case 13:return Xt===null?Zu():l.alternate===null&&Ze===0&&(Ze=3),l.flags&=-257,l.flags|=65536,l.lanes=n,a===Is?l.flags|=16384:(t=l.updateQueue,t===null?l.updateQueue=new Set([a]):t.add(a),$u(e,a,n)),!1;case 22:return l.flags|=65536,a===Is?l.flags|=16384:(t=l.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([a])},l.updateQueue=t):(l=t.retryQueue,l===null?t.retryQueue=new Set([a]):l.add(a)),$u(e,a,n)),!1}throw Error(c(435,l.tag))}return $u(e,a,n),Zu(),!1}if(He)return t=Pt.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=n,a!==Qs&&(e=Error(c(422),{cause:a}),on(Ut(e,l)))):(a!==Qs&&(t=Error(c(423),{cause:a}),on(Ut(t,l))),e=e.current.alternate,e.flags|=65536,n&=-n,e.lanes|=n,a=Ut(a,l),n=Au(e.stateNode,a,n),lu(e,n),Ze!==4&&(Ze=2)),!1;var i=Error(c(520),{cause:a});if(i=Ut(i,l),Dn===null?Dn=[i]:Dn.push(i),Ze!==4&&(Ze=2),t===null)return!0;a=Ut(a,l),l=t;do{switch(l.tag){case 3:return l.flags|=65536,e=n&-n,l.lanes|=e,e=Au(l.stateNode,a,e),lu(l,e),!1;case 1:if(t=l.type,i=l.stateNode,(l.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(Cl===null||!Cl.has(i))))return l.flags|=65536,n&=-n,l.lanes|=n,n=Fr(n),Ir(n,e,l,a),lu(l,n),!1}l=l.return}while(l!==null);return!1}var ef=Error(c(461)),nt=!1;function ot(e,t,l,a){t.child=e===null?Xr(t,null,l,a):Ca(t,e.child,l,a)}function tf(e,t,l,a,n){l=l.render;var i=t.ref;if("ref"in a){var s={};for(var o in a)o!=="ref"&&(s[o]=a[o])}else s=a;return Vl(t),a=uu(e,t,l,s,i,n),o=ou(),e!==null&&!nt?(cu(e,t,n),al(e,t,n)):(He&&o&&Xs(t),t.flags|=1,ot(e,t,a,n),t.child)}function lf(e,t,l,a,n){if(e===null){var i=l.type;return typeof i=="function"&&!Ls(i)&&i.defaultProps===void 0&&l.compare===null?(t.tag=15,t.type=i,af(e,t,i,a,n)):(e=ri(l.type,null,a,t,t.mode,n),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!wu(e,n)){var s=i.memoizedProps;if(l=l.compare,l=l!==null?l:an,l(s,a)&&e.ref===t.ref)return al(e,t,n)}return t.flags|=1,e=$t(i,a),e.ref=t.ref,e.return=t,t.child=e}function af(e,t,l,a,n){if(e!==null){var i=e.memoizedProps;if(an(i,a)&&e.ref===t.ref)if(nt=!1,t.pendingProps=a=i,wu(e,n))(e.flags&131072)!==0&&(nt=!0);else return t.lanes=e.lanes,al(e,t,n)}return Tu(e,t,l,a,n)}function nf(e,t,l){var a=t.pendingProps,n=a.children,i=e!==null?e.memoizedState:null;if(a.mode==="hidden"){if((t.flags&128)!==0){if(a=i!==null?i.baseLanes|l:l,e!==null){for(n=t.child=e.child,i=0;n!==null;)i=i|n.lanes|n.childLanes,n=n.sibling;t.childLanes=i&~a}else t.childLanes=0,t.child=null;return sf(e,t,a,l)}if((l&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&yi(t,i!==null?i.cachePool:null),i!==null?ar(t,i):nu(),kr(t);else return t.lanes=t.childLanes=536870912,sf(e,t,i!==null?i.baseLanes|l:l,l)}else i!==null?(yi(t,i.cachePool),ar(t,i),Sl(),t.memoizedState=null):(e!==null&&yi(t,null),nu(),Sl());return ot(e,t,n,l),t.child}function sf(e,t,l,a){var n=Fs();return n=n===null?null:{parent:Ie._currentValue,pool:n},t.memoizedState={baseLanes:l,cachePool:n},e!==null&&yi(t,null),nu(),kr(t),e!==null&&cn(e,t,a,!0),null}function wi(e,t){var l=t.ref;if(l===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(c(284));(e===null||e.ref!==l)&&(t.flags|=4194816)}}function Tu(e,t,l,a,n){return Vl(t),l=uu(e,t,l,a,void 0,n),a=ou(),e!==null&&!nt?(cu(e,t,n),al(e,t,n)):(He&&a&&Xs(t),t.flags|=1,ot(e,t,l,n),t.child)}function uf(e,t,l,a,n,i){return Vl(t),t.updateQueue=null,l=ir(t,a,l,n),nr(e),a=ou(),e!==null&&!nt?(cu(e,t,i),al(e,t,i)):(He&&a&&Xs(t),t.flags|=1,ot(e,t,l,i),t.child)}function of(e,t,l,a,n){if(Vl(t),t.stateNode===null){var i=ha,s=l.contextType;typeof s=="object"&&s!==null&&(i=dt(s)),i=new l(a,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=Eu,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=a,i.state=t.memoizedState,i.refs={},eu(t),s=l.contextType,i.context=typeof s=="object"&&s!==null?dt(s):ha,i.state=t.memoizedState,s=l.getDerivedStateFromProps,typeof s=="function"&&(xu(t,l,s,a),i.state=t.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(s=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),s!==i.state&&Eu.enqueueReplaceState(i,i.state,null),hn(t,a,i,n),yn(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),a=!0}else if(e===null){i=t.stateNode;var o=t.memoizedProps,f=$l(l,o);i.props=f;var x=i.context,j=l.contextType;s=ha,typeof j=="object"&&j!==null&&(s=dt(j));var L=l.getDerivedStateFromProps;j=typeof L=="function"||typeof i.getSnapshotBeforeUpdate=="function",o=t.pendingProps!==o,j||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(o||x!==s)&&Vr(t,i,a,s),yl=!1;var E=t.memoizedState;i.state=E,hn(t,a,i,n),yn(),x=t.memoizedState,o||E!==x||yl?(typeof L=="function"&&(xu(t,l,L,a),x=t.memoizedState),(f=yl||Qr(t,l,f,a,E,x,s))?(j||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=a,t.memoizedState=x),i.props=a,i.state=x,i.context=s,a=f):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),a=!1)}else{i=t.stateNode,tu(e,t),s=t.memoizedProps,j=$l(l,s),i.props=j,L=t.pendingProps,E=i.context,x=l.contextType,f=ha,typeof x=="object"&&x!==null&&(f=dt(x)),o=l.getDerivedStateFromProps,(x=typeof o=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s!==L||E!==f)&&Vr(t,i,a,f),yl=!1,E=t.memoizedState,i.state=E,hn(t,a,i,n),yn();var C=t.memoizedState;s!==L||E!==C||yl||e!==null&&e.dependencies!==null&&gi(e.dependencies)?(typeof o=="function"&&(xu(t,l,o,a),C=t.memoizedState),(j=yl||Qr(t,l,j,a,E,C,f)||e!==null&&e.dependencies!==null&&gi(e.dependencies))?(x||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(a,C,f),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(a,C,f)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),t.memoizedProps=a,t.memoizedState=C),i.props=a,i.state=C,i.context=f,a=j):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),a=!1)}return i=a,wi(e,t),a=(t.flags&128)!==0,i||a?(i=t.stateNode,l=a&&typeof l.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&a?(t.child=Ca(t,e.child,null,n),t.child=Ca(t,null,l,n)):ot(e,t,l,n),t.memoizedState=i.state,e=t.child):e=al(e,t,n),e}function cf(e,t,l,a){return un(),t.flags|=256,ot(e,t,l,a),t.child}var Cu={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Mu(e){return{baseLanes:e,cachePool:Jc()}}function Ou(e,t,l){return e=e!==null?e.childLanes&~l:0,t&&(e|=Gt),e}function rf(e,t,l){var a=t.pendingProps,n=!1,i=(t.flags&128)!==0,s;if((s=i)||(s=e!==null&&e.memoizedState===null?!1:(et.current&2)!==0),s&&(n=!0,t.flags&=-129),s=(t.flags&32)!==0,t.flags&=-33,e===null){if(He){if(n?bl(t):Sl(),He){var o=Ve,f;if(f=o){e:{for(f=o,o=Yt;f.nodeType!==8;){if(!o){o=null;break e}if(f=qt(f.nextSibling),f===null){o=null;break e}}o=f}o!==null?(t.memoizedState={dehydrated:o,treeContext:ql!==null?{id:Wt,overflow:Ft}:null,retryLane:536870912,hydrationErrors:null},f=At(18,null,null,0),f.stateNode=o,f.return=t,t.child=f,mt=t,Ve=null,f=!0):f=!1}f||kl(t)}if(o=t.memoizedState,o!==null&&(o=o.dehydrated,o!==null))return fo(o)?t.lanes=32:t.lanes=536870912,null;ll(t)}return o=a.children,a=a.fallback,n?(Sl(),n=t.mode,o=Ui({mode:"hidden",children:o},n),a=Ll(a,n,l,null),o.return=t,a.return=t,o.sibling=a,t.child=o,n=t.child,n.memoizedState=Mu(l),n.childLanes=Ou(e,s,l),t.memoizedState=Cu,a):(bl(t),Du(t,o))}if(f=e.memoizedState,f!==null&&(o=f.dehydrated,o!==null)){if(i)t.flags&256?(bl(t),t.flags&=-257,t=zu(e,t,l)):t.memoizedState!==null?(Sl(),t.child=e.child,t.flags|=128,t=null):(Sl(),n=a.fallback,o=t.mode,a=Ui({mode:"visible",children:a.children},o),n=Ll(n,o,l,null),n.flags|=2,a.return=t,n.return=t,a.sibling=n,t.child=a,Ca(t,e.child,null,l),a=t.child,a.memoizedState=Mu(l),a.childLanes=Ou(e,s,l),t.memoizedState=Cu,t=n);else if(bl(t),fo(o)){if(s=o.nextSibling&&o.nextSibling.dataset,s)var x=s.dgst;s=x,a=Error(c(419)),a.stack="",a.digest=s,on({value:a,source:null,stack:null}),t=zu(e,t,l)}else if(nt||cn(e,t,l,!1),s=(l&e.childLanes)!==0,nt||s){if(s=Xe,s!==null&&(a=l&-l,a=(a&42)!==0?1:ds(a),a=(a&(s.suspendedLanes|l))!==0?0:a,a!==0&&a!==f.retryLane))throw f.retryLane=a,ya(e,a),Dt(s,e,a),ef;o.data==="$?"||Zu(),t=zu(e,t,l)}else o.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=f.treeContext,Ve=qt(o.nextSibling),mt=t,He=!0,Xl=null,Yt=!1,e!==null&&(jt[Ht++]=Wt,jt[Ht++]=Ft,jt[Ht++]=ql,Wt=e.id,Ft=e.overflow,ql=t),t=Du(t,a.children),t.flags|=4096);return t}return n?(Sl(),n=a.fallback,o=t.mode,f=e.child,x=f.sibling,a=$t(f,{mode:"hidden",children:a.children}),a.subtreeFlags=f.subtreeFlags&65011712,x!==null?n=$t(x,n):(n=Ll(n,o,l,null),n.flags|=2),n.return=t,a.return=t,a.sibling=n,t.child=a,a=n,n=t.child,o=e.child.memoizedState,o===null?o=Mu(l):(f=o.cachePool,f!==null?(x=Ie._currentValue,f=f.parent!==x?{parent:x,pool:x}:f):f=Jc(),o={baseLanes:o.baseLanes|l,cachePool:f}),n.memoizedState=o,n.childLanes=Ou(e,s,l),t.memoizedState=Cu,a):(bl(t),l=e.child,e=l.sibling,l=$t(l,{mode:"visible",children:a.children}),l.return=t,l.sibling=null,e!==null&&(s=t.deletions,s===null?(t.deletions=[e],t.flags|=16):s.push(e)),t.child=l,t.memoizedState=null,l)}function Du(e,t){return t=Ui({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Ui(e,t){return e=At(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function zu(e,t,l){return Ca(t,e.child,null,l),e=Du(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function ff(e,t,l){e.lanes|=t;var a=e.alternate;a!==null&&(a.lanes|=t),Zs(e.return,t,l)}function Ru(e,t,l,a,n){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:a,tail:l,tailMode:n}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=a,i.tail=l,i.tailMode=n)}function df(e,t,l){var a=t.pendingProps,n=a.revealOrder,i=a.tail;if(ot(e,t,a.children,l),a=et.current,(a&2)!==0)a=a&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&ff(e,l,t);else if(e.tag===19)ff(e,l,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}a&=1}switch(Z(et,a),n){case"forwards":for(l=t.child,n=null;l!==null;)e=l.alternate,e!==null&&Di(e)===null&&(n=l),l=l.sibling;l=n,l===null?(n=t.child,t.child=null):(n=l.sibling,l.sibling=null),Ru(t,!1,n,l,i);break;case"backwards":for(l=null,n=t.child,t.child=null;n!==null;){if(e=n.alternate,e!==null&&Di(e)===null){t.child=n;break}e=n.sibling,n.sibling=l,l=n,n=e}Ru(t,!0,l,null,i);break;case"together":Ru(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function al(e,t,l){if(e!==null&&(t.dependencies=e.dependencies),Tl|=t.lanes,(l&t.childLanes)===0)if(e!==null){if(cn(e,t,l,!1),(l&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,l=$t(e,e.pendingProps),t.child=l,l.return=t;e.sibling!==null;)e=e.sibling,l=l.sibling=$t(e,e.pendingProps),l.return=t;l.sibling=null}return t.child}function wu(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&gi(e)))}function Pm(e,t,l){switch(t.tag){case 3:je(t,t.stateNode.containerInfo),ml(t,Ie,e.memoizedState.cache),un();break;case 27:case 5:zt(t);break;case 4:je(t,t.stateNode.containerInfo);break;case 10:ml(t,t.type,t.memoizedProps.value);break;case 13:var a=t.memoizedState;if(a!==null)return a.dehydrated!==null?(bl(t),t.flags|=128,null):(l&t.child.childLanes)!==0?rf(e,t,l):(bl(t),e=al(e,t,l),e!==null?e.sibling:null);bl(t);break;case 19:var n=(e.flags&128)!==0;if(a=(l&t.childLanes)!==0,a||(cn(e,t,l,!1),a=(l&t.childLanes)!==0),n){if(a)return df(e,t,l);t.flags|=128}if(n=t.memoizedState,n!==null&&(n.rendering=null,n.tail=null,n.lastEffect=null),Z(et,et.current),a)break;return null;case 22:case 23:return t.lanes=0,nf(e,t,l);case 24:ml(t,Ie,e.memoizedState.cache)}return al(e,t,l)}function gf(e,t,l){if(e!==null)if(e.memoizedProps!==t.pendingProps)nt=!0;else{if(!wu(e,l)&&(t.flags&128)===0)return nt=!1,Pm(e,t,l);nt=(e.flags&131072)!==0}else nt=!1,He&&(t.flags&1048576)!==0&&qc(t,di,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var a=t.elementType,n=a._init;if(a=n(a._payload),t.type=a,typeof a=="function")Ls(a)?(e=$l(a,e),t.tag=1,t=of(null,t,a,e,l)):(t.tag=0,t=Tu(null,t,a,e,l));else{if(a!=null){if(n=a.$$typeof,n===K){t.tag=11,t=tf(null,t,a,e,l);break e}else if(n===w){t.tag=14,t=lf(null,t,a,e,l);break e}}throw t=lt(a)||a,Error(c(306,t,""))}}return t;case 0:return Tu(e,t,t.type,t.pendingProps,l);case 1:return a=t.type,n=$l(a,t.pendingProps),of(e,t,a,n,l);case 3:e:{if(je(t,t.stateNode.containerInfo),e===null)throw Error(c(387));a=t.pendingProps;var i=t.memoizedState;n=i.element,tu(e,t),hn(t,a,null,l);var s=t.memoizedState;if(a=s.cache,ml(t,Ie,a),a!==i.cache&&Js(t,[Ie],l,!0),yn(),a=s.element,i.isDehydrated)if(i={element:a,isDehydrated:!1,cache:s.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=cf(e,t,a,l);break e}else if(a!==n){n=Ut(Error(c(424)),t),on(n),t=cf(e,t,a,l);break e}else{switch(e=t.stateNode.containerInfo,e.nodeType){case 9:e=e.body;break;default:e=e.nodeName==="HTML"?e.ownerDocument.body:e}for(Ve=qt(e.firstChild),mt=t,He=!0,Xl=null,Yt=!0,l=Xr(t,null,a,l),t.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(un(),a===n){t=al(e,t,l);break e}ot(e,t,a,l)}t=t.child}return t;case 26:return wi(e,t),e===null?(l=pd(t.type,null,t.pendingProps,null))?t.memoizedState=l:He||(l=t.type,e=t.pendingProps,a=Vi(de.current).createElement(l),a[ft]=t,a[yt]=e,rt(a,l,e),at(a),t.stateNode=a):t.memoizedState=pd(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return zt(t),e===null&&He&&(a=t.stateNode=md(t.type,t.pendingProps,de.current),mt=t,Yt=!0,n=Ve,Dl(t.type)?(go=n,Ve=qt(a.firstChild)):Ve=n),ot(e,t,t.pendingProps.children,l),wi(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&He&&((n=a=Ve)&&(a=fy(a,t.type,t.pendingProps,Yt),a!==null?(t.stateNode=a,mt=t,Ve=qt(a.firstChild),Yt=!1,n=!0):n=!1),n||kl(t)),zt(t),n=t.type,i=t.pendingProps,s=e!==null?e.memoizedProps:null,a=i.children,oo(n,i)?a=null:s!==null&&oo(n,s)&&(t.flags|=32),t.memoizedState!==null&&(n=uu(e,t,Dm,null,null,l),Gn._currentValue=n),wi(e,t),ot(e,t,a,l),t.child;case 6:return e===null&&He&&((e=l=Ve)&&(l=dy(l,t.pendingProps,Yt),l!==null?(t.stateNode=l,mt=t,Ve=null,e=!0):e=!1),e||kl(t)),null;case 13:return rf(e,t,l);case 4:return je(t,t.stateNode.containerInfo),a=t.pendingProps,e===null?t.child=Ca(t,null,a,l):ot(e,t,a,l),t.child;case 11:return tf(e,t,t.type,t.pendingProps,l);case 7:return ot(e,t,t.pendingProps,l),t.child;case 8:return ot(e,t,t.pendingProps.children,l),t.child;case 12:return ot(e,t,t.pendingProps.children,l),t.child;case 10:return a=t.pendingProps,ml(t,t.type,a.value),ot(e,t,a.children,l),t.child;case 9:return n=t.type._context,a=t.pendingProps.children,Vl(t),n=dt(n),a=a(n),t.flags|=1,ot(e,t,a,l),t.child;case 14:return lf(e,t,t.type,t.pendingProps,l);case 15:return af(e,t,t.type,t.pendingProps,l);case 19:return df(e,t,l);case 31:return a=t.pendingProps,l=t.mode,a={mode:a.mode,children:a.children},e===null?(l=Ui(a,l),l.ref=t.ref,t.child=l,l.return=t,t=l):(l=$t(e.child,a),l.ref=t.ref,t.child=l,l.return=t,t=l),t;case 22:return nf(e,t,l);case 24:return Vl(t),a=dt(Ie),e===null?(n=Fs(),n===null&&(n=Xe,i=$s(),n.pooledCache=i,i.refCount++,i!==null&&(n.pooledCacheLanes|=l),n=i),t.memoizedState={parent:a,cache:n},eu(t),ml(t,Ie,n)):((e.lanes&l)!==0&&(tu(e,t),hn(t,null,null,l),yn()),n=e.memoizedState,i=t.memoizedState,n.parent!==a?(n={parent:a,cache:a},t.memoizedState=n,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=n),ml(t,Ie,a)):(a=i.cache,ml(t,Ie,a),a!==n.cache&&Js(t,[Ie],l,!0))),ot(e,t,t.pendingProps.children,l),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function nl(e){e.flags|=4}function mf(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!xd(t)){if(t=Pt.current,t!==null&&((Re&4194048)===Re?Xt!==null:(Re&62914560)!==Re&&(Re&536870912)===0||t!==Xt))throw gn=Is,$c;e.flags|=8192}}function Ni(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?ko():536870912,e.lanes|=t,za|=t)}function En(e,t){if(!He)switch(e.tailMode){case"hidden":t=e.tail;for(var l=null;t!==null;)t.alternate!==null&&(l=t),t=t.sibling;l===null?e.tail=null:l.sibling=null;break;case"collapsed":l=e.tail;for(var a=null;l!==null;)l.alternate!==null&&(a=l),l=l.sibling;a===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:a.sibling=null}}function Qe(e){var t=e.alternate!==null&&e.alternate.child===e.child,l=0,a=0;if(t)for(var n=e.child;n!==null;)l|=n.lanes|n.childLanes,a|=n.subtreeFlags&65011712,a|=n.flags&65011712,n.return=e,n=n.sibling;else for(n=e.child;n!==null;)l|=n.lanes|n.childLanes,a|=n.subtreeFlags,a|=n.flags,n.return=e,n=n.sibling;return e.subtreeFlags|=a,e.childLanes=l,t}function Gm(e,t,l){var a=t.pendingProps;switch(ks(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Qe(t),null;case 1:return Qe(t),null;case 3:return l=t.stateNode,a=null,e!==null&&(a=e.memoizedState.cache),t.memoizedState.cache!==a&&(t.flags|=2048),el(Ie),ut(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(e===null||e.child===null)&&(sn(t)?nl(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,kc())),Qe(t),null;case 26:return l=t.memoizedState,e===null?(nl(t),l!==null?(Qe(t),mf(t,l)):(Qe(t),t.flags&=-16777217)):l?l!==e.memoizedState?(nl(t),Qe(t),mf(t,l)):(Qe(t),t.flags&=-16777217):(e.memoizedProps!==a&&nl(t),Qe(t),t.flags&=-16777217),null;case 27:U(t),l=de.current;var n=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==a&&nl(t);else{if(!a){if(t.stateNode===null)throw Error(c(166));return Qe(t),null}e=ne.current,sn(t)?Yc(t):(e=md(n,a,l),t.stateNode=e,nl(t))}return Qe(t),null;case 5:if(U(t),l=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==a&&nl(t);else{if(!a){if(t.stateNode===null)throw Error(c(166));return Qe(t),null}if(e=ne.current,sn(t))Yc(t);else{switch(n=Vi(de.current),e){case 1:e=n.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:e=n.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":e=n.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":e=n.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":e=n.createElement("div"),e.innerHTML=" + diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 6ee384f5..dd6cfc3b 100644 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.831", + "version": "1.0.842", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index 39735f2e..4f502dcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1356", + "version": "1.0.1367", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 06eec708..d3a011ba 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index e1da12d5..ec87b1a1 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1361", + "version": "0.5.1372", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 20f8b4f2..93234bf1 100644 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 61d54cb6..df861c37 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index f2a4cfdf..22216f46 100644 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 0edf947f..bcb35014 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index ba563a6e..783c7544 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 62639cbb..5660d3b8 100644 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 57142fd6..f85d8dc6 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index f612158c..ff9f01a7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 648f39ab..c6879c96 100644 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1382", + "version": "0.5.1393", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 62b1874c..4ef378ae 100644 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 13048307..c5f56306 100644 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index ac810e94..0fce5697 100644 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index dd5d05ba..b777e6d7 100644 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index f28fb8ec..d11041bf 100644 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index de8005ec..ebe30730 100644 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 60b49e35..76d25ca4 100644 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx index ad104e74..d7a6ae00 100644 --- a/pages/options/src/components/LLMSelector.tsx +++ b/pages/options/src/components/LLMSelector.tsx @@ -1,6 +1,7 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { usePluginSettings } from '../hooks/usePluginSettings'; import { AIKey } from '../hooks/useAIKeys'; +import { APIKeyManager } from '../utils/encryption'; interface LLMSelectorProps { promptType: 'basic_analysis' | 'deep_analysis'; @@ -19,17 +20,27 @@ const LLMSelector: React.FC = ({ hasDefaultLLM, onLLMChange, }) => { - const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings, getAPIKey } = usePluginSettings(); + const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings(); const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl); const [apiKey, setApiKey] = useState(''); + const saveTimeoutRef = useRef(); - // Загружаем текущие настройки при монтировании + // Загружаем API-ключ при монтировании компонента + useEffect(() => { + const loadApiKey = async () => { + const keyId = `ozon-analyzer-${promptType}-${language}`; + const key = await APIKeyManager.getDecryptedKey(keyId) || ''; + setApiKey(key); + }; + loadApiKey(); + }, [promptType, language]); + + // Загружаем LLM при изменении promptType/language useEffect(() => { const currentSettings = promptType === 'basic_analysis' ? settings.basic_analysis[language] : settings.deep_analysis[language]; - // Логика: если сохранённое llm есть — использовать его; иначе — если hasDefaultLLM — "Default LLM"; иначе — "" const savedLLM = currentSettings.llm; let initialLLM = savedLLM; @@ -42,8 +53,7 @@ const LLMSelector: React.FC = ({ } setSelectedLLM(initialLLM); - setApiKey(getAPIKey()); - }, [promptType, language, settings, getAPIKey, hasDefaultLLM]); + }, [promptType, language, settings, hasDefaultLLM]); // Сохраняем выбор LLM const handleLLMChange = async (newLLM: string) => { @@ -61,13 +71,20 @@ const LLMSelector: React.FC = ({ }; // Сохраняем API ключ - const handleApiKeyChange = async (newApiKey: string) => { + const handleApiKeyChange = (newApiKey: string) => { setApiKey(newApiKey); - await settings.saveSettings?.({ - ...settings, - api_keys: { default: newApiKey }, - }); - onLLMChange(selectedLLM, newApiKey); + + if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current); + + saveTimeoutRef.current = setTimeout(async () => { + try { + const keyId = `ozon-analyzer-${promptType}-${language}`; + await APIKeyManager.saveEncryptedKey(keyId, newApiKey); + onLLMChange(selectedLLM, newApiKey); + } catch (error) { + console.error('Failed to save API key:', error); + } + }, 500); }; // Получаем список опций для селекта diff --git a/pages/options/src/hooks/usePluginSettings.ts b/pages/options/src/hooks/usePluginSettings.ts index 2777f93f..3b479254 100644 --- a/pages/options/src/hooks/usePluginSettings.ts +++ b/pages/options/src/hooks/usePluginSettings.ts @@ -84,6 +84,26 @@ export const usePluginSettings = () => { setSettings({ ...DEFAULT_SETTINGS, ...storedSettings, + basic_analysis: { + ru: { + ...DEFAULT_SETTINGS.basic_analysis.ru, + ...storedSettings.basic_analysis?.ru, + }, + en: { + ...DEFAULT_SETTINGS.basic_analysis.en, + ...storedSettings.basic_analysis?.en, + }, + }, + deep_analysis: { + ru: { + ...DEFAULT_SETTINGS.deep_analysis.ru, + ...storedSettings.deep_analysis?.ru, + }, + en: { + ...DEFAULT_SETTINGS.deep_analysis.en, + ...storedSettings.deep_analysis?.en, + }, + }, api_keys: decryptedApiKeys, }); } else { diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 2b3f5009..4fba9139 100644 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1374", + "version": "0.5.1385", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 9daab841..1d3021c2 100644 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1379", + "version": "0.27.1390", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 624b4ace..9c27115a 100644 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1381", + "version": "0.27.1392", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/tests/e2e/package.json b/tests/e2e/package.json index adab07dc..0dc1dccb 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@extension/e2e", - "version": "0.5.1374", + "version": "0.5.1385", "description": "E2e tests configuration boilerplate", "private": true, "sideEffects": false, From d272c2858087a9d4b5a7c52e8fbf9f188fbc994c Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Wed, 15 Oct 2025 05:59:36 +0500 Subject: [PATCH 07/15] =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B8?= =?UTF-8?q?=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20GPT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chrome-extension/src/background/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index dd6cfc3b..58cac846 100644 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.842", + "version": "1.0.848", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" From 4f40eaad36e40b0c3500ba7f883dda9885f102ef Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Wed, 22 Oct 2025 01:27:25 +0500 Subject: [PATCH 08/15] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D1=8B.=20=D0=9F=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=B5=D0=B5=D1=80=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/README.md | 0 .cursor/backup/README.md | 11 - .cursor/backup/index.mdc | 27 - .cursor/backup/rules/AUTOMATION.md | 205 ----- .cursor/backup/rules/EXPERIENCE-TRANSFER.md | 279 ------- .cursor/backup/rules/README.mdc | 117 --- .cursor/backup/rules/ai-index.mdc | 89 -- .cursor/backup/rules/ai-memory.mdc | 38 - .cursor/backup/rules/ai-optimization.mdc | 46 -- .../architecture/architecture-chat-system.mdc | 35 - .../architecture-error-handling.mdc | 40 - .../architecture-observability.mdc | 40 - .../architecture/architecture-performance.mdc | 40 - .../architecture/architecture-plugin.mdc | 34 - .../architecture-project-structure.mdc | 38 - .../architecture/architecture-security.mdc | 40 - .../architecture/architecture-workflow.mdc | 37 - .../rules/architecture/file-relationships.mdc | 339 -------- .../architecture/project-architecture.mdc | 47 -- .../cursor-export/IMPORT-INSTRUCTIONS.md | 60 -- .../backup/rules/cursor-export/ai-memory.mdc | 46 -- .../dev/dev-principle-01-do-no-harm.mdc | 34 - .../dev/dev-principle-03-best-practices.mdc | 35 - .../dev/dev-principle-04-fail-fast-safe.mdc | 34 - .../dev/dev-principle-05-observability.mdc | 34 - .../dev/dev-principle-06-config-as-code.mdc | 34 - ...v-principle-07-progressive-enhancement.mdc | 34 - ...ev-principle-08-data-integrity-privacy.mdc | 35 - .../dev-principle-09-continuous-learning.mdc | 34 - .../dev-principle-10-ecosystem-thinking.mdc | 34 - .../dev/development-guidelines.mdc | 39 - .../rules/cursor-export/dev/git-workflow.mdc | 19 - .../dev/security-and-deployment.mdc | 49 -- .../dev/testing-and-debugging.mdc | 49 -- .../rules/cursor-export/dev/typescript.mdc | 20 - .../rules/cursor-export/doc/ai-fallback.mdc | 27 - .../rules/cursor-export/doc/ai-first.mdc | 69 -- .../rules/cursor-export/doc/knowledge-map.mdc | 21 - .../cursor-export/doc/mdc-file-standards.mdc | 134 --- .../cursor-export/doc/memorybank-quality.mdc | 18 - .../cursor-export/doc/restore-context.mdc | 17 - .../rules/cursor-export/environment.mdc | 12 - .cursor/backup/rules/cursor-export/index.mdc | 80 -- .../cursor-export/monorepo-best-practices.mdc | 208 ----- .../plugin/plugin-best-practices.mdc | 23 - .../plugin/plugin-documentation.mdc | 23 - .../plugin/plugin-error-handling.mdc | 27 - .../plugin/plugin-performance.mdc | 27 - .../cursor-export/plugin/plugin-security.mdc | 22 - .../cursor-export/plugin/plugin-structure.mdc | 60 -- .../cursor-export/plugin/plugin-testing.mdc | 23 - .../cursor-export/security/validation.mdc | 18 - .../typescript-build-troubleshooting.mdc | 166 ---- .../cursor-export/ui/ui-accessibility.mdc | 27 - .../rules/cursor-export/ui/ui-animation.mdc | 23 - .../cursor-export/ui/ui-error-handling.mdc | 23 - .../rules/cursor-export/ui/ui-forms.mdc | 23 - .../cursor-export/ui/ui-loading-states.mdc | 23 - .../rules/cursor-export/ui/ui-mobile.mdc | 23 - .../rules/cursor-export/ui/ui-navigation.mdc | 23 - .../rules/cursor-export/ui/ui-performance.mdc | 23 - .../cursor-export/ui/ui-react-components.mdc | 23 - .../rules/cursor-export/ui/ui-styling.mdc | 23 - .../rules/cursor-export/ui/ui-testing.mdc | 27 - .../cursor-export/workflow/automation.mdc | 15 - .../rules/cursor-export/workflow/branches.mdc | 19 - .../rules/cursor-export/workflow/workflow.mdc | 19 - .../backup/rules/dev/date-time-patterns.mdc | 170 ---- .../rules/dev/development-guidelines.mdc | 23 - .../rules/dev/development-principles.mdc | 65 -- .../rules/dev/testing-troubleshooting.mdc | 66 -- .../backup/rules/doc/ai-answer-self-check.mdc | 39 - .../rules/doc/command-synchronization.mdc | 264 ------ .../rules/doc/context-translation-system.mdc | 206 ----- .../rules/doc/cursor-protection-system.mdc | 309 ------- .../backup/rules/doc/documentation-map.mdc | 187 ----- .../doc/internationalization-complete.mdc | 162 ---- .../backup/rules/doc/universal-commands.mdc | 133 --- .cursor/backup/rules/memory-bank/INDEX.md | 65 -- .../rules/memory-bank/architecture/README.md | 22 - .../architecture/component-structure.md | 12 - .../memory-bank/architecture/decisions.md | 12 - .../memory-bank/architecture/patterns.md | 12 - .../rules/memory-bank/architecture/routing.md | 12 - .../architecture/state-management.md | 12 - .../rules/memory-bank/context/README.md | 21 - .../rules/memory-bank/context/dependencies.md | 12 - .../rules/memory-bank/context/deployment.md | 12 - .../rules/memory-bank/context/environment.md | 12 - .../rules/memory-bank/context/tech-stack.md | 12 - .../backup/rules/memory-bank/core/INDEX.md | 59 -- .../backup/rules/memory-bank/core/README.md | 23 - .../rules/memory-bank/core/activeContext.md | 36 - .../backup/rules/memory-bank/core/general.md | 31 - .../rules/memory-bank/core/migrated-INDEX.md | 59 -- .../backup/rules/memory-bank/core/progress.md | 31 - .../rules/memory-bank/core/projectbrief.md | 33 - .../rules/memory-bank/core/session-log.md | 12 - .../rules/memory-bank/development/README.md | 22 - .../memory-bank/development/build-process.md | 12 - .../development/debugging-guide.md | 12 - .../memory-bank/development/devtools-guide.md | 12 - .../development/testing-results.md | 12 - .../development/version-management.md | 12 - .../backup/rules/memory-bank/errors/README.md | 22 - .../rules/memory-bank/errors/build-errors.md | 12 - .../backup/rules/memory-bank/errors/errors.md | 12 - .../memory-bank/errors/runtime-errors.md | 12 - .../memory-bank/errors/typescript-errors.md | 12 - .../rules/memory-bank/errors/ui-errors.md | 12 - .../memory-bank/memory-bank-controller.mdc | 309 ------- .../rules/memory-bank/planning/README.md | 21 - .../memory-bank/planning/feature-roadmap.md | 12 - .../memory-bank/planning/migration-plans.md | 12 - .../planning/optimization-plans.md | 12 - .../rules/memory-bank/planning/tech-debt.md | 12 - .cursor/backup/rules/memory-bank/ui/README.md | 22 - .../rules/memory-bank/ui/accessibility.md | 12 - .../rules/memory-bank/ui/component-library.md | 12 - .../rules/memory-bank/ui/performance.md | 12 - .../rules/memory-bank/ui/responsive-design.md | 12 - .../rules/memory-bank/ui/styling-patterns.md | 12 - .../backup/rules/workflow/ci-automation.mdc | 85 -- .../rules/workflow/experience-transfer.mdc | 73 -- .cursor/index.mdc | 0 .cursor/rules/AUTOMATION.md | 205 ----- .cursor/rules/EXPERIENCE-TRANSFER.md | 279 ------- .cursor/rules/README.mdc | 117 --- .cursor/rules/ai-index.mdc | 89 -- .cursor/rules/ai-memory.mdc | 38 - .cursor/rules/ai-optimization.mdc | 46 -- .../architecture/architecture-chat-system.mdc | 35 - .../architecture-error-handling.mdc | 40 - .../architecture-observability.mdc | 40 - .../architecture/architecture-performance.mdc | 40 - .../architecture/architecture-plugin.mdc | 34 - .../architecture-project-structure.mdc | 38 - .../architecture/architecture-security.mdc | 40 - .../architecture/architecture-workflow.mdc | 37 - .../rules/architecture/file-relationships.mdc | 339 -------- .../architecture/project-architecture.mdc | 47 -- .cursor/rules/audit-cursor.cjs | 288 ------- .cursor/rules/auto-translate-requests.cjs | 342 -------- .cursor/rules/check-rules-structure.cjs | 33 - .cursor/rules/command-sync.cjs | 670 --------------- .cursor/rules/context-translator.cjs | 409 --------- .cursor/rules/create-rule-for.mdc | 39 - .cursor/rules/create-rule.cjs | 168 ---- .../cursor-export/IMPORT-INSTRUCTIONS.md | 60 -- .cursor/rules/cursor-export/ai-memory.mdc | 46 -- .../dev/dev-principle-01-do-no-harm.mdc | 34 - .../dev/dev-principle-03-best-practices.mdc | 35 - .../dev/dev-principle-04-fail-fast-safe.mdc | 34 - .../dev/dev-principle-05-observability.mdc | 34 - .../dev/dev-principle-06-config-as-code.mdc | 34 - ...v-principle-07-progressive-enhancement.mdc | 34 - ...ev-principle-08-data-integrity-privacy.mdc | 35 - .../dev-principle-09-continuous-learning.mdc | 34 - .../dev-principle-10-ecosystem-thinking.mdc | 34 - .../dev/development-guidelines.mdc | 39 - .../rules/cursor-export/dev/git-workflow.mdc | 19 - .../dev/security-and-deployment.mdc | 49 -- .../dev/testing-and-debugging.mdc | 49 -- .../rules/cursor-export/dev/typescript.mdc | 20 - .../rules/cursor-export/doc/ai-fallback.mdc | 27 - .cursor/rules/cursor-export/doc/ai-first.mdc | 69 -- .../rules/cursor-export/doc/knowledge-map.mdc | 21 - .../cursor-export/doc/mdc-file-standards.mdc | 134 --- .../cursor-export/doc/memorybank-quality.mdc | 18 - .../cursor-export/doc/restore-context.mdc | 17 - .cursor/rules/cursor-export/environment.mdc | 12 - .cursor/rules/cursor-export/import-cursor.cjs | 116 --- .cursor/rules/cursor-export/index.mdc | 80 -- .../cursor-export/monorepo-best-practices.mdc | 208 ----- .../plugin/plugin-best-practices.mdc | 23 - .../plugin/plugin-documentation.mdc | 23 - .../plugin/plugin-error-handling.mdc | 27 - .../plugin/plugin-performance.mdc | 27 - .../cursor-export/plugin/plugin-security.mdc | 22 - .../cursor-export/plugin/plugin-structure.mdc | 60 -- .../cursor-export/plugin/plugin-testing.mdc | 23 - .../cursor-export/security/validation.mdc | 18 - .../typescript-build-troubleshooting.mdc | 166 ---- .../cursor-export/ui/ui-accessibility.mdc | 27 - .../rules/cursor-export/ui/ui-animation.mdc | 23 - .../cursor-export/ui/ui-error-handling.mdc | 23 - .cursor/rules/cursor-export/ui/ui-forms.mdc | 23 - .../cursor-export/ui/ui-loading-states.mdc | 23 - .cursor/rules/cursor-export/ui/ui-mobile.mdc | 23 - .../rules/cursor-export/ui/ui-navigation.mdc | 23 - .../rules/cursor-export/ui/ui-performance.mdc | 23 - .../cursor-export/ui/ui-react-components.mdc | 23 - .cursor/rules/cursor-export/ui/ui-styling.mdc | 23 - .cursor/rules/cursor-export/ui/ui-testing.mdc | 27 - .../cursor-export/workflow/automation.mdc | 15 - .../rules/cursor-export/workflow/branches.mdc | 19 - .../rules/cursor-export/workflow/workflow.mdc | 19 - .cursor/rules/cursor-git-hook.cjs | 240 ------ .cursor/rules/cursor-manager.cjs | 580 ------------- .cursor/rules/cursor-protector.cjs | 726 ---------------- .cursor/rules/dev/date-time-patterns.mdc | 170 ---- .cursor/rules/dev/development-guidelines.mdc | 23 - .cursor/rules/dev/development-principles.mdc | 65 -- .cursor/rules/dev/testing-troubleshooting.mdc | 66 -- .cursor/rules/doc/ai-answer-self-check.mdc | 39 - .cursor/rules/doc/always-russian-answer.mdc | 7 - .cursor/rules/doc/auto-translate-requests.mdc | 387 --------- .../doc/chrome-extension-manual-restore.mdc | 26 - .cursor/rules/doc/command-synchronization.mdc | 264 ------ .../rules/doc/context-translation-system.mdc | 206 ----- .../rules/doc/cursor-protection-system.mdc | 309 ------- .cursor/rules/doc/documentation-map.mdc | 196 ----- .../doc/internationalization-complete.mdc | 162 ---- .cursor/rules/doc/universal-commands.mdc | 133 --- .cursor/rules/documentation-helper.cjs | 472 ----------- .cursor/rules/environment.yaml | 33 - .cursor/rules/export-cursor.cjs | 303 ------- .cursor/rules/fix-cursor.cjs | 350 -------- .cursor/rules/generate-rules-index.cjs | 100 --- .cursor/rules/memory-bank-auditor.cjs | 313 ------- .cursor/rules/memory-bank-manager.cjs | 622 -------------- .cursor/rules/memory-bank-organizer.cjs | 331 -------- .../rules/memory-bank-structure-creator.cjs | 690 ---------------- .cursor/rules/memory-bank/INDEX.md | 65 -- .../rules/memory-bank/architecture/README.md | 22 - .../architecture/component-structure.md | 12 - .../memory-bank/architecture/decisions.md | 12 - .../memory-bank/architecture/patterns.md | 12 - .../rules/memory-bank/architecture/routing.md | 12 - .../architecture/state-management.md | 12 - .cursor/rules/memory-bank/context/README.md | 21 - .../rules/memory-bank/context/dependencies.md | 12 - .../rules/memory-bank/context/deployment.md | 12 - .../rules/memory-bank/context/environment.md | 12 - .../rules/memory-bank/context/tech-stack.md | 12 - .cursor/rules/memory-bank/core/INDEX.md | 59 -- .cursor/rules/memory-bank/core/README.md | 23 - .../rules/memory-bank/core/activeContext.md | 36 - .cursor/rules/memory-bank/core/general.md | 31 - .../rules/memory-bank/core/migrated-INDEX.md | 59 -- .cursor/rules/memory-bank/core/progress.md | 31 - .../rules/memory-bank/core/projectbrief.md | 33 - .cursor/rules/memory-bank/core/session-log.md | 12 - .../rules/memory-bank/development/README.md | 22 - .../memory-bank/development/build-process.md | 12 - .../development/debugging-guide.md | 12 - .../memory-bank/development/devtools-guide.md | 12 - .../development/testing-results.md | 12 - .../development/version-management.md | 12 - .cursor/rules/memory-bank/errors/README.md | 22 - .../rules/memory-bank/errors/build-errors.md | 12 - .cursor/rules/memory-bank/errors/errors.md | 12 - .../memory-bank/errors/runtime-errors.md | 12 - .../memory-bank/errors/typescript-errors.md | 12 - .cursor/rules/memory-bank/errors/ui-errors.md | 12 - .../memory-bank/memory-bank-controller.mdc | 309 ------- .cursor/rules/memory-bank/planning/README.md | 21 - .../memory-bank/planning/feature-roadmap.md | 12 - .../memory-bank/planning/migration-plans.md | 12 - .../planning/optimization-plans.md | 12 - .../rules/memory-bank/planning/tech-debt.md | 12 - .cursor/rules/memory-bank/ui/README.md | 22 - .cursor/rules/memory-bank/ui/accessibility.md | 12 - .../rules/memory-bank/ui/component-library.md | 12 - .cursor/rules/memory-bank/ui/performance.md | 12 - .../rules/memory-bank/ui/responsive-design.md | 12 - .../rules/memory-bank/ui/styling-patterns.md | 12 - .cursor/rules/optimize-for-ai.cjs | 398 --------- .cursor/rules/protect-cursor.cjs | 263 ------ .cursor/rules/request-translator.cjs | 562 ------------- .cursor/rules/save-context.cjs | 83 -- .cursor/rules/translate-to-english.cjs | 122 --- .cursor/rules/workflow/ci-automation.mdc | 85 -- .../rules/workflow/experience-transfer.mdc | 73 -- .cursorignore | 0 .example.env | 0 .git-secrets | 0 .gitattributes | 0 .gitguardian.yaml | 0 .github/CODEOWNERS | 0 .github/FUNDING.yml | 0 .github/ISSUE_TEMPLATE/bug_report.yml | 0 .github/ISSUE_TEMPLATE/config.yml | 0 .github/ISSUE_TEMPLATE/feature_request.yml | 0 .github/auto_assign.yml | 0 .github/dependabot.yml | 0 .github/pull_request_template.md | 0 .github/secret_scanning.yml | 0 .github/stale.yml | 0 .github/workflows/audit.yml | 0 .github/workflows/auto-change-prs-branch.yml | 0 .github/workflows/build-zip.yml | 0 .../cancel-other-workflows-on-close.yml | 0 .github/workflows/codeql.yml | 0 .github/workflows/danger.yml | 0 .github/workflows/dependencies-auto-merge.yml | 0 .github/workflows/e2e-modular.yml | 0 .github/workflows/e2e.yml | 0 .github/workflows/greetings.yml | 0 .github/workflows/lint.yml | 0 .github/workflows/prettier.yml | 0 .github/workflows/release.yml | 0 .github/workflows/rules-check.yml | 0 .gitignore | 0 .gitmodules | 0 .kilocode/mcp.json | 3 +- .kilocode/rules/general-rules.md | 0 .kilocode/rules/restricted_files.md | 0 .npmrc | 0 .nvmrc | 0 .prettierignore | 0 .prettierrc | 0 .releaserc.json | 0 .roo/mcp.json | 0 .rules/ai-fallback.rules.md | 0 CHANGELOG.md | 0 CURSOR_AI_MEMORY_BANK.md | 0 DEVELOPER_SETUP.md | 0 EXTENSION_TESTING_INSTRUCTIONS.md | 0 FUTURE_CHANGES_SUMMARY.md | 0 LICENSE | 0 MEMORY_BANK.md | 0 PLUGIN_DEVELOPMENT.md | 0 ProjectGraphAgent/.gitignore | 0 ProjectGraphAgent/CHANGELOG.md | 0 ProjectGraphAgent/LLM_GUIDELINES.md | 0 ProjectGraphAgent/README.md | 0 ProjectGraphAgent/README.ru.md | 0 ProjectGraphAgent/README_PUBLISH.md | 0 ProjectGraphAgent/README_PUBLISH.ru.md | 0 ProjectGraphAgent/WORKFLOW_GUIDE.md | 0 ProjectGraphAgent/WORKFLOW_GUIDE.ru.md | 0 ProjectGraphAgent/adapters/python.mjs | 0 ProjectGraphAgent/adapters/typescript.mjs | 0 .../graph_parts/README_path_indexing.md | 0 .../graph_parts/ai_commands.jsonnet | 0 .../chat-architecture-changes.jsonnet | 0 .../graph_parts/entities.jsonnet | 0 ProjectGraphAgent/graph_parts/meta.jsonnet | 0 .../graph_parts/path_index.jsonnet | 0 .../graph_parts/path_search_examples.jsonnet | 0 ProjectGraphAgent/graph_parts/plans.jsonnet | 0 .../graph_parts/policies.jsonnet | 0 .../graph_parts/relations.jsonnet | 0 ProjectGraphAgent/graph_parts/schema.jsonnet | 0 .../graph_parts/templates.jsonnet | 0 ProjectGraphAgent/package-lock.json | 0 ProjectGraphAgent/package.json | 4 +- ProjectGraphAgent/project_graph.jsonnet | 0 ProjectGraphAgent/scripts/ai_committer.mjs | 0 ProjectGraphAgent/scripts/clean_project.mjs | 0 ProjectGraphAgent/scripts/graph_generator.mjs | 0 ProjectGraphAgent/scripts/graph_validator.mjs | 0 .../scripts/publish_workflow.mjs | 0 .../scripts/sync_ai_commands.mjs | 0 .../scripts/sync_to_standalone.mjs | 0 ProjectGraphAgent/settings.json | 0 ProjectGraphAgent/test_path_search.js | 0 QUICK_START.md | 0 UPDATE-PACKAGE-VERSIONS.md | 0 USER_COMMANDS.md | 0 agent-plugins-platform-v0.5.683.tar.gz | Bin analyze-potential-issues.js | 0 backup/documentation/cursor-quick-setup.md | 0 backup/documentation/cursor-setup-guide.md | 0 bash-scripts/copy_env.sh | 0 bash-scripts/run-chromium-old-extension.sh | 0 bash-scripts/set_global_env.sh | 0 bash-scripts/update_version.sh | 0 bridge/mcp-bridge.js | 0 bridge/pyodide-worker.js | 0 bridge/worker-manager.js | 0 .../COMMUNICATION_DEBUG_README.md | 0 .../HTML_TRANSMISSION_TEST_INSTRUCTIONS.md | 0 .../HTML_TRANSMISSION_TEST_REPORT.md | 0 .../RACE_CONDITION_FIX_SUMMARY.md | 0 chrome-extension/generate-large-html.cjs | 0 chrome-extension/images/icon-128.xcf | Bin chrome-extension/images/icon-34.xcf | Bin chrome-extension/large-test-file.html | 0 chrome-extension/large-test-summary.txt | 0 chrome-extension/manifest.cjs | 0 chrome-extension/manifest.ts | 0 chrome-extension/package.json | 2 +- chrome-extension/pre-build.tsconfig.json | 0 .../public/_locales/en/messages.json | 0 .../public/_locales/ko/messages.json | 0 chrome-extension/public/background-worker.js | 0 chrome-extension/public/background.js | 0 chrome-extension/public/content.css | 0 .../public/content/workerManager.js | 0 chrome-extension/public/icon-128.png | Bin chrome-extension/public/icon-34.png | Bin chrome-extension/public/manifest.json | 0 chrome-extension/public/offscreen-init.js | 0 chrome-extension/public/offscreen.html | 0 chrome-extension/public/offscreen.js | 0 .../public/options/assets/index-C7Ftd-oV.css | 0 .../public/options/assets/index-CNQsTj65.js | 0 .../options/assets/index-CNQsTj65.js.map | 0 chrome-extension/public/options/index.html | 0 .../public/options/logo_horizontal.svg | 0 .../public/options/logo_horizontal_dark.svg | 0 .../public/plugins/google-helper/icon.svg | 0 .../plugins/google-helper/manifest.json | 0 .../plugins/google-helper/mcp_server.py | 0 .../ozon-analyzer/MONITORING_README.md | 0 .../public/plugins/ozon-analyzer/README.md | 0 .../__pycache__/mcp_server.cpython-313.pyc | Bin 230783 -> 231440 bytes .../curl-templates/basic_analysis-en.curl | 0 .../curl-templates/basic_analysis-ru.curl | 0 .../curl-templates/deep_analysis-en.curl | 0 .../curl-templates/deep_analysis-ru.curl | 0 .../public/plugins/ozon-analyzer/icon.svg | 0 .../plugins/ozon-analyzer/manifest.json | 9 +- .../plugins/ozon-analyzer/mcp_server.py | 567 ++++--------- .../plugins/ozon-analyzer/options/index.html | 0 .../ozon-analyzer/production-config.json | 0 .../plugins/ozon-analyzer/workflow.json | 0 .../public/plugins/test-plugin/icon.svg | 0 .../public/plugins/test-plugin/manifest.json | 0 .../public/plugins/test-plugin/mcp_server.py | 0 .../public/plugins/time-test/icon.svg | 0 .../public/plugins/time-test/manifest.json | 0 .../public/plugins/time-test/mcp_server.py | 0 .../public/plugins/time-test/workflow.json | 0 chrome-extension/public/pyodide-worker.js | 0 .../public/pyodide/fonts/DejaVuSans-Bold.ttf | Bin .../pyodide/fonts/DejaVuSans-BoldOblique.ttf | Bin .../pyodide/fonts/DejaVuSans-Oblique.ttf | Bin .../public/pyodide/fonts/DejaVuSans.ttf | Bin .../pyodide/fonts/DejaVuSansDisplay.ttf | Bin .../pyodide/fonts/DejaVuSansMono-Bold.ttf | Bin .../fonts/DejaVuSansMono-BoldOblique.ttf | Bin .../pyodide/fonts/DejaVuSansMono-Oblique.ttf | Bin .../public/pyodide/fonts/DejaVuSansMono.ttf | Bin .../public/pyodide/fonts/DejaVuSerif-Bold.ttf | Bin .../pyodide/fonts/DejaVuSerif-BoldItalic.ttf | Bin .../pyodide/fonts/DejaVuSerif-Italic.ttf | Bin .../public/pyodide/fonts/DejaVuSerif.ttf | Bin .../pyodide/fonts/DejaVuSerifDisplay.ttf | Bin .../public/pyodide/fonts/Humor-Sans.ttf | Bin .../public/pyodide/fonts/LICENSE_DEJAVU | 0 .../public/pyodide/fonts/LICENSE_STIX | 0 .../public/pyodide/fonts/STIXGeneral.ttf | Bin .../public/pyodide/fonts/STIXGeneralBol.ttf | Bin .../pyodide/fonts/STIXGeneralBolIta.ttf | Bin .../pyodide/fonts/STIXGeneralItalic.ttf | Bin .../public/pyodide/fonts/STIXNonUni.ttf | Bin .../public/pyodide/fonts/STIXNonUniBol.ttf | Bin .../public/pyodide/fonts/STIXNonUniBolIta.ttf | Bin .../public/pyodide/fonts/STIXNonUniIta.ttf | Bin .../pyodide/fonts/STIXSizFiveSymReg.ttf | Bin .../pyodide/fonts/STIXSizFourSymBol.ttf | Bin .../pyodide/fonts/STIXSizFourSymReg.ttf | Bin .../public/pyodide/fonts/STIXSizOneSymBol.ttf | Bin .../public/pyodide/fonts/STIXSizOneSymReg.ttf | Bin .../pyodide/fonts/STIXSizThreeSymBol.ttf | Bin .../pyodide/fonts/STIXSizThreeSymReg.ttf | Bin .../public/pyodide/fonts/STIXSizTwoSymBol.ttf | Bin .../public/pyodide/fonts/STIXSizTwoSymReg.ttf | Bin .../public/pyodide/fonts/cmb10.ttf | Bin .../public/pyodide/fonts/cmex10.ttf | Bin .../public/pyodide/fonts/cmmi10.ttf | Bin .../public/pyodide/fonts/cmr10.ttf | Bin .../public/pyodide/fonts/cmss10.ttf | Bin .../public/pyodide/fonts/cmsy10.ttf | Bin .../public/pyodide/fonts/cmtt10.ttf | Bin chrome-extension/public/pyodide/package.json | 2 +- .../public/pyodide/pyodide-lock.json | 0 .../public/pyodide/pyodide.asm.js | 0 chrome-extension/public/pyodide/pyodide.js | 0 .../public/pyodide/pyodide.js.map | 0 .../public/pyodide/python_stdlib.zip | Bin chrome-extension/public/pyodide/test.py | 0 .../side-panel/assets/index-BGEZNpCW.js.map | 1 - .../{index-BGEZNpCW.js => index-D46GpuPf.js} | 22 +- .../side-panel/assets/index-D46GpuPf.js.map | 1 + .../side-panel/assets/index-DTNx5ty5.css | 0 chrome-extension/public/side-panel/index.html | 2 +- .../public/side-panel/logo_vertical.svg | 0 .../public/side-panel/logo_vertical_dark.svg | 0 .../README-pyodide-manual-testing.md | 0 .../public/test-scripts/README.md | 0 .../test-scripts/demo-console-commands.js | 0 .../test-scripts/integration-test-helpers.js | 0 .../public/test-scripts/integration-test.html | 0 .../public/test-scripts/integration-test.js | 0 .../public/test-scripts/ozon-test.js | 0 .../public/test-scripts/page-ui.js | 0 .../pyodide-offscreen-manual-test.js | 0 .../public/test-scripts/test-loader.js | 0 .../beautifulsoup4-4.12.3-py3-none-any.whl | Bin .../certifi-2024.12.14-py3-none-any.whl | Bin .../charset_normalizer-3.3.2-py3-none-any.whl | Bin .../public/wheels/idna-3.7-py3-none-any.whl | Bin .../pyodide_http-0.2.1-py3-none-any.whl | Bin .../wheels/requests-2.31.0-py3-none-any.whl | Bin .../wheels/soupsieve-2.5-py3-none-any.whl | Bin .../wheels/urllib3-2.2.3-py3-none-any.whl | Bin .../src/background/ai-api-client.ts | 18 + .../src/background/background-worker.ts | 0 chrome-extension/src/background/host-api.ts | 0 chrome-extension/src/background/index.ts | 0 .../src/background/index.ts.backup | 0 chrome-extension/src/background/mcp-bridge.ts | 0 .../background/monitoring/alert-manager.ts | 0 .../background/monitoring/error-tracker.ts | 0 .../monitoring/html-extraction-monitor.ts | 0 .../src/background/monitoring/index.ts | 0 .../src/background/monitoring/logger.ts | 0 .../monitoring/metrics-collector.ts | 0 .../background/monitoring/monitoring-core.ts | 0 .../background/monitoring/network-tracker.ts | 0 .../monitoring/performance-monitor.ts | 0 .../background/monitoring/pyodide-monitor.ts | 0 .../src/background/package-lock.json | 0 chrome-extension/src/background/package.json | 2 +- .../src/background/plugin-chat-api.ts | 0 .../src/background/plugin-chat-cache.ts | 0 .../src/background/plugin-manager.ts | 0 .../src/background/prompt-test-handler.ts | 0 .../src/background/pyodide-worker.js | 0 .../src/background/worker-manager.ts | 0 .../src/background/workflow-engine.ts | 0 chrome-extension/src/content/workerManager.js | 0 .../src/side-panel/components/JsonViewer.css | 0 .../src/side-panel/components/JsonViewer.tsx | 0 .../src/side-panel/components/LogManager.css | 0 .../src/side-panel/components/LogManager.tsx | 0 .../src/side-panel/components/PluginCard.css | 0 .../src/side-panel/components/PluginCard.tsx | 0 .../components/ToastNotifications.css | 0 .../components/ToastNotifications.tsx | 0 chrome-extension/tsconfig.json | 0 chrome-extension/utils/cleanup-build.cjs | 0 .../utils/plugins/make-manifest-plugin.ts | 0 chrome-extension/vite.config.mts | 0 commitlint.config.cjs | 0 core/host-api.js | 0 core/plugin-manager.js | 0 core/workflow-engine.js | 0 danger/dangerfile.js | 0 docs/README-ozon-analyzer.md | 0 docs/README.md | 0 docs/administration/Cursor_Rules_Index.en.md | 0 docs/administration/README.md | 0 docs/api-documentation.md | 0 docs/api/.nojekyll | 0 docs/api/assets/hierarchy.js | 0 docs/api/assets/highlight.css | 0 docs/api/assets/icons.js | 0 docs/api/assets/icons.svg | 0 docs/api/assets/main.js | 0 docs/api/assets/navigation.js | 0 docs/api/assets/search.js | 0 docs/api/assets/style.css | 0 docs/api/functions/streamFileToZip.html | 0 docs/api/hierarchy.html | 0 docs/api/index.html | 0 docs/api/interfaces/IManifestParser.html | 0 docs/api/modules.html | 0 docs/api/variables/ManifestParser.html | 0 docs/architecture.md | 0 .../chrome_extension_pyodide.txt | 0 docs/chrome_pyodide/chrome_pyodide_readme.md | 0 docs/developer-commands.md | 0 docs/devtools-panel-troubleshooting.md | 0 docs/devtools-panel-usage.md | 0 docs/devtools-testing-guide.md | 0 docs/examples.md | 0 docs/gemini-delegation-summary.md | 0 docs/integration-guide.md | 0 docs/message-port-fix-summary.md | 0 docs/monitoring-alerting.md | 0 docs/onboarding.md | 0 docs/performance-benchmarking.md | 0 .../ozon-analyzer-integration-guide.md | 0 docs/plugins/ozon-analyzer-technical-spec.md | 0 .../plugins/ozon-analyzer-ui-documentation.md | 0 docs/security-compliance.md | 0 docs/transfer-best-practices-user.md | 0 docs/transferability.ru.md | 0 docs/troubleshooting.md | 0 e2e_test_diagram.md | 0 eslint.config.ts | 0 memory-bank/INDEX.md | 0 memory-bank/MEMORY_BANK_STRUCTURE.md | 0 memory-bank/README.md | 0 memory-bank/architecture/README.md | 0 .../architecture/architecture-decisions.md | 0 .../comprehensive-architecture.md | 0 .../architecture/manifest-data-flow.md | 0 .../architecture/platform-core-analysis.md | 0 .../architecture/plugin-system-integration.md | 0 .../security-architecture-update.md | 0 .../architecture/security-architecture.md | 0 memory-bank/architecture/systemPatterns.md | 0 memory-bank/audit_logs.md | 0 .../chrome-extension-manual-restore.md | 0 memory-bank/context/ENVIRONMENT.md | 0 memory-bank/context/README.md | 0 memory-bank/context/excluded-directories.md | 0 memory-bank/context/productContext.md | 0 memory-bank/context/techContext.md | 0 memory-bank/core/README.md | 0 memory-bank/core/activeContext.md | 0 .../core/api-keys-security-implementation.md | 0 .../activeContext-2025-07-19T02-38-14-572Z.md | 0 .../activeContext-2025-07-19T02-38-25-140Z.md | 0 .../progress-2025-07-19T02-38-14-572Z.md | 0 .../progress-2025-07-19T02-38-25-140Z.md | 0 memory-bank/core/plugin-adaptations.md | 0 memory-bank/core/progress.md | 0 memory-bank/core/projectbrief.md | 0 memory-bank/core/session-log.md | 0 memory-bank/cursor-saved-memories.md | 0 memory-bank/cursor-user-rules-simple.md | 0 memory-bank/deprecated/AI-access-policy.md | 0 .../ai-barrel-exports-best-practices.md | 0 .../deprecated/ai-user-commands-reference.md | 0 .../deprecated/cursor-project-rules.md | 0 .../cursor-saved-memories-analysis.md | 0 .../deprecated/cursor-saved-memories.md | 0 .../deprecated/cursor-update-mechanism.md | 0 memory-bank/deprecated/cursor-user-rules.md | 0 memory-bank/deprecated/mdc-file-standards.md | 0 memory-bank/deprecated/user-commands.md | 0 memory-bank/development/README.md | 0 .../barrel-exports-best-practices.md | 0 .../development/build-sources-mapping.md | 0 .../development/debug-testing-guide.md | 0 .../development/devtools-testing-guide.md | 0 .../development/ozon-analyzer-testing.md | 0 memory-bank/development/testing-results.md | 0 memory-bank/development/user-commands.md | 0 memory-bank/development/version-management.md | 0 memory-bank/diagrams/graph.mmd | 0 memory-bank/drift.md | 0 memory-bank/errors/ERRORS_AND_SOLUTIONS.md | 0 memory-bank/errors/README.md | 0 memory-bank/errors/error-graveyard.md | 0 memory-bank/errors/errors.md | 0 ...script-build-troubleshooting-experience.md | 0 memory-bank/errors/vite-react19-errors.md | 0 memory-bank/planning/README.md | 0 .../documentation-optimization-plan.md | 0 .../documentation-optimization-summary.md | 0 memory-bank/planning/future-plans.md | 0 memory-bank/plans/README.md | 0 .../chrome-extension-chat-recovery/README.md | 0 .../architecture/chat-architecture.md | 0 .../docs/code-changes-summary.md | 0 .../docs/lessons-learned.md | 0 .../docs/problems-solved.md | 0 .../docs/project-overview.md | 0 .../testing/testing-results.md | 0 memory-bank/ui/README.md | 0 memory-bank/ui/chat-context-fix.md | 0 memory-bank/ui/chat-messages-interface.md | 0 .../ui/chat-text-alignment-settings.md | 0 memory-bank/ui/html-transmission-settings.md | 0 memory-bank/ui/lazy-chat-sync.md | 0 memory-bank/ui/options-scrolling-fix.md | 0 .../ui/ozon-analyzer-ui-integration.md | 0 .../ui/plugin-control-panel-buttons.md | 0 .../ui/plugin-settings-transmission.md | 0 memory-bank/ui/side-panel-improvements.md | 0 memory-bank/ui/theme-switcher-component.md | 0 memory-bank/ui/theme-switching-settings.md | 0 messaging-example/background.js | 0 messaging-example/eslint.config.js | 0 messaging-example/manifest.json | 0 messaging-example/side-panel.html | 0 messaging-example/side-panel.js | 0 nohup.out | 0 package.json | 2 +- packages/dev-utils/README.md | 0 packages/dev-utils/index.mts | 0 packages/dev-utils/lib/index.ts | 0 .../dev-utils/lib/manifest-parser/impl.ts | 0 .../dev-utils/lib/manifest-parser/index.ts | 0 .../dev-utils/lib/manifest-parser/types.ts | 0 packages/dev-utils/lib/stream-file-to-zip.ts | 0 packages/dev-utils/package.json | 2 +- packages/dev-utils/tsconfig.json | 0 packages/env/README.md | 0 packages/env/index.mts | 0 packages/env/lib/config.ts | 0 packages/env/lib/const.ts | 0 packages/env/lib/index.ts | 0 packages/env/lib/types.ts | 0 packages/env/package.json | 2 +- packages/env/tsconfig.json | 0 packages/env/use-env-example.png | Bin packages/hmr/index.mts | 0 packages/hmr/lib/consts.ts | 0 packages/hmr/lib/initializers/init-client.ts | 0 .../lib/initializers/init-reload-server.ts | 0 packages/hmr/lib/injections/refresh.ts | 0 packages/hmr/lib/injections/reload.ts | 0 packages/hmr/lib/interpreter/index.ts | 0 packages/hmr/lib/plugins/index.ts | 0 .../lib/plugins/make-entry-point-plugin.ts | 0 .../hmr/lib/plugins/watch-public-plugin.ts | 0 .../hmr/lib/plugins/watch-rebuild-plugin.ts | 0 packages/hmr/lib/types.ts | 0 packages/hmr/package.json | 2 +- packages/hmr/rollup.config.ts | 0 packages/hmr/tsconfig.json | 0 packages/i18n/.gitignore | 0 packages/i18n/README.md | 0 packages/i18n/index.mts | 0 packages/i18n/lib/consts.ts | 0 packages/i18n/lib/i18n-dev.ts | 0 packages/i18n/lib/i18n-prod.ts | 0 packages/i18n/lib/index.ts | 0 packages/i18n/lib/prepare-build.ts | 0 .../i18n/lib/set-related-locale-import.ts | 0 packages/i18n/lib/types.ts | 0 packages/i18n/locales/en/messages.json | 0 packages/i18n/locales/ko/messages.json | 0 packages/i18n/package.json | 2 +- packages/i18n/prepare-build.tsconfig.json | 0 packages/i18n/tsconfig.json | 0 packages/module-manager/README.md | 0 packages/module-manager/index.mts | 0 .../lib/base/cli-args-processor.ts | 0 packages/module-manager/lib/base/index.ts | 0 .../lib/base/run-module-manager.ts | 0 packages/module-manager/lib/const.ts | 0 packages/module-manager/lib/helpers/index.ts | 0 packages/module-manager/lib/helpers/utils.ts | 0 .../module-manager/lib/helpers/zip-utils.ts | 0 packages/module-manager/lib/index.ts | 0 packages/module-manager/lib/paths.ts | 0 .../lib/processing/delete-feature.ts | 0 .../module-manager/lib/processing/index.ts | 0 .../lib/processing/modules-handler.ts | 0 .../lib/processing/recover-feature.ts | 0 packages/module-manager/lib/types.ts | 0 packages/module-manager/package.json | 2 +- packages/module-manager/tsconfig.json | 0 packages/shared/README.md | 0 packages/shared/const.ts | 0 packages/shared/index.mts | 0 packages/shared/lib/hoc/index.ts | 0 .../shared/lib/hoc/with-error-boundary.tsx | 0 packages/shared/lib/hoc/with-suspense.tsx | 0 packages/shared/lib/hooks/index.ts | 0 packages/shared/lib/hooks/use-storage.ts | 0 packages/shared/lib/hooks/use-storage.tsx | 0 packages/shared/lib/utils/colorful-logger.ts | 0 packages/shared/lib/utils/const.ts | 0 packages/shared/lib/utils/helpers.ts | 0 packages/shared/lib/utils/index.ts | 0 .../shared/lib/utils/init-app-with-shadow.ts | 0 packages/shared/lib/utils/types.ts | 0 packages/shared/package.json | 2 +- packages/shared/tsconfig.json | 0 packages/storage/index.mts | 0 packages/storage/lib/base/base.ts | 0 packages/storage/lib/base/enums.ts | 0 packages/storage/lib/base/index.ts | 0 packages/storage/lib/base/types.ts | 0 .../impl/example-chat-alignment-storage.ts | 0 .../storage/lib/impl/example-theme-storage.ts | 0 packages/storage/lib/impl/index.ts | 0 packages/storage/lib/index.ts | 0 packages/storage/lib/plugin-settings.ts | 0 packages/storage/lib/types.ts | 0 packages/storage/package.json | 2 +- packages/storage/tsconfig.json | 0 packages/tailwindcss-config/package.json | 2 +- .../tailwindcss-config/tailwind.config.ts | 0 packages/tailwindcss-config/tsconfig.json | 0 packages/tsconfig/app.json | 0 packages/tsconfig/base.json | 0 packages/tsconfig/module.json | 0 packages/tsconfig/package.json | 2 +- packages/ui/README.md | 0 packages/ui/global.css | 0 packages/ui/index.ts | 0 packages/ui/lib/assets/warning.svg | 0 packages/ui/lib/components/LoadingSpinner.tsx | 0 packages/ui/lib/components/PluginCard.css | 0 packages/ui/lib/components/PluginCard.tsx | 0 .../ui/lib/components/TestReact19Pure.tsx | 0 packages/ui/lib/components/ToggleButton.tsx | 0 .../components/error-display/ErrorDisplay.tsx | 0 .../components/error-display/ErrorHeader.tsx | 0 .../error-display/ErrorResetButton.tsx | 0 .../error-display/ErrorStackTraceList.tsx | 0 packages/ui/lib/components/index.ts | 0 packages/ui/lib/index.ts | 0 packages/ui/lib/utils.ts | 0 packages/ui/lib/with-ui.ts | 0 packages/ui/package.json | 2 +- packages/ui/tailwind-global.config.ts | 0 packages/ui/tailwind.config.ts | 0 packages/ui/tsconfig.json | 0 packages/ui/types/tailwindcss-config.d.ts | 0 packages/vite-config/index.mts | 0 .../vite-config/lib/build-content-script.ts | 0 .../lib/get-content-script-entires.ts | 0 packages/vite-config/lib/index.ts | 0 packages/vite-config/lib/with-page-config.ts | 0 packages/vite-config/package.json | 2 +- packages/vite-config/tailwind.d.ts | 0 packages/vite-config/tsconfig.json | 0 .../types/vite-plugin-node-polyfills.d.ts | 0 packages/zipper/index.mts | 0 packages/zipper/lib/index.ts | 0 packages/zipper/lib/zip-bundle.ts | 0 packages/zipper/package.json | 2 +- packages/zipper/tsconfig.json | 0 pages/content-runtime/README.md | 0 pages/content-runtime/build.mts | 0 pages/content-runtime/package.json | 2 +- pages/content-runtime/src/matches/all/App.tsx | 0 .../content-runtime/src/matches/all/index.css | 0 .../content-runtime/src/matches/all/index.tsx | 0 .../src/matches/example/App.tsx | 0 .../src/matches/example/index.css | 0 .../src/matches/example/index.tsx | 0 pages/content-runtime/tailwind.config.ts | 0 pages/content-runtime/tailwind.d.ts | 0 pages/content-runtime/tsconfig.json | 0 pages/content-runtime/vite.config.mts | 0 pages/content-ui/README.md | 0 pages/content-ui/build.mts | 0 pages/content-ui/package.json | 2 +- pages/content-ui/postcss.config.cjs | 0 pages/content-ui/public/logo.svg | 0 pages/content-ui/src/matches/all/App.tsx | 0 pages/content-ui/src/matches/all/index.css | 0 pages/content-ui/src/matches/all/index.tsx | 0 pages/content-ui/src/matches/example/App.tsx | 0 .../content-ui/src/matches/example/index.css | 0 .../content-ui/src/matches/example/index.tsx | 0 pages/content-ui/tailwind.config.ts | 0 pages/content-ui/tailwind.d.ts | 0 pages/content-ui/tsconfig.json | 0 pages/content-ui/vite.config.mts | 0 pages/content/README.md | 0 pages/content/build.mts | 0 pages/content/package.json | 2 +- pages/content/public/logo.svg | 0 pages/content/src/matches/all/index.ts | 0 pages/content/src/matches/example/index.ts | 0 pages/content/src/sample-function.ts | 0 pages/content/tsconfig.json | 0 pages/devtools/index.html | 0 pages/devtools/package.json | 2 +- pages/devtools/public/logo.svg | 0 pages/devtools/src/index.ts | 0 pages/devtools/tsconfig.json | 0 pages/devtools/vite.config.mts | 0 pages/new-tab/index.html | 0 pages/new-tab/package.json | 2 +- pages/new-tab/postcss.config.cjs | 0 pages/new-tab/public/logo_horizontal.svg | 0 pages/new-tab/public/logo_horizontal_dark.svg | 0 pages/new-tab/src/NewTab.css | 0 pages/new-tab/src/NewTab.scss | 0 pages/new-tab/src/NewTab.tsx | 0 pages/new-tab/src/index.css | 0 pages/new-tab/src/index.tsx | 0 pages/new-tab/tailwind.config.ts | 0 pages/new-tab/tsconfig.json | 0 pages/new-tab/vite.config.mts | 0 pages/new-tab/vite.config.mts.backup | 0 pages/options/index.html | 0 pages/options/package.json | 2 +- pages/options/postcss.config.cjs | 0 pages/options/public/logo_horizontal.svg | 0 pages/options/public/logo_horizontal_dark.svg | 0 pages/options/src/Options.css | 0 pages/options/src/Options.tsx | 0 pages/options/src/components/APPOptions.tsx | 0 pages/options/src/components/ErrorDisplay.tsx | 0 pages/options/src/components/IDELayout.tsx | 0 pages/options/src/components/LLMSelector.tsx | 16 +- .../options/src/components/LoadingSpinner.tsx | 0 .../src/components/LocalErrorBoundary.tsx | 0 pages/options/src/components/PluginCard.css | 0 pages/options/src/components/PluginCard.tsx | 0 .../options/src/components/PluginDetails.tsx | 40 + pages/options/src/components/PluginsTab.tsx | 0 pages/options/src/components/SettingsTab.tsx | 0 .../options/src/components/ThemeSwitcher.tsx | 0 pages/options/src/components/ToggleButton.tsx | 0 pages/options/src/components/index.ts | 0 pages/options/src/hooks/index.ts | 0 pages/options/src/hooks/useAIKeys.ts | 0 pages/options/src/hooks/usePluginSettings.ts | 8 +- pages/options/src/hooks/usePlugins.ts | 0 pages/options/src/hooks/useTabs.ts | 0 pages/options/src/hooks/useTranslations.ts | 0 pages/options/src/index.css | 0 pages/options/src/index.tsx | 0 pages/options/src/locales/en.json | 0 pages/options/src/locales/ru.json | 0 pages/options/src/utils/encryption.ts | 0 pages/options/src/utils/mcpIntegration.ts | 0 pages/options/src/utils/testAPIKeys.ts | 0 pages/options/tailwind.config.ts | 0 pages/options/tsconfig.json | 0 pages/options/vite.config.mts | 0 pages/side-panel/index.html | 0 pages/side-panel/package.json | 2 +- pages/side-panel/postcss.config.cjs | 0 pages/side-panel/public/logo_vertical.svg | 0 .../side-panel/public/logo_vertical_dark.svg | 0 pages/side-panel/src/SidePanel.css | 0 pages/side-panel/src/SidePanel.tsx | 0 .../side-panel/src/components/DraftStatus.css | 0 .../side-panel/src/components/DraftStatus.tsx | 0 .../src/components/ErrorDisplay.tsx | 0 .../side-panel/src/components/JsonViewer.css | 0 .../side-panel/src/components/JsonViewer.tsx | 0 .../src/components/LoadingSpinner.tsx | 0 .../src/components/LocalErrorBoundary.tsx | 0 .../side-panel/src/components/LogManager.css | 0 .../side-panel/src/components/LogManager.tsx | 0 .../side-panel/src/components/PluginCard.css | 0 .../side-panel/src/components/PluginCard.tsx | 0 .../src/components/PluginControlPanel.css | 0 .../src/components/PluginControlPanel.tsx | 0 .../src/components/PluginDetails.css | 0 .../src/components/PluginDetails.tsx | 0 .../src/components/ToastNotifications.css | 0 .../src/components/ToastNotifications.tsx | 0 .../src/components/ToggleButton.tsx | 0 pages/side-panel/src/hooks/useLazyChatSync.ts | 0 pages/side-panel/src/hooks/useTranslations.ts | 0 pages/side-panel/src/index.css | 0 pages/side-panel/src/index.tsx | 0 pages/side-panel/tailwind.config.ts | 0 pages/side-panel/tsconfig.json | 0 pages/side-panel/vite.config.mts | 0 platform-core/public/logo.svg | 0 .../public/pyodide/fonts/DejaVuSans-Bold.ttf | Bin .../pyodide/fonts/DejaVuSans-BoldOblique.ttf | Bin .../pyodide/fonts/DejaVuSans-Oblique.ttf | Bin .../public/pyodide/fonts/DejaVuSans.ttf | Bin .../pyodide/fonts/DejaVuSansDisplay.ttf | Bin .../pyodide/fonts/DejaVuSansMono-Bold.ttf | Bin .../fonts/DejaVuSansMono-BoldOblique.ttf | Bin .../pyodide/fonts/DejaVuSansMono-Oblique.ttf | Bin .../public/pyodide/fonts/DejaVuSansMono.ttf | Bin .../public/pyodide/fonts/DejaVuSerif-Bold.ttf | Bin .../pyodide/fonts/DejaVuSerif-BoldItalic.ttf | Bin .../pyodide/fonts/DejaVuSerif-Italic.ttf | Bin .../public/pyodide/fonts/DejaVuSerif.ttf | Bin .../pyodide/fonts/DejaVuSerifDisplay.ttf | Bin .../public/pyodide/fonts/Humor-Sans.ttf | Bin .../public/pyodide/fonts/LICENSE_DEJAVU | 0 .../public/pyodide/fonts/LICENSE_STIX | 0 .../public/pyodide/fonts/STIXGeneral.ttf | Bin .../public/pyodide/fonts/STIXGeneralBol.ttf | Bin .../pyodide/fonts/STIXGeneralBolIta.ttf | Bin .../pyodide/fonts/STIXGeneralItalic.ttf | Bin .../public/pyodide/fonts/STIXNonUni.ttf | Bin .../public/pyodide/fonts/STIXNonUniBol.ttf | Bin .../public/pyodide/fonts/STIXNonUniBolIta.ttf | Bin .../public/pyodide/fonts/STIXNonUniIta.ttf | Bin .../pyodide/fonts/STIXSizFiveSymReg.ttf | Bin .../pyodide/fonts/STIXSizFourSymBol.ttf | Bin .../pyodide/fonts/STIXSizFourSymReg.ttf | Bin .../public/pyodide/fonts/STIXSizOneSymBol.ttf | Bin .../public/pyodide/fonts/STIXSizOneSymReg.ttf | Bin .../pyodide/fonts/STIXSizThreeSymBol.ttf | Bin .../pyodide/fonts/STIXSizThreeSymReg.ttf | Bin .../public/pyodide/fonts/STIXSizTwoSymBol.ttf | Bin .../public/pyodide/fonts/STIXSizTwoSymReg.ttf | Bin platform-core/public/pyodide/fonts/cmb10.ttf | Bin platform-core/public/pyodide/fonts/cmex10.ttf | Bin platform-core/public/pyodide/fonts/cmmi10.ttf | Bin platform-core/public/pyodide/fonts/cmr10.ttf | Bin platform-core/public/pyodide/fonts/cmss10.ttf | Bin platform-core/public/pyodide/fonts/cmsy10.ttf | Bin platform-core/public/pyodide/fonts/cmtt10.ttf | Bin platform-core/public/pyodide/package.json | 2 +- .../public/pyodide/pyodide-lock.json | 0 platform-core/public/pyodide/pyodide.asm.js | 0 platform-core/public/pyodide/pyodide.js | 0 platform-core/public/pyodide/pyodide.js.map | 0 .../public/pyodide/python_stdlib.zip | Bin .../beautifulsoup4-4.12.3-py3-none-any.whl | Bin .../certifi-2024.12.14-py3-none-any.whl | Bin .../charset_normalizer-3.3.2-py3-none-any.whl | Bin .../public/wheels/idna-3.7-py3-none-any.whl | Bin .../pyodide_http-0.2.1-py3-none-any.whl | Bin .../wheels/requests-2.31.0-py3-none-any.whl | Bin .../wheels/soupsieve-2.5-py3-none-any.whl | Bin .../wheels/urllib3-2.2.3-py3-none-any.whl | Bin platform-core/src/background.ts | 0 platform-core/ui/PluginCard.css | 0 platform-core/ui/test-harness.js | 0 pnpm-lock.yaml | 0 pnpm-workspace.yaml | 0 public/logo.svg | 0 .../curl-templates/basic-en.curl | 0 .../curl-templates/basic-ru.curl | 0 .../ozon-analyzer/curl-templates/deep-en.curl | 0 .../ozon-analyzer/curl-templates/deep-ru.curl | 0 public/plugins/ozon-analyzer/icon.svg | 0 public/plugins/ozon-analyzer/manifest.json | 0 public/plugins/ozon-analyzer/mcp_server.py | 0 public/plugins/ozon-analyzer/workflow.json | 0 public/plugins/test-chat-plugin/icon.svg | 0 public/plugins/test-chat-plugin/manifest.json | 0 public/plugins/test-chat-plugin/mcp_server.py | 0 public/pyodide/fonts/DejaVuSans-Bold.ttf | Bin .../pyodide/fonts/DejaVuSans-BoldOblique.ttf | Bin public/pyodide/fonts/DejaVuSans-Oblique.ttf | Bin public/pyodide/fonts/DejaVuSans.ttf | Bin public/pyodide/fonts/DejaVuSansDisplay.ttf | Bin public/pyodide/fonts/DejaVuSansMono-Bold.ttf | Bin .../fonts/DejaVuSansMono-BoldOblique.ttf | Bin .../pyodide/fonts/DejaVuSansMono-Oblique.ttf | Bin public/pyodide/fonts/DejaVuSansMono.ttf | Bin public/pyodide/fonts/DejaVuSerif-Bold.ttf | Bin .../pyodide/fonts/DejaVuSerif-BoldItalic.ttf | Bin public/pyodide/fonts/DejaVuSerif-Italic.ttf | Bin public/pyodide/fonts/DejaVuSerif.ttf | Bin public/pyodide/fonts/DejaVuSerifDisplay.ttf | Bin public/pyodide/fonts/Humor-Sans.ttf | Bin public/pyodide/fonts/LICENSE_DEJAVU | 0 public/pyodide/fonts/LICENSE_STIX | 0 public/pyodide/fonts/STIXGeneral.ttf | Bin public/pyodide/fonts/STIXGeneralBol.ttf | Bin public/pyodide/fonts/STIXGeneralBolIta.ttf | Bin public/pyodide/fonts/STIXGeneralItalic.ttf | Bin public/pyodide/fonts/STIXNonUni.ttf | Bin public/pyodide/fonts/STIXNonUniBol.ttf | Bin public/pyodide/fonts/STIXNonUniBolIta.ttf | Bin public/pyodide/fonts/STIXNonUniIta.ttf | Bin public/pyodide/fonts/STIXSizFiveSymReg.ttf | Bin public/pyodide/fonts/STIXSizFourSymBol.ttf | Bin public/pyodide/fonts/STIXSizFourSymReg.ttf | Bin public/pyodide/fonts/STIXSizOneSymBol.ttf | Bin public/pyodide/fonts/STIXSizOneSymReg.ttf | Bin public/pyodide/fonts/STIXSizThreeSymBol.ttf | Bin public/pyodide/fonts/STIXSizThreeSymReg.ttf | Bin public/pyodide/fonts/STIXSizTwoSymBol.ttf | Bin public/pyodide/fonts/STIXSizTwoSymReg.ttf | Bin public/pyodide/fonts/cmb10.ttf | Bin public/pyodide/fonts/cmex10.ttf | Bin public/pyodide/fonts/cmmi10.ttf | Bin public/pyodide/fonts/cmr10.ttf | Bin public/pyodide/fonts/cmss10.ttf | Bin public/pyodide/fonts/cmsy10.ttf | Bin public/pyodide/fonts/cmtt10.ttf | Bin public/pyodide/package.json | 2 +- public/pyodide/pyodide-lock.json | 0 public/pyodide/pyodide.asm.js | 0 public/pyodide/pyodide.js | 0 public/pyodide/pyodide.js.map | 0 public/pyodide/python_stdlib.zip | Bin .../beautifulsoup4-4.12.3-py3-none-any.whl | Bin .../certifi-2024.12.14-py3-none-any.whl | Bin .../charset_normalizer-3.3.2-py3-none-any.whl | Bin public/wheels/idna-3.7-py3-none-any.whl | Bin .../pyodide_http-0.2.1-py3-none-any.whl | Bin .../wheels/requests-2.31.0-py3-none-any.whl | Bin public/wheels/soupsieve-2.5-py3-none-any.whl | Bin public/wheels/urllib3-2.2.3-py3-none-any.whl | Bin renovate.json | 0 secrets.json | 0 src/background/background.ts | 0 src/background/offscreen-manager.ts | 0 src/background/offscreen.ts | 0 src/background/transfer-metadata-manager.ts | 0 src/matches/all/index.tsx | 0 src/matches/example/index.tsx | 0 test-scripts/README-prompt-testing.md | 113 --- test-scripts/README.md | 63 -- test-scripts/advanced-chat-test.js | 382 --------- test-scripts/chat-api-test.js | 222 ----- test-scripts/chat-conversion-test.js | 71 -- test-scripts/chat-debug-commands.js | 233 ------ test-scripts/chat-test-page.html | 394 --------- test-scripts/chat-test.html | 411 ---------- test-scripts/integration-chat-test.js | 409 --------- test-scripts/ozon-test.js | 242 ------ test-scripts/test-chat-functionality.js | 200 ----- test-scripts/test-message-communication.js | 239 ------ test-scripts/test-prompt-system.js | 243 ------ test-scripts/test-pyodide-large-data.js | 205 ----- tests/e2e/config/wdio.browser.conf.ts | 58 -- tests/e2e/config/wdio.conf.ts | 97 --- tests/e2e/config/wdio.d.ts | 7 - tests/e2e/helpers/theme.ts | 26 - tests/e2e/package.json | 24 - tests/e2e/specs/debug-side-panel.test.ts | 139 ---- tests/e2e/specs/page-content-runtime.test.ts | 36 - tests/e2e/specs/page-content-ui.test.ts | 21 - tests/e2e/specs/page-content.test.ts | 33 - tests/e2e/specs/page-new-tab.test.ts | 15 - tests/e2e/specs/page-options.test.ts | 148 ---- tests/e2e/specs/page-popup.test.ts | 12 - tests/e2e/specs/page-side-panel.test.ts | 111 --- tests/e2e/specs/smoke.test.ts | 7 - tests/e2e/tsconfig.json | 12 - tests/e2e/utils/extension-path.ts | 49 -- tests/html-chunking-mock.test.ts | 545 ------------ .../async-message-handler.js | 363 -------- .../browser-detection.js | 321 -------- .../integration-test-report.md | 222 ----- .../mocks/environment.js | 271 ------ .../node-test-runner.js | 94 --- .../specs/ai-api.test.js | 442 ---------- .../specs/bridge-communication.test.js | 774 ------------------ .../specs/cross-environment.test.js | 341 -------- .../specs/html-extraction.test.js | 567 ------------- .../specs/performance-benchmark.js | 381 --------- .../specs/plugin-loader.test.js | 232 ------ .../specs/workflow-engine.test.js | 325 -------- .../ozon-analyzer-integration/test-index.html | 177 ---- .../ozon-analyzer-integration/test-runner.js | 343 -------- tests/ozon-analyzer-integration/test-ui.js | 583 ------------- .../utils/test-framework.js | 181 ---- tests/run-mock-tests.cjs | 212 ----- tests/simple-mock-tests.js | 421 ---------- tools/E2E_TESTING_GUIDE.md | 0 tools/dev_helper.sh | 0 tools/generate-toc-and-crossrefs.cjs | 0 tools/ozon-analyzer-e2e-metrics-collector.js | 0 tools/safe-delete.js | 0 tools/sync_plans.py | 0 tools/test-scenarios.js | 0 tsconfig.json | 0 turbo.json | 0 types/vite-plugin-node-polyfills.d.ts | 0 ui/PluginCard.css | 0 ui/PluginCard.js | 0 ui/log-manager.js | 0 ui/test-harness.js | 0 vite.config.mts | 0 1141 files changed, 285 insertions(+), 32691 deletions(-) mode change 100644 => 100755 .cursor/README.md delete mode 100644 .cursor/backup/README.md delete mode 100644 .cursor/backup/index.mdc delete mode 100644 .cursor/backup/rules/AUTOMATION.md delete mode 100644 .cursor/backup/rules/EXPERIENCE-TRANSFER.md delete mode 100644 .cursor/backup/rules/README.mdc delete mode 100644 .cursor/backup/rules/ai-index.mdc delete mode 100644 .cursor/backup/rules/ai-memory.mdc delete mode 100644 .cursor/backup/rules/ai-optimization.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-chat-system.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-error-handling.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-observability.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-performance.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-plugin.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-project-structure.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-security.mdc delete mode 100644 .cursor/backup/rules/architecture/architecture-workflow.mdc delete mode 100644 .cursor/backup/rules/architecture/file-relationships.mdc delete mode 100644 .cursor/backup/rules/architecture/project-architecture.mdc delete mode 100644 .cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md delete mode 100644 .cursor/backup/rules/cursor-export/ai-memory.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/development-guidelines.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/git-workflow.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc delete mode 100644 .cursor/backup/rules/cursor-export/dev/typescript.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/ai-fallback.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/ai-first.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/knowledge-map.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc delete mode 100644 .cursor/backup/rules/cursor-export/doc/restore-context.mdc delete mode 100644 .cursor/backup/rules/cursor-export/environment.mdc delete mode 100644 .cursor/backup/rules/cursor-export/index.mdc delete mode 100644 .cursor/backup/rules/cursor-export/monorepo-best-practices.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-security.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc delete mode 100644 .cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc delete mode 100644 .cursor/backup/rules/cursor-export/security/validation.mdc delete mode 100644 .cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-animation.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-forms.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-mobile.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-navigation.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-performance.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-react-components.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-styling.mdc delete mode 100644 .cursor/backup/rules/cursor-export/ui/ui-testing.mdc delete mode 100644 .cursor/backup/rules/cursor-export/workflow/automation.mdc delete mode 100644 .cursor/backup/rules/cursor-export/workflow/branches.mdc delete mode 100644 .cursor/backup/rules/cursor-export/workflow/workflow.mdc delete mode 100644 .cursor/backup/rules/dev/date-time-patterns.mdc delete mode 100644 .cursor/backup/rules/dev/development-guidelines.mdc delete mode 100644 .cursor/backup/rules/dev/development-principles.mdc delete mode 100644 .cursor/backup/rules/dev/testing-troubleshooting.mdc delete mode 100644 .cursor/backup/rules/doc/ai-answer-self-check.mdc delete mode 100644 .cursor/backup/rules/doc/command-synchronization.mdc delete mode 100644 .cursor/backup/rules/doc/context-translation-system.mdc delete mode 100644 .cursor/backup/rules/doc/cursor-protection-system.mdc delete mode 100644 .cursor/backup/rules/doc/documentation-map.mdc delete mode 100644 .cursor/backup/rules/doc/internationalization-complete.mdc delete mode 100644 .cursor/backup/rules/doc/universal-commands.mdc delete mode 100644 .cursor/backup/rules/memory-bank/INDEX.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/README.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/component-structure.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/decisions.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/patterns.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/routing.md delete mode 100644 .cursor/backup/rules/memory-bank/architecture/state-management.md delete mode 100644 .cursor/backup/rules/memory-bank/context/README.md delete mode 100644 .cursor/backup/rules/memory-bank/context/dependencies.md delete mode 100644 .cursor/backup/rules/memory-bank/context/deployment.md delete mode 100644 .cursor/backup/rules/memory-bank/context/environment.md delete mode 100644 .cursor/backup/rules/memory-bank/context/tech-stack.md delete mode 100644 .cursor/backup/rules/memory-bank/core/INDEX.md delete mode 100644 .cursor/backup/rules/memory-bank/core/README.md delete mode 100644 .cursor/backup/rules/memory-bank/core/activeContext.md delete mode 100644 .cursor/backup/rules/memory-bank/core/general.md delete mode 100644 .cursor/backup/rules/memory-bank/core/migrated-INDEX.md delete mode 100644 .cursor/backup/rules/memory-bank/core/progress.md delete mode 100644 .cursor/backup/rules/memory-bank/core/projectbrief.md delete mode 100644 .cursor/backup/rules/memory-bank/core/session-log.md delete mode 100644 .cursor/backup/rules/memory-bank/development/README.md delete mode 100644 .cursor/backup/rules/memory-bank/development/build-process.md delete mode 100644 .cursor/backup/rules/memory-bank/development/debugging-guide.md delete mode 100644 .cursor/backup/rules/memory-bank/development/devtools-guide.md delete mode 100644 .cursor/backup/rules/memory-bank/development/testing-results.md delete mode 100644 .cursor/backup/rules/memory-bank/development/version-management.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/README.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/build-errors.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/errors.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/runtime-errors.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/typescript-errors.md delete mode 100644 .cursor/backup/rules/memory-bank/errors/ui-errors.md delete mode 100644 .cursor/backup/rules/memory-bank/memory-bank-controller.mdc delete mode 100644 .cursor/backup/rules/memory-bank/planning/README.md delete mode 100644 .cursor/backup/rules/memory-bank/planning/feature-roadmap.md delete mode 100644 .cursor/backup/rules/memory-bank/planning/migration-plans.md delete mode 100644 .cursor/backup/rules/memory-bank/planning/optimization-plans.md delete mode 100644 .cursor/backup/rules/memory-bank/planning/tech-debt.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/README.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/accessibility.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/component-library.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/performance.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/responsive-design.md delete mode 100644 .cursor/backup/rules/memory-bank/ui/styling-patterns.md delete mode 100644 .cursor/backup/rules/workflow/ci-automation.mdc delete mode 100644 .cursor/backup/rules/workflow/experience-transfer.mdc mode change 100644 => 100755 .cursor/index.mdc delete mode 100644 .cursor/rules/AUTOMATION.md delete mode 100644 .cursor/rules/EXPERIENCE-TRANSFER.md delete mode 100644 .cursor/rules/README.mdc delete mode 100644 .cursor/rules/ai-index.mdc delete mode 100644 .cursor/rules/ai-memory.mdc delete mode 100644 .cursor/rules/ai-optimization.mdc delete mode 100644 .cursor/rules/architecture/architecture-chat-system.mdc delete mode 100644 .cursor/rules/architecture/architecture-error-handling.mdc delete mode 100644 .cursor/rules/architecture/architecture-observability.mdc delete mode 100644 .cursor/rules/architecture/architecture-performance.mdc delete mode 100644 .cursor/rules/architecture/architecture-plugin.mdc delete mode 100644 .cursor/rules/architecture/architecture-project-structure.mdc delete mode 100644 .cursor/rules/architecture/architecture-security.mdc delete mode 100644 .cursor/rules/architecture/architecture-workflow.mdc delete mode 100644 .cursor/rules/architecture/file-relationships.mdc delete mode 100644 .cursor/rules/architecture/project-architecture.mdc delete mode 100644 .cursor/rules/audit-cursor.cjs delete mode 100644 .cursor/rules/auto-translate-requests.cjs delete mode 100644 .cursor/rules/check-rules-structure.cjs delete mode 100644 .cursor/rules/command-sync.cjs delete mode 100644 .cursor/rules/context-translator.cjs delete mode 100644 .cursor/rules/create-rule-for.mdc delete mode 100644 .cursor/rules/create-rule.cjs delete mode 100644 .cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md delete mode 100644 .cursor/rules/cursor-export/ai-memory.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc delete mode 100644 .cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc delete mode 100644 .cursor/rules/cursor-export/dev/development-guidelines.mdc delete mode 100644 .cursor/rules/cursor-export/dev/git-workflow.mdc delete mode 100644 .cursor/rules/cursor-export/dev/security-and-deployment.mdc delete mode 100644 .cursor/rules/cursor-export/dev/testing-and-debugging.mdc delete mode 100644 .cursor/rules/cursor-export/dev/typescript.mdc delete mode 100644 .cursor/rules/cursor-export/doc/ai-fallback.mdc delete mode 100644 .cursor/rules/cursor-export/doc/ai-first.mdc delete mode 100644 .cursor/rules/cursor-export/doc/knowledge-map.mdc delete mode 100644 .cursor/rules/cursor-export/doc/mdc-file-standards.mdc delete mode 100644 .cursor/rules/cursor-export/doc/memorybank-quality.mdc delete mode 100644 .cursor/rules/cursor-export/doc/restore-context.mdc delete mode 100644 .cursor/rules/cursor-export/environment.mdc delete mode 100755 .cursor/rules/cursor-export/import-cursor.cjs delete mode 100644 .cursor/rules/cursor-export/index.mdc delete mode 100644 .cursor/rules/cursor-export/monorepo-best-practices.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-best-practices.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-documentation.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-error-handling.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-performance.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-security.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-structure.mdc delete mode 100644 .cursor/rules/cursor-export/plugin/plugin-testing.mdc delete mode 100644 .cursor/rules/cursor-export/security/validation.mdc delete mode 100644 .cursor/rules/cursor-export/typescript-build-troubleshooting.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-accessibility.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-animation.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-error-handling.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-forms.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-loading-states.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-mobile.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-navigation.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-performance.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-react-components.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-styling.mdc delete mode 100644 .cursor/rules/cursor-export/ui/ui-testing.mdc delete mode 100644 .cursor/rules/cursor-export/workflow/automation.mdc delete mode 100644 .cursor/rules/cursor-export/workflow/branches.mdc delete mode 100644 .cursor/rules/cursor-export/workflow/workflow.mdc delete mode 100644 .cursor/rules/cursor-git-hook.cjs delete mode 100644 .cursor/rules/cursor-manager.cjs delete mode 100644 .cursor/rules/cursor-protector.cjs delete mode 100644 .cursor/rules/dev/date-time-patterns.mdc delete mode 100644 .cursor/rules/dev/development-guidelines.mdc delete mode 100644 .cursor/rules/dev/development-principles.mdc delete mode 100644 .cursor/rules/dev/testing-troubleshooting.mdc delete mode 100644 .cursor/rules/doc/ai-answer-self-check.mdc delete mode 100644 .cursor/rules/doc/always-russian-answer.mdc delete mode 100644 .cursor/rules/doc/auto-translate-requests.mdc delete mode 100644 .cursor/rules/doc/chrome-extension-manual-restore.mdc delete mode 100644 .cursor/rules/doc/command-synchronization.mdc delete mode 100644 .cursor/rules/doc/context-translation-system.mdc delete mode 100644 .cursor/rules/doc/cursor-protection-system.mdc delete mode 100644 .cursor/rules/doc/documentation-map.mdc delete mode 100644 .cursor/rules/doc/internationalization-complete.mdc delete mode 100644 .cursor/rules/doc/universal-commands.mdc delete mode 100644 .cursor/rules/documentation-helper.cjs delete mode 100644 .cursor/rules/environment.yaml delete mode 100644 .cursor/rules/export-cursor.cjs delete mode 100644 .cursor/rules/fix-cursor.cjs delete mode 100644 .cursor/rules/generate-rules-index.cjs delete mode 100644 .cursor/rules/memory-bank-auditor.cjs delete mode 100644 .cursor/rules/memory-bank-manager.cjs delete mode 100644 .cursor/rules/memory-bank-organizer.cjs delete mode 100644 .cursor/rules/memory-bank-structure-creator.cjs delete mode 100644 .cursor/rules/memory-bank/INDEX.md delete mode 100644 .cursor/rules/memory-bank/architecture/README.md delete mode 100644 .cursor/rules/memory-bank/architecture/component-structure.md delete mode 100644 .cursor/rules/memory-bank/architecture/decisions.md delete mode 100644 .cursor/rules/memory-bank/architecture/patterns.md delete mode 100644 .cursor/rules/memory-bank/architecture/routing.md delete mode 100644 .cursor/rules/memory-bank/architecture/state-management.md delete mode 100644 .cursor/rules/memory-bank/context/README.md delete mode 100644 .cursor/rules/memory-bank/context/dependencies.md delete mode 100644 .cursor/rules/memory-bank/context/deployment.md delete mode 100644 .cursor/rules/memory-bank/context/environment.md delete mode 100644 .cursor/rules/memory-bank/context/tech-stack.md delete mode 100644 .cursor/rules/memory-bank/core/INDEX.md delete mode 100644 .cursor/rules/memory-bank/core/README.md delete mode 100644 .cursor/rules/memory-bank/core/activeContext.md delete mode 100644 .cursor/rules/memory-bank/core/general.md delete mode 100644 .cursor/rules/memory-bank/core/migrated-INDEX.md delete mode 100644 .cursor/rules/memory-bank/core/progress.md delete mode 100644 .cursor/rules/memory-bank/core/projectbrief.md delete mode 100644 .cursor/rules/memory-bank/core/session-log.md delete mode 100644 .cursor/rules/memory-bank/development/README.md delete mode 100644 .cursor/rules/memory-bank/development/build-process.md delete mode 100644 .cursor/rules/memory-bank/development/debugging-guide.md delete mode 100644 .cursor/rules/memory-bank/development/devtools-guide.md delete mode 100644 .cursor/rules/memory-bank/development/testing-results.md delete mode 100644 .cursor/rules/memory-bank/development/version-management.md delete mode 100644 .cursor/rules/memory-bank/errors/README.md delete mode 100644 .cursor/rules/memory-bank/errors/build-errors.md delete mode 100644 .cursor/rules/memory-bank/errors/errors.md delete mode 100644 .cursor/rules/memory-bank/errors/runtime-errors.md delete mode 100644 .cursor/rules/memory-bank/errors/typescript-errors.md delete mode 100644 .cursor/rules/memory-bank/errors/ui-errors.md delete mode 100644 .cursor/rules/memory-bank/memory-bank-controller.mdc delete mode 100644 .cursor/rules/memory-bank/planning/README.md delete mode 100644 .cursor/rules/memory-bank/planning/feature-roadmap.md delete mode 100644 .cursor/rules/memory-bank/planning/migration-plans.md delete mode 100644 .cursor/rules/memory-bank/planning/optimization-plans.md delete mode 100644 .cursor/rules/memory-bank/planning/tech-debt.md delete mode 100644 .cursor/rules/memory-bank/ui/README.md delete mode 100644 .cursor/rules/memory-bank/ui/accessibility.md delete mode 100644 .cursor/rules/memory-bank/ui/component-library.md delete mode 100644 .cursor/rules/memory-bank/ui/performance.md delete mode 100644 .cursor/rules/memory-bank/ui/responsive-design.md delete mode 100644 .cursor/rules/memory-bank/ui/styling-patterns.md delete mode 100644 .cursor/rules/optimize-for-ai.cjs delete mode 100644 .cursor/rules/protect-cursor.cjs delete mode 100644 .cursor/rules/request-translator.cjs delete mode 100644 .cursor/rules/save-context.cjs delete mode 100644 .cursor/rules/translate-to-english.cjs delete mode 100644 .cursor/rules/workflow/ci-automation.mdc delete mode 100644 .cursor/rules/workflow/experience-transfer.mdc mode change 100644 => 100755 .cursorignore mode change 100644 => 100755 .example.env mode change 100644 => 100755 .git-secrets mode change 100644 => 100755 .gitattributes mode change 100644 => 100755 .gitguardian.yaml mode change 100644 => 100755 .github/CODEOWNERS mode change 100644 => 100755 .github/FUNDING.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/bug_report.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/config.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/feature_request.yml mode change 100644 => 100755 .github/auto_assign.yml mode change 100644 => 100755 .github/dependabot.yml mode change 100644 => 100755 .github/pull_request_template.md mode change 100644 => 100755 .github/secret_scanning.yml mode change 100644 => 100755 .github/stale.yml mode change 100644 => 100755 .github/workflows/audit.yml mode change 100644 => 100755 .github/workflows/auto-change-prs-branch.yml mode change 100644 => 100755 .github/workflows/build-zip.yml mode change 100644 => 100755 .github/workflows/cancel-other-workflows-on-close.yml mode change 100644 => 100755 .github/workflows/codeql.yml mode change 100644 => 100755 .github/workflows/danger.yml mode change 100644 => 100755 .github/workflows/dependencies-auto-merge.yml mode change 100644 => 100755 .github/workflows/e2e-modular.yml mode change 100644 => 100755 .github/workflows/e2e.yml mode change 100644 => 100755 .github/workflows/greetings.yml mode change 100644 => 100755 .github/workflows/lint.yml mode change 100644 => 100755 .github/workflows/prettier.yml mode change 100644 => 100755 .github/workflows/release.yml mode change 100644 => 100755 .github/workflows/rules-check.yml mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .gitmodules mode change 100644 => 100755 .kilocode/mcp.json mode change 100644 => 100755 .kilocode/rules/general-rules.md mode change 100644 => 100755 .kilocode/rules/restricted_files.md mode change 100644 => 100755 .npmrc mode change 100644 => 100755 .nvmrc mode change 100644 => 100755 .prettierignore mode change 100644 => 100755 .prettierrc mode change 100644 => 100755 .releaserc.json mode change 100644 => 100755 .roo/mcp.json mode change 100644 => 100755 .rules/ai-fallback.rules.md mode change 100644 => 100755 CHANGELOG.md mode change 100644 => 100755 CURSOR_AI_MEMORY_BANK.md mode change 100644 => 100755 DEVELOPER_SETUP.md mode change 100644 => 100755 EXTENSION_TESTING_INSTRUCTIONS.md mode change 100644 => 100755 FUTURE_CHANGES_SUMMARY.md mode change 100644 => 100755 LICENSE mode change 100644 => 100755 MEMORY_BANK.md mode change 100644 => 100755 PLUGIN_DEVELOPMENT.md mode change 100644 => 100755 ProjectGraphAgent/.gitignore mode change 100644 => 100755 ProjectGraphAgent/CHANGELOG.md mode change 100644 => 100755 ProjectGraphAgent/LLM_GUIDELINES.md mode change 100644 => 100755 ProjectGraphAgent/README.md mode change 100644 => 100755 ProjectGraphAgent/README.ru.md mode change 100644 => 100755 ProjectGraphAgent/README_PUBLISH.md mode change 100644 => 100755 ProjectGraphAgent/README_PUBLISH.ru.md mode change 100644 => 100755 ProjectGraphAgent/WORKFLOW_GUIDE.md mode change 100644 => 100755 ProjectGraphAgent/WORKFLOW_GUIDE.ru.md mode change 100644 => 100755 ProjectGraphAgent/adapters/python.mjs mode change 100644 => 100755 ProjectGraphAgent/adapters/typescript.mjs mode change 100644 => 100755 ProjectGraphAgent/graph_parts/README_path_indexing.md mode change 100644 => 100755 ProjectGraphAgent/graph_parts/ai_commands.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/chat-architecture-changes.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/entities.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/meta.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/path_index.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/path_search_examples.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/plans.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/policies.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/relations.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/schema.jsonnet mode change 100644 => 100755 ProjectGraphAgent/graph_parts/templates.jsonnet mode change 100644 => 100755 ProjectGraphAgent/package-lock.json mode change 100644 => 100755 ProjectGraphAgent/package.json mode change 100644 => 100755 ProjectGraphAgent/project_graph.jsonnet mode change 100644 => 100755 ProjectGraphAgent/scripts/ai_committer.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/clean_project.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/graph_generator.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/graph_validator.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/publish_workflow.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/sync_ai_commands.mjs mode change 100644 => 100755 ProjectGraphAgent/scripts/sync_to_standalone.mjs mode change 100644 => 100755 ProjectGraphAgent/settings.json mode change 100644 => 100755 ProjectGraphAgent/test_path_search.js mode change 100644 => 100755 QUICK_START.md mode change 100644 => 100755 UPDATE-PACKAGE-VERSIONS.md mode change 100644 => 100755 USER_COMMANDS.md mode change 100644 => 100755 agent-plugins-platform-v0.5.683.tar.gz mode change 100644 => 100755 analyze-potential-issues.js mode change 100644 => 100755 backup/documentation/cursor-quick-setup.md mode change 100644 => 100755 backup/documentation/cursor-setup-guide.md mode change 100644 => 100755 bash-scripts/copy_env.sh mode change 100644 => 100755 bash-scripts/run-chromium-old-extension.sh mode change 100644 => 100755 bash-scripts/set_global_env.sh mode change 100644 => 100755 bash-scripts/update_version.sh mode change 100644 => 100755 bridge/mcp-bridge.js mode change 100644 => 100755 bridge/pyodide-worker.js mode change 100644 => 100755 bridge/worker-manager.js mode change 100644 => 100755 chrome-extension/COMMUNICATION_DEBUG_README.md mode change 100644 => 100755 chrome-extension/HTML_TRANSMISSION_TEST_INSTRUCTIONS.md mode change 100644 => 100755 chrome-extension/HTML_TRANSMISSION_TEST_REPORT.md mode change 100644 => 100755 chrome-extension/RACE_CONDITION_FIX_SUMMARY.md mode change 100644 => 100755 chrome-extension/generate-large-html.cjs mode change 100644 => 100755 chrome-extension/images/icon-128.xcf mode change 100644 => 100755 chrome-extension/images/icon-34.xcf mode change 100644 => 100755 chrome-extension/large-test-file.html mode change 100644 => 100755 chrome-extension/large-test-summary.txt mode change 100644 => 100755 chrome-extension/manifest.cjs mode change 100644 => 100755 chrome-extension/manifest.ts mode change 100644 => 100755 chrome-extension/package.json mode change 100644 => 100755 chrome-extension/pre-build.tsconfig.json mode change 100644 => 100755 chrome-extension/public/_locales/en/messages.json mode change 100644 => 100755 chrome-extension/public/_locales/ko/messages.json mode change 100644 => 100755 chrome-extension/public/background-worker.js mode change 100644 => 100755 chrome-extension/public/background.js mode change 100644 => 100755 chrome-extension/public/content.css mode change 100644 => 100755 chrome-extension/public/content/workerManager.js mode change 100644 => 100755 chrome-extension/public/icon-128.png mode change 100644 => 100755 chrome-extension/public/icon-34.png mode change 100644 => 100755 chrome-extension/public/manifest.json mode change 100644 => 100755 chrome-extension/public/offscreen-init.js mode change 100644 => 100755 chrome-extension/public/offscreen.html mode change 100644 => 100755 chrome-extension/public/offscreen.js mode change 100644 => 100755 chrome-extension/public/options/assets/index-C7Ftd-oV.css mode change 100644 => 100755 chrome-extension/public/options/assets/index-CNQsTj65.js mode change 100644 => 100755 chrome-extension/public/options/assets/index-CNQsTj65.js.map mode change 100644 => 100755 chrome-extension/public/options/index.html mode change 100644 => 100755 chrome-extension/public/options/logo_horizontal.svg mode change 100644 => 100755 chrome-extension/public/options/logo_horizontal_dark.svg mode change 100644 => 100755 chrome-extension/public/plugins/google-helper/icon.svg mode change 100644 => 100755 chrome-extension/public/plugins/google-helper/manifest.json mode change 100644 => 100755 chrome-extension/public/plugins/google-helper/mcp_server.py mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/MONITORING_README.md mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/README.md mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/__pycache__/mcp_server.cpython-313.pyc mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-en.curl mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/curl-templates/basic_analysis-ru.curl mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-en.curl mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/curl-templates/deep_analysis-ru.curl mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/icon.svg mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/manifest.json mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/mcp_server.py mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/options/index.html mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/production-config.json mode change 100644 => 100755 chrome-extension/public/plugins/ozon-analyzer/workflow.json mode change 100644 => 100755 chrome-extension/public/plugins/test-plugin/icon.svg mode change 100644 => 100755 chrome-extension/public/plugins/test-plugin/manifest.json mode change 100644 => 100755 chrome-extension/public/plugins/test-plugin/mcp_server.py mode change 100644 => 100755 chrome-extension/public/plugins/time-test/icon.svg mode change 100644 => 100755 chrome-extension/public/plugins/time-test/manifest.json mode change 100644 => 100755 chrome-extension/public/plugins/time-test/mcp_server.py mode change 100644 => 100755 chrome-extension/public/plugins/time-test/workflow.json mode change 100644 => 100755 chrome-extension/public/pyodide-worker.js mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSans-Bold.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSans-BoldOblique.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSans-Oblique.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSans.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSansDisplay.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSansMono-Bold.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSansMono-BoldOblique.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSansMono-Oblique.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSansMono.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSerif-Bold.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSerif-BoldItalic.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSerif-Italic.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSerif.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/DejaVuSerifDisplay.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/Humor-Sans.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/LICENSE_DEJAVU mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/LICENSE_STIX mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXGeneral.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXGeneralBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXGeneralBolIta.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXGeneralItalic.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXNonUni.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXNonUniBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXNonUniBolIta.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXNonUniIta.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizFiveSymReg.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizFourSymBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizFourSymReg.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizOneSymBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizOneSymReg.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizThreeSymBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizThreeSymReg.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizTwoSymBol.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/STIXSizTwoSymReg.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmb10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmex10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmmi10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmr10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmss10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmsy10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/fonts/cmtt10.ttf mode change 100644 => 100755 chrome-extension/public/pyodide/package.json mode change 100644 => 100755 chrome-extension/public/pyodide/pyodide-lock.json mode change 100644 => 100755 chrome-extension/public/pyodide/pyodide.asm.js mode change 100644 => 100755 chrome-extension/public/pyodide/pyodide.js mode change 100644 => 100755 chrome-extension/public/pyodide/pyodide.js.map mode change 100644 => 100755 chrome-extension/public/pyodide/python_stdlib.zip mode change 100644 => 100755 chrome-extension/public/pyodide/test.py delete mode 100644 chrome-extension/public/side-panel/assets/index-BGEZNpCW.js.map rename chrome-extension/public/side-panel/assets/{index-BGEZNpCW.js => index-D46GpuPf.js} (61%) mode change 100644 => 100755 create mode 100755 chrome-extension/public/side-panel/assets/index-D46GpuPf.js.map mode change 100644 => 100755 chrome-extension/public/side-panel/assets/index-DTNx5ty5.css mode change 100644 => 100755 chrome-extension/public/side-panel/index.html mode change 100644 => 100755 chrome-extension/public/side-panel/logo_vertical.svg mode change 100644 => 100755 chrome-extension/public/side-panel/logo_vertical_dark.svg mode change 100644 => 100755 chrome-extension/public/test-scripts/README-pyodide-manual-testing.md mode change 100644 => 100755 chrome-extension/public/test-scripts/README.md mode change 100644 => 100755 chrome-extension/public/test-scripts/demo-console-commands.js mode change 100644 => 100755 chrome-extension/public/test-scripts/integration-test-helpers.js mode change 100644 => 100755 chrome-extension/public/test-scripts/integration-test.html mode change 100644 => 100755 chrome-extension/public/test-scripts/integration-test.js mode change 100644 => 100755 chrome-extension/public/test-scripts/ozon-test.js mode change 100644 => 100755 chrome-extension/public/test-scripts/page-ui.js mode change 100644 => 100755 chrome-extension/public/test-scripts/pyodide-offscreen-manual-test.js mode change 100644 => 100755 chrome-extension/public/test-scripts/test-loader.js mode change 100644 => 100755 chrome-extension/public/wheels/beautifulsoup4-4.12.3-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/certifi-2024.12.14-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/charset_normalizer-3.3.2-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/idna-3.7-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/pyodide_http-0.2.1-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/requests-2.31.0-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/soupsieve-2.5-py3-none-any.whl mode change 100644 => 100755 chrome-extension/public/wheels/urllib3-2.2.3-py3-none-any.whl mode change 100644 => 100755 chrome-extension/src/background/ai-api-client.ts mode change 100644 => 100755 chrome-extension/src/background/background-worker.ts mode change 100644 => 100755 chrome-extension/src/background/host-api.ts mode change 100644 => 100755 chrome-extension/src/background/index.ts mode change 100644 => 100755 chrome-extension/src/background/index.ts.backup mode change 100644 => 100755 chrome-extension/src/background/mcp-bridge.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/alert-manager.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/error-tracker.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/html-extraction-monitor.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/index.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/logger.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/metrics-collector.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/monitoring-core.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/network-tracker.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/performance-monitor.ts mode change 100644 => 100755 chrome-extension/src/background/monitoring/pyodide-monitor.ts mode change 100644 => 100755 chrome-extension/src/background/package-lock.json mode change 100644 => 100755 chrome-extension/src/background/package.json mode change 100644 => 100755 chrome-extension/src/background/plugin-chat-api.ts mode change 100644 => 100755 chrome-extension/src/background/plugin-chat-cache.ts mode change 100644 => 100755 chrome-extension/src/background/plugin-manager.ts mode change 100644 => 100755 chrome-extension/src/background/prompt-test-handler.ts mode change 100644 => 100755 chrome-extension/src/background/pyodide-worker.js mode change 100644 => 100755 chrome-extension/src/background/worker-manager.ts mode change 100644 => 100755 chrome-extension/src/background/workflow-engine.ts mode change 100644 => 100755 chrome-extension/src/content/workerManager.js mode change 100644 => 100755 chrome-extension/src/side-panel/components/JsonViewer.css mode change 100644 => 100755 chrome-extension/src/side-panel/components/JsonViewer.tsx mode change 100644 => 100755 chrome-extension/src/side-panel/components/LogManager.css mode change 100644 => 100755 chrome-extension/src/side-panel/components/LogManager.tsx mode change 100644 => 100755 chrome-extension/src/side-panel/components/PluginCard.css mode change 100644 => 100755 chrome-extension/src/side-panel/components/PluginCard.tsx mode change 100644 => 100755 chrome-extension/src/side-panel/components/ToastNotifications.css mode change 100644 => 100755 chrome-extension/src/side-panel/components/ToastNotifications.tsx mode change 100644 => 100755 chrome-extension/tsconfig.json mode change 100644 => 100755 chrome-extension/utils/cleanup-build.cjs mode change 100644 => 100755 chrome-extension/utils/plugins/make-manifest-plugin.ts mode change 100644 => 100755 chrome-extension/vite.config.mts mode change 100644 => 100755 commitlint.config.cjs mode change 100644 => 100755 core/host-api.js mode change 100644 => 100755 core/plugin-manager.js mode change 100644 => 100755 core/workflow-engine.js mode change 100644 => 100755 danger/dangerfile.js mode change 100644 => 100755 docs/README-ozon-analyzer.md mode change 100644 => 100755 docs/README.md mode change 100644 => 100755 docs/administration/Cursor_Rules_Index.en.md mode change 100644 => 100755 docs/administration/README.md mode change 100644 => 100755 docs/api-documentation.md mode change 100644 => 100755 docs/api/.nojekyll mode change 100644 => 100755 docs/api/assets/hierarchy.js mode change 100644 => 100755 docs/api/assets/highlight.css mode change 100644 => 100755 docs/api/assets/icons.js mode change 100644 => 100755 docs/api/assets/icons.svg mode change 100644 => 100755 docs/api/assets/main.js mode change 100644 => 100755 docs/api/assets/navigation.js mode change 100644 => 100755 docs/api/assets/search.js mode change 100644 => 100755 docs/api/assets/style.css mode change 100644 => 100755 docs/api/functions/streamFileToZip.html mode change 100644 => 100755 docs/api/hierarchy.html mode change 100644 => 100755 docs/api/index.html mode change 100644 => 100755 docs/api/interfaces/IManifestParser.html mode change 100644 => 100755 docs/api/modules.html mode change 100644 => 100755 docs/api/variables/ManifestParser.html mode change 100644 => 100755 docs/architecture.md mode change 100644 => 100755 docs/chrome_pyodide/chrome_extension_pyodide.txt mode change 100644 => 100755 docs/chrome_pyodide/chrome_pyodide_readme.md mode change 100644 => 100755 docs/developer-commands.md mode change 100644 => 100755 docs/devtools-panel-troubleshooting.md mode change 100644 => 100755 docs/devtools-panel-usage.md mode change 100644 => 100755 docs/devtools-testing-guide.md mode change 100644 => 100755 docs/examples.md mode change 100644 => 100755 docs/gemini-delegation-summary.md mode change 100644 => 100755 docs/integration-guide.md mode change 100644 => 100755 docs/message-port-fix-summary.md mode change 100644 => 100755 docs/monitoring-alerting.md mode change 100644 => 100755 docs/onboarding.md mode change 100644 => 100755 docs/performance-benchmarking.md mode change 100644 => 100755 docs/plugins/ozon-analyzer-integration-guide.md mode change 100644 => 100755 docs/plugins/ozon-analyzer-technical-spec.md mode change 100644 => 100755 docs/plugins/ozon-analyzer-ui-documentation.md mode change 100644 => 100755 docs/security-compliance.md mode change 100644 => 100755 docs/transfer-best-practices-user.md mode change 100644 => 100755 docs/transferability.ru.md mode change 100644 => 100755 docs/troubleshooting.md mode change 100644 => 100755 e2e_test_diagram.md mode change 100644 => 100755 eslint.config.ts mode change 100644 => 100755 memory-bank/INDEX.md mode change 100644 => 100755 memory-bank/MEMORY_BANK_STRUCTURE.md mode change 100644 => 100755 memory-bank/README.md mode change 100644 => 100755 memory-bank/architecture/README.md mode change 100644 => 100755 memory-bank/architecture/architecture-decisions.md mode change 100644 => 100755 memory-bank/architecture/comprehensive-architecture.md mode change 100644 => 100755 memory-bank/architecture/manifest-data-flow.md mode change 100644 => 100755 memory-bank/architecture/platform-core-analysis.md mode change 100644 => 100755 memory-bank/architecture/plugin-system-integration.md mode change 100644 => 100755 memory-bank/architecture/security-architecture-update.md mode change 100644 => 100755 memory-bank/architecture/security-architecture.md mode change 100644 => 100755 memory-bank/architecture/systemPatterns.md mode change 100644 => 100755 memory-bank/audit_logs.md mode change 100644 => 100755 memory-bank/chrome-extension-manual-restore.md mode change 100644 => 100755 memory-bank/context/ENVIRONMENT.md mode change 100644 => 100755 memory-bank/context/README.md mode change 100644 => 100755 memory-bank/context/excluded-directories.md mode change 100644 => 100755 memory-bank/context/productContext.md mode change 100644 => 100755 memory-bank/context/techContext.md mode change 100644 => 100755 memory-bank/core/README.md mode change 100644 => 100755 memory-bank/core/activeContext.md mode change 100644 => 100755 memory-bank/core/api-keys-security-implementation.md mode change 100644 => 100755 memory-bank/core/backup/activeContext-2025-07-19T02-38-14-572Z.md mode change 100644 => 100755 memory-bank/core/backup/activeContext-2025-07-19T02-38-25-140Z.md mode change 100644 => 100755 memory-bank/core/backup/progress-2025-07-19T02-38-14-572Z.md mode change 100644 => 100755 memory-bank/core/backup/progress-2025-07-19T02-38-25-140Z.md mode change 100644 => 100755 memory-bank/core/plugin-adaptations.md mode change 100644 => 100755 memory-bank/core/progress.md mode change 100644 => 100755 memory-bank/core/projectbrief.md mode change 100644 => 100755 memory-bank/core/session-log.md mode change 100644 => 100755 memory-bank/cursor-saved-memories.md mode change 100644 => 100755 memory-bank/cursor-user-rules-simple.md mode change 100644 => 100755 memory-bank/deprecated/AI-access-policy.md mode change 100644 => 100755 memory-bank/deprecated/ai-barrel-exports-best-practices.md mode change 100644 => 100755 memory-bank/deprecated/ai-user-commands-reference.md mode change 100644 => 100755 memory-bank/deprecated/cursor-project-rules.md mode change 100644 => 100755 memory-bank/deprecated/cursor-saved-memories-analysis.md mode change 100644 => 100755 memory-bank/deprecated/cursor-saved-memories.md mode change 100644 => 100755 memory-bank/deprecated/cursor-update-mechanism.md mode change 100644 => 100755 memory-bank/deprecated/cursor-user-rules.md mode change 100644 => 100755 memory-bank/deprecated/mdc-file-standards.md mode change 100644 => 100755 memory-bank/deprecated/user-commands.md mode change 100644 => 100755 memory-bank/development/README.md mode change 100644 => 100755 memory-bank/development/barrel-exports-best-practices.md mode change 100644 => 100755 memory-bank/development/build-sources-mapping.md mode change 100644 => 100755 memory-bank/development/debug-testing-guide.md mode change 100644 => 100755 memory-bank/development/devtools-testing-guide.md mode change 100644 => 100755 memory-bank/development/ozon-analyzer-testing.md mode change 100644 => 100755 memory-bank/development/testing-results.md mode change 100644 => 100755 memory-bank/development/user-commands.md mode change 100644 => 100755 memory-bank/development/version-management.md mode change 100644 => 100755 memory-bank/diagrams/graph.mmd mode change 100644 => 100755 memory-bank/drift.md mode change 100644 => 100755 memory-bank/errors/ERRORS_AND_SOLUTIONS.md mode change 100644 => 100755 memory-bank/errors/README.md mode change 100644 => 100755 memory-bank/errors/error-graveyard.md mode change 100644 => 100755 memory-bank/errors/errors.md mode change 100644 => 100755 memory-bank/errors/typescript-build-troubleshooting-experience.md mode change 100644 => 100755 memory-bank/errors/vite-react19-errors.md mode change 100644 => 100755 memory-bank/planning/README.md mode change 100644 => 100755 memory-bank/planning/documentation-optimization-plan.md mode change 100644 => 100755 memory-bank/planning/documentation-optimization-summary.md mode change 100644 => 100755 memory-bank/planning/future-plans.md mode change 100644 => 100755 memory-bank/plans/README.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/README.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/architecture/chat-architecture.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/docs/code-changes-summary.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/docs/problems-solved.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/docs/project-overview.md mode change 100644 => 100755 memory-bank/projects/chrome-extension-chat-recovery/testing/testing-results.md mode change 100644 => 100755 memory-bank/ui/README.md mode change 100644 => 100755 memory-bank/ui/chat-context-fix.md mode change 100644 => 100755 memory-bank/ui/chat-messages-interface.md mode change 100644 => 100755 memory-bank/ui/chat-text-alignment-settings.md mode change 100644 => 100755 memory-bank/ui/html-transmission-settings.md mode change 100644 => 100755 memory-bank/ui/lazy-chat-sync.md mode change 100644 => 100755 memory-bank/ui/options-scrolling-fix.md mode change 100644 => 100755 memory-bank/ui/ozon-analyzer-ui-integration.md mode change 100644 => 100755 memory-bank/ui/plugin-control-panel-buttons.md mode change 100644 => 100755 memory-bank/ui/plugin-settings-transmission.md mode change 100644 => 100755 memory-bank/ui/side-panel-improvements.md mode change 100644 => 100755 memory-bank/ui/theme-switcher-component.md mode change 100644 => 100755 memory-bank/ui/theme-switching-settings.md mode change 100644 => 100755 messaging-example/background.js mode change 100644 => 100755 messaging-example/eslint.config.js mode change 100644 => 100755 messaging-example/manifest.json mode change 100644 => 100755 messaging-example/side-panel.html mode change 100644 => 100755 messaging-example/side-panel.js mode change 100644 => 100755 nohup.out mode change 100644 => 100755 package.json mode change 100644 => 100755 packages/dev-utils/README.md mode change 100644 => 100755 packages/dev-utils/index.mts mode change 100644 => 100755 packages/dev-utils/lib/index.ts mode change 100644 => 100755 packages/dev-utils/lib/manifest-parser/impl.ts mode change 100644 => 100755 packages/dev-utils/lib/manifest-parser/index.ts mode change 100644 => 100755 packages/dev-utils/lib/manifest-parser/types.ts mode change 100644 => 100755 packages/dev-utils/lib/stream-file-to-zip.ts mode change 100644 => 100755 packages/dev-utils/package.json mode change 100644 => 100755 packages/dev-utils/tsconfig.json mode change 100644 => 100755 packages/env/README.md mode change 100644 => 100755 packages/env/index.mts mode change 100644 => 100755 packages/env/lib/config.ts mode change 100644 => 100755 packages/env/lib/const.ts mode change 100644 => 100755 packages/env/lib/index.ts mode change 100644 => 100755 packages/env/lib/types.ts mode change 100644 => 100755 packages/env/package.json mode change 100644 => 100755 packages/env/tsconfig.json mode change 100644 => 100755 packages/env/use-env-example.png mode change 100644 => 100755 packages/hmr/index.mts mode change 100644 => 100755 packages/hmr/lib/consts.ts mode change 100644 => 100755 packages/hmr/lib/initializers/init-client.ts mode change 100644 => 100755 packages/hmr/lib/initializers/init-reload-server.ts mode change 100644 => 100755 packages/hmr/lib/injections/refresh.ts mode change 100644 => 100755 packages/hmr/lib/injections/reload.ts mode change 100644 => 100755 packages/hmr/lib/interpreter/index.ts mode change 100644 => 100755 packages/hmr/lib/plugins/index.ts mode change 100644 => 100755 packages/hmr/lib/plugins/make-entry-point-plugin.ts mode change 100644 => 100755 packages/hmr/lib/plugins/watch-public-plugin.ts mode change 100644 => 100755 packages/hmr/lib/plugins/watch-rebuild-plugin.ts mode change 100644 => 100755 packages/hmr/lib/types.ts mode change 100644 => 100755 packages/hmr/package.json mode change 100644 => 100755 packages/hmr/rollup.config.ts mode change 100644 => 100755 packages/hmr/tsconfig.json mode change 100644 => 100755 packages/i18n/.gitignore mode change 100644 => 100755 packages/i18n/README.md mode change 100644 => 100755 packages/i18n/index.mts mode change 100644 => 100755 packages/i18n/lib/consts.ts mode change 100644 => 100755 packages/i18n/lib/i18n-dev.ts mode change 100644 => 100755 packages/i18n/lib/i18n-prod.ts mode change 100644 => 100755 packages/i18n/lib/index.ts mode change 100644 => 100755 packages/i18n/lib/prepare-build.ts mode change 100644 => 100755 packages/i18n/lib/set-related-locale-import.ts mode change 100644 => 100755 packages/i18n/lib/types.ts mode change 100644 => 100755 packages/i18n/locales/en/messages.json mode change 100644 => 100755 packages/i18n/locales/ko/messages.json mode change 100644 => 100755 packages/i18n/package.json mode change 100644 => 100755 packages/i18n/prepare-build.tsconfig.json mode change 100644 => 100755 packages/i18n/tsconfig.json mode change 100644 => 100755 packages/module-manager/README.md mode change 100644 => 100755 packages/module-manager/index.mts mode change 100644 => 100755 packages/module-manager/lib/base/cli-args-processor.ts mode change 100644 => 100755 packages/module-manager/lib/base/index.ts mode change 100644 => 100755 packages/module-manager/lib/base/run-module-manager.ts mode change 100644 => 100755 packages/module-manager/lib/const.ts mode change 100644 => 100755 packages/module-manager/lib/helpers/index.ts mode change 100644 => 100755 packages/module-manager/lib/helpers/utils.ts mode change 100644 => 100755 packages/module-manager/lib/helpers/zip-utils.ts mode change 100644 => 100755 packages/module-manager/lib/index.ts mode change 100644 => 100755 packages/module-manager/lib/paths.ts mode change 100644 => 100755 packages/module-manager/lib/processing/delete-feature.ts mode change 100644 => 100755 packages/module-manager/lib/processing/index.ts mode change 100644 => 100755 packages/module-manager/lib/processing/modules-handler.ts mode change 100644 => 100755 packages/module-manager/lib/processing/recover-feature.ts mode change 100644 => 100755 packages/module-manager/lib/types.ts mode change 100644 => 100755 packages/module-manager/package.json mode change 100644 => 100755 packages/module-manager/tsconfig.json mode change 100644 => 100755 packages/shared/README.md mode change 100644 => 100755 packages/shared/const.ts mode change 100644 => 100755 packages/shared/index.mts mode change 100644 => 100755 packages/shared/lib/hoc/index.ts mode change 100644 => 100755 packages/shared/lib/hoc/with-error-boundary.tsx mode change 100644 => 100755 packages/shared/lib/hoc/with-suspense.tsx mode change 100644 => 100755 packages/shared/lib/hooks/index.ts mode change 100644 => 100755 packages/shared/lib/hooks/use-storage.ts mode change 100644 => 100755 packages/shared/lib/hooks/use-storage.tsx mode change 100644 => 100755 packages/shared/lib/utils/colorful-logger.ts mode change 100644 => 100755 packages/shared/lib/utils/const.ts mode change 100644 => 100755 packages/shared/lib/utils/helpers.ts mode change 100644 => 100755 packages/shared/lib/utils/index.ts mode change 100644 => 100755 packages/shared/lib/utils/init-app-with-shadow.ts mode change 100644 => 100755 packages/shared/lib/utils/types.ts mode change 100644 => 100755 packages/shared/package.json mode change 100644 => 100755 packages/shared/tsconfig.json mode change 100644 => 100755 packages/storage/index.mts mode change 100644 => 100755 packages/storage/lib/base/base.ts mode change 100644 => 100755 packages/storage/lib/base/enums.ts mode change 100644 => 100755 packages/storage/lib/base/index.ts mode change 100644 => 100755 packages/storage/lib/base/types.ts mode change 100644 => 100755 packages/storage/lib/impl/example-chat-alignment-storage.ts mode change 100644 => 100755 packages/storage/lib/impl/example-theme-storage.ts mode change 100644 => 100755 packages/storage/lib/impl/index.ts mode change 100644 => 100755 packages/storage/lib/index.ts mode change 100644 => 100755 packages/storage/lib/plugin-settings.ts mode change 100644 => 100755 packages/storage/lib/types.ts mode change 100644 => 100755 packages/storage/package.json mode change 100644 => 100755 packages/storage/tsconfig.json mode change 100644 => 100755 packages/tailwindcss-config/package.json mode change 100644 => 100755 packages/tailwindcss-config/tailwind.config.ts mode change 100644 => 100755 packages/tailwindcss-config/tsconfig.json mode change 100644 => 100755 packages/tsconfig/app.json mode change 100644 => 100755 packages/tsconfig/base.json mode change 100644 => 100755 packages/tsconfig/module.json mode change 100644 => 100755 packages/tsconfig/package.json mode change 100644 => 100755 packages/ui/README.md mode change 100644 => 100755 packages/ui/global.css mode change 100644 => 100755 packages/ui/index.ts mode change 100644 => 100755 packages/ui/lib/assets/warning.svg mode change 100644 => 100755 packages/ui/lib/components/LoadingSpinner.tsx mode change 100644 => 100755 packages/ui/lib/components/PluginCard.css mode change 100644 => 100755 packages/ui/lib/components/PluginCard.tsx mode change 100644 => 100755 packages/ui/lib/components/TestReact19Pure.tsx mode change 100644 => 100755 packages/ui/lib/components/ToggleButton.tsx mode change 100644 => 100755 packages/ui/lib/components/error-display/ErrorDisplay.tsx mode change 100644 => 100755 packages/ui/lib/components/error-display/ErrorHeader.tsx mode change 100644 => 100755 packages/ui/lib/components/error-display/ErrorResetButton.tsx mode change 100644 => 100755 packages/ui/lib/components/error-display/ErrorStackTraceList.tsx mode change 100644 => 100755 packages/ui/lib/components/index.ts mode change 100644 => 100755 packages/ui/lib/index.ts mode change 100644 => 100755 packages/ui/lib/utils.ts mode change 100644 => 100755 packages/ui/lib/with-ui.ts mode change 100644 => 100755 packages/ui/package.json mode change 100644 => 100755 packages/ui/tailwind-global.config.ts mode change 100644 => 100755 packages/ui/tailwind.config.ts mode change 100644 => 100755 packages/ui/tsconfig.json mode change 100644 => 100755 packages/ui/types/tailwindcss-config.d.ts mode change 100644 => 100755 packages/vite-config/index.mts mode change 100644 => 100755 packages/vite-config/lib/build-content-script.ts mode change 100644 => 100755 packages/vite-config/lib/get-content-script-entires.ts mode change 100644 => 100755 packages/vite-config/lib/index.ts mode change 100644 => 100755 packages/vite-config/lib/with-page-config.ts mode change 100644 => 100755 packages/vite-config/package.json mode change 100644 => 100755 packages/vite-config/tailwind.d.ts mode change 100644 => 100755 packages/vite-config/tsconfig.json mode change 100644 => 100755 packages/vite-config/types/vite-plugin-node-polyfills.d.ts mode change 100644 => 100755 packages/zipper/index.mts mode change 100644 => 100755 packages/zipper/lib/index.ts mode change 100644 => 100755 packages/zipper/lib/zip-bundle.ts mode change 100644 => 100755 packages/zipper/package.json mode change 100644 => 100755 packages/zipper/tsconfig.json mode change 100644 => 100755 pages/content-runtime/README.md mode change 100644 => 100755 pages/content-runtime/build.mts mode change 100644 => 100755 pages/content-runtime/package.json mode change 100644 => 100755 pages/content-runtime/src/matches/all/App.tsx mode change 100644 => 100755 pages/content-runtime/src/matches/all/index.css mode change 100644 => 100755 pages/content-runtime/src/matches/all/index.tsx mode change 100644 => 100755 pages/content-runtime/src/matches/example/App.tsx mode change 100644 => 100755 pages/content-runtime/src/matches/example/index.css mode change 100644 => 100755 pages/content-runtime/src/matches/example/index.tsx mode change 100644 => 100755 pages/content-runtime/tailwind.config.ts mode change 100644 => 100755 pages/content-runtime/tailwind.d.ts mode change 100644 => 100755 pages/content-runtime/tsconfig.json mode change 100644 => 100755 pages/content-runtime/vite.config.mts mode change 100644 => 100755 pages/content-ui/README.md mode change 100644 => 100755 pages/content-ui/build.mts mode change 100644 => 100755 pages/content-ui/package.json mode change 100644 => 100755 pages/content-ui/postcss.config.cjs mode change 100644 => 100755 pages/content-ui/public/logo.svg mode change 100644 => 100755 pages/content-ui/src/matches/all/App.tsx mode change 100644 => 100755 pages/content-ui/src/matches/all/index.css mode change 100644 => 100755 pages/content-ui/src/matches/all/index.tsx mode change 100644 => 100755 pages/content-ui/src/matches/example/App.tsx mode change 100644 => 100755 pages/content-ui/src/matches/example/index.css mode change 100644 => 100755 pages/content-ui/src/matches/example/index.tsx mode change 100644 => 100755 pages/content-ui/tailwind.config.ts mode change 100644 => 100755 pages/content-ui/tailwind.d.ts mode change 100644 => 100755 pages/content-ui/tsconfig.json mode change 100644 => 100755 pages/content-ui/vite.config.mts mode change 100644 => 100755 pages/content/README.md mode change 100644 => 100755 pages/content/build.mts mode change 100644 => 100755 pages/content/package.json mode change 100644 => 100755 pages/content/public/logo.svg mode change 100644 => 100755 pages/content/src/matches/all/index.ts mode change 100644 => 100755 pages/content/src/matches/example/index.ts mode change 100644 => 100755 pages/content/src/sample-function.ts mode change 100644 => 100755 pages/content/tsconfig.json mode change 100644 => 100755 pages/devtools/index.html mode change 100644 => 100755 pages/devtools/package.json mode change 100644 => 100755 pages/devtools/public/logo.svg mode change 100644 => 100755 pages/devtools/src/index.ts mode change 100644 => 100755 pages/devtools/tsconfig.json mode change 100644 => 100755 pages/devtools/vite.config.mts mode change 100644 => 100755 pages/new-tab/index.html mode change 100644 => 100755 pages/new-tab/package.json mode change 100644 => 100755 pages/new-tab/postcss.config.cjs mode change 100644 => 100755 pages/new-tab/public/logo_horizontal.svg mode change 100644 => 100755 pages/new-tab/public/logo_horizontal_dark.svg mode change 100644 => 100755 pages/new-tab/src/NewTab.css mode change 100644 => 100755 pages/new-tab/src/NewTab.scss mode change 100644 => 100755 pages/new-tab/src/NewTab.tsx mode change 100644 => 100755 pages/new-tab/src/index.css mode change 100644 => 100755 pages/new-tab/src/index.tsx mode change 100644 => 100755 pages/new-tab/tailwind.config.ts mode change 100644 => 100755 pages/new-tab/tsconfig.json mode change 100644 => 100755 pages/new-tab/vite.config.mts mode change 100644 => 100755 pages/new-tab/vite.config.mts.backup mode change 100644 => 100755 pages/options/index.html mode change 100644 => 100755 pages/options/package.json mode change 100644 => 100755 pages/options/postcss.config.cjs mode change 100644 => 100755 pages/options/public/logo_horizontal.svg mode change 100644 => 100755 pages/options/public/logo_horizontal_dark.svg mode change 100644 => 100755 pages/options/src/Options.css mode change 100644 => 100755 pages/options/src/Options.tsx mode change 100644 => 100755 pages/options/src/components/APPOptions.tsx mode change 100644 => 100755 pages/options/src/components/ErrorDisplay.tsx mode change 100644 => 100755 pages/options/src/components/IDELayout.tsx mode change 100644 => 100755 pages/options/src/components/LLMSelector.tsx mode change 100644 => 100755 pages/options/src/components/LoadingSpinner.tsx mode change 100644 => 100755 pages/options/src/components/LocalErrorBoundary.tsx mode change 100644 => 100755 pages/options/src/components/PluginCard.css mode change 100644 => 100755 pages/options/src/components/PluginCard.tsx mode change 100644 => 100755 pages/options/src/components/PluginDetails.tsx mode change 100644 => 100755 pages/options/src/components/PluginsTab.tsx mode change 100644 => 100755 pages/options/src/components/SettingsTab.tsx mode change 100644 => 100755 pages/options/src/components/ThemeSwitcher.tsx mode change 100644 => 100755 pages/options/src/components/ToggleButton.tsx mode change 100644 => 100755 pages/options/src/components/index.ts mode change 100644 => 100755 pages/options/src/hooks/index.ts mode change 100644 => 100755 pages/options/src/hooks/useAIKeys.ts mode change 100644 => 100755 pages/options/src/hooks/usePluginSettings.ts mode change 100644 => 100755 pages/options/src/hooks/usePlugins.ts mode change 100644 => 100755 pages/options/src/hooks/useTabs.ts mode change 100644 => 100755 pages/options/src/hooks/useTranslations.ts mode change 100644 => 100755 pages/options/src/index.css mode change 100644 => 100755 pages/options/src/index.tsx mode change 100644 => 100755 pages/options/src/locales/en.json mode change 100644 => 100755 pages/options/src/locales/ru.json mode change 100644 => 100755 pages/options/src/utils/encryption.ts mode change 100644 => 100755 pages/options/src/utils/mcpIntegration.ts mode change 100644 => 100755 pages/options/src/utils/testAPIKeys.ts mode change 100644 => 100755 pages/options/tailwind.config.ts mode change 100644 => 100755 pages/options/tsconfig.json mode change 100644 => 100755 pages/options/vite.config.mts mode change 100644 => 100755 pages/side-panel/index.html mode change 100644 => 100755 pages/side-panel/package.json mode change 100644 => 100755 pages/side-panel/postcss.config.cjs mode change 100644 => 100755 pages/side-panel/public/logo_vertical.svg mode change 100644 => 100755 pages/side-panel/public/logo_vertical_dark.svg mode change 100644 => 100755 pages/side-panel/src/SidePanel.css mode change 100644 => 100755 pages/side-panel/src/SidePanel.tsx mode change 100644 => 100755 pages/side-panel/src/components/DraftStatus.css mode change 100644 => 100755 pages/side-panel/src/components/DraftStatus.tsx mode change 100644 => 100755 pages/side-panel/src/components/ErrorDisplay.tsx mode change 100644 => 100755 pages/side-panel/src/components/JsonViewer.css mode change 100644 => 100755 pages/side-panel/src/components/JsonViewer.tsx mode change 100644 => 100755 pages/side-panel/src/components/LoadingSpinner.tsx mode change 100644 => 100755 pages/side-panel/src/components/LocalErrorBoundary.tsx mode change 100644 => 100755 pages/side-panel/src/components/LogManager.css mode change 100644 => 100755 pages/side-panel/src/components/LogManager.tsx mode change 100644 => 100755 pages/side-panel/src/components/PluginCard.css mode change 100644 => 100755 pages/side-panel/src/components/PluginCard.tsx mode change 100644 => 100755 pages/side-panel/src/components/PluginControlPanel.css mode change 100644 => 100755 pages/side-panel/src/components/PluginControlPanel.tsx mode change 100644 => 100755 pages/side-panel/src/components/PluginDetails.css mode change 100644 => 100755 pages/side-panel/src/components/PluginDetails.tsx mode change 100644 => 100755 pages/side-panel/src/components/ToastNotifications.css mode change 100644 => 100755 pages/side-panel/src/components/ToastNotifications.tsx mode change 100644 => 100755 pages/side-panel/src/components/ToggleButton.tsx mode change 100644 => 100755 pages/side-panel/src/hooks/useLazyChatSync.ts mode change 100644 => 100755 pages/side-panel/src/hooks/useTranslations.ts mode change 100644 => 100755 pages/side-panel/src/index.css mode change 100644 => 100755 pages/side-panel/src/index.tsx mode change 100644 => 100755 pages/side-panel/tailwind.config.ts mode change 100644 => 100755 pages/side-panel/tsconfig.json mode change 100644 => 100755 pages/side-panel/vite.config.mts mode change 100644 => 100755 platform-core/public/logo.svg mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSans-Bold.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSans-BoldOblique.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSans-Oblique.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSans.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSansDisplay.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSansMono-Bold.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSansMono-BoldOblique.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSansMono-Oblique.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSansMono.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSerif-Bold.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSerif-BoldItalic.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSerif-Italic.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSerif.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/DejaVuSerifDisplay.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/Humor-Sans.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/LICENSE_DEJAVU mode change 100644 => 100755 platform-core/public/pyodide/fonts/LICENSE_STIX mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXGeneral.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXGeneralBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXGeneralBolIta.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXGeneralItalic.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXNonUni.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXNonUniBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXNonUniBolIta.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXNonUniIta.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizFiveSymReg.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizFourSymBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizFourSymReg.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizOneSymBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizOneSymReg.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizThreeSymBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizThreeSymReg.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizTwoSymBol.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/STIXSizTwoSymReg.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmb10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmex10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmmi10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmr10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmss10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmsy10.ttf mode change 100644 => 100755 platform-core/public/pyodide/fonts/cmtt10.ttf mode change 100644 => 100755 platform-core/public/pyodide/package.json mode change 100644 => 100755 platform-core/public/pyodide/pyodide-lock.json mode change 100644 => 100755 platform-core/public/pyodide/pyodide.asm.js mode change 100644 => 100755 platform-core/public/pyodide/pyodide.js mode change 100644 => 100755 platform-core/public/pyodide/pyodide.js.map mode change 100644 => 100755 platform-core/public/pyodide/python_stdlib.zip mode change 100644 => 100755 platform-core/public/wheels/beautifulsoup4-4.12.3-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/certifi-2024.12.14-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/charset_normalizer-3.3.2-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/idna-3.7-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/pyodide_http-0.2.1-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/requests-2.31.0-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/soupsieve-2.5-py3-none-any.whl mode change 100644 => 100755 platform-core/public/wheels/urllib3-2.2.3-py3-none-any.whl mode change 100644 => 100755 platform-core/src/background.ts mode change 100644 => 100755 platform-core/ui/PluginCard.css mode change 100644 => 100755 platform-core/ui/test-harness.js mode change 100644 => 100755 pnpm-lock.yaml mode change 100644 => 100755 pnpm-workspace.yaml mode change 100644 => 100755 public/logo.svg mode change 100644 => 100755 public/plugins/ozon-analyzer/curl-templates/basic-en.curl mode change 100644 => 100755 public/plugins/ozon-analyzer/curl-templates/basic-ru.curl mode change 100644 => 100755 public/plugins/ozon-analyzer/curl-templates/deep-en.curl mode change 100644 => 100755 public/plugins/ozon-analyzer/curl-templates/deep-ru.curl mode change 100644 => 100755 public/plugins/ozon-analyzer/icon.svg mode change 100644 => 100755 public/plugins/ozon-analyzer/manifest.json mode change 100644 => 100755 public/plugins/ozon-analyzer/mcp_server.py mode change 100644 => 100755 public/plugins/ozon-analyzer/workflow.json mode change 100644 => 100755 public/plugins/test-chat-plugin/icon.svg mode change 100644 => 100755 public/plugins/test-chat-plugin/manifest.json mode change 100644 => 100755 public/plugins/test-chat-plugin/mcp_server.py mode change 100644 => 100755 public/pyodide/fonts/DejaVuSans-Bold.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSans-BoldOblique.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSans-Oblique.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSans.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSansDisplay.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSansMono-Bold.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSansMono-BoldOblique.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSansMono-Oblique.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSansMono.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSerif-Bold.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSerif-BoldItalic.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSerif-Italic.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSerif.ttf mode change 100644 => 100755 public/pyodide/fonts/DejaVuSerifDisplay.ttf mode change 100644 => 100755 public/pyodide/fonts/Humor-Sans.ttf mode change 100644 => 100755 public/pyodide/fonts/LICENSE_DEJAVU mode change 100644 => 100755 public/pyodide/fonts/LICENSE_STIX mode change 100644 => 100755 public/pyodide/fonts/STIXGeneral.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXGeneralBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXGeneralBolIta.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXGeneralItalic.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXNonUni.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXNonUniBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXNonUniBolIta.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXNonUniIta.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizFiveSymReg.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizFourSymBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizFourSymReg.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizOneSymBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizOneSymReg.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizThreeSymBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizThreeSymReg.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizTwoSymBol.ttf mode change 100644 => 100755 public/pyodide/fonts/STIXSizTwoSymReg.ttf mode change 100644 => 100755 public/pyodide/fonts/cmb10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmex10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmmi10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmr10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmss10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmsy10.ttf mode change 100644 => 100755 public/pyodide/fonts/cmtt10.ttf mode change 100644 => 100755 public/pyodide/package.json mode change 100644 => 100755 public/pyodide/pyodide-lock.json mode change 100644 => 100755 public/pyodide/pyodide.asm.js mode change 100644 => 100755 public/pyodide/pyodide.js mode change 100644 => 100755 public/pyodide/pyodide.js.map mode change 100644 => 100755 public/pyodide/python_stdlib.zip mode change 100644 => 100755 public/wheels/beautifulsoup4-4.12.3-py3-none-any.whl mode change 100644 => 100755 public/wheels/certifi-2024.12.14-py3-none-any.whl mode change 100644 => 100755 public/wheels/charset_normalizer-3.3.2-py3-none-any.whl mode change 100644 => 100755 public/wheels/idna-3.7-py3-none-any.whl mode change 100644 => 100755 public/wheels/pyodide_http-0.2.1-py3-none-any.whl mode change 100644 => 100755 public/wheels/requests-2.31.0-py3-none-any.whl mode change 100644 => 100755 public/wheels/soupsieve-2.5-py3-none-any.whl mode change 100644 => 100755 public/wheels/urllib3-2.2.3-py3-none-any.whl mode change 100644 => 100755 renovate.json mode change 100644 => 100755 secrets.json mode change 100644 => 100755 src/background/background.ts mode change 100644 => 100755 src/background/offscreen-manager.ts mode change 100644 => 100755 src/background/offscreen.ts mode change 100644 => 100755 src/background/transfer-metadata-manager.ts mode change 100644 => 100755 src/matches/all/index.tsx mode change 100644 => 100755 src/matches/example/index.tsx delete mode 100644 test-scripts/README-prompt-testing.md delete mode 100644 test-scripts/README.md delete mode 100644 test-scripts/advanced-chat-test.js delete mode 100644 test-scripts/chat-api-test.js delete mode 100644 test-scripts/chat-conversion-test.js delete mode 100644 test-scripts/chat-debug-commands.js delete mode 100644 test-scripts/chat-test-page.html delete mode 100644 test-scripts/chat-test.html delete mode 100644 test-scripts/integration-chat-test.js delete mode 100644 test-scripts/ozon-test.js delete mode 100644 test-scripts/test-chat-functionality.js delete mode 100644 test-scripts/test-message-communication.js delete mode 100644 test-scripts/test-prompt-system.js delete mode 100644 test-scripts/test-pyodide-large-data.js delete mode 100644 tests/e2e/config/wdio.browser.conf.ts delete mode 100644 tests/e2e/config/wdio.conf.ts delete mode 100644 tests/e2e/config/wdio.d.ts delete mode 100644 tests/e2e/helpers/theme.ts delete mode 100644 tests/e2e/package.json delete mode 100644 tests/e2e/specs/debug-side-panel.test.ts delete mode 100644 tests/e2e/specs/page-content-runtime.test.ts delete mode 100644 tests/e2e/specs/page-content-ui.test.ts delete mode 100644 tests/e2e/specs/page-content.test.ts delete mode 100644 tests/e2e/specs/page-new-tab.test.ts delete mode 100644 tests/e2e/specs/page-options.test.ts delete mode 100644 tests/e2e/specs/page-popup.test.ts delete mode 100644 tests/e2e/specs/page-side-panel.test.ts delete mode 100644 tests/e2e/specs/smoke.test.ts delete mode 100644 tests/e2e/tsconfig.json delete mode 100644 tests/e2e/utils/extension-path.ts delete mode 100644 tests/html-chunking-mock.test.ts delete mode 100644 tests/ozon-analyzer-integration/async-message-handler.js delete mode 100644 tests/ozon-analyzer-integration/browser-detection.js delete mode 100644 tests/ozon-analyzer-integration/integration-test-report.md delete mode 100644 tests/ozon-analyzer-integration/mocks/environment.js delete mode 100644 tests/ozon-analyzer-integration/node-test-runner.js delete mode 100644 tests/ozon-analyzer-integration/specs/ai-api.test.js delete mode 100644 tests/ozon-analyzer-integration/specs/bridge-communication.test.js delete mode 100644 tests/ozon-analyzer-integration/specs/cross-environment.test.js delete mode 100644 tests/ozon-analyzer-integration/specs/html-extraction.test.js delete mode 100644 tests/ozon-analyzer-integration/specs/performance-benchmark.js delete mode 100644 tests/ozon-analyzer-integration/specs/plugin-loader.test.js delete mode 100644 tests/ozon-analyzer-integration/specs/workflow-engine.test.js delete mode 100644 tests/ozon-analyzer-integration/test-index.html delete mode 100644 tests/ozon-analyzer-integration/test-runner.js delete mode 100644 tests/ozon-analyzer-integration/test-ui.js delete mode 100644 tests/ozon-analyzer-integration/utils/test-framework.js delete mode 100644 tests/run-mock-tests.cjs delete mode 100644 tests/simple-mock-tests.js mode change 100644 => 100755 tools/E2E_TESTING_GUIDE.md mode change 100644 => 100755 tools/dev_helper.sh mode change 100644 => 100755 tools/generate-toc-and-crossrefs.cjs mode change 100644 => 100755 tools/ozon-analyzer-e2e-metrics-collector.js mode change 100644 => 100755 tools/safe-delete.js mode change 100644 => 100755 tools/sync_plans.py mode change 100644 => 100755 tools/test-scenarios.js mode change 100644 => 100755 tsconfig.json mode change 100644 => 100755 turbo.json mode change 100644 => 100755 types/vite-plugin-node-polyfills.d.ts mode change 100644 => 100755 ui/PluginCard.css mode change 100644 => 100755 ui/PluginCard.js mode change 100644 => 100755 ui/log-manager.js mode change 100644 => 100755 ui/test-harness.js mode change 100644 => 100755 vite.config.mts diff --git a/.cursor/README.md b/.cursor/README.md old mode 100644 new mode 100755 diff --git a/.cursor/backup/README.md b/.cursor/backup/README.md deleted file mode 100644 index e6439f58..00000000 --- a/.cursor/backup/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## AI Access Policy - -См. [memory-bank/AI-access-policy.md](../memory-bank/AI-access-policy.md) — политика полного доступа AI-ассистента ко всем файлам проекта и проактивных действий. - ---- - -## Environment Parameters - -См. [rules/environment.yaml](rules/environment.yaml) — Parameters операционной и тестовой среды, доступные AI-ассистенту для автоматизации и диагностики. - ---- \ No newline at end of file diff --git a/.cursor/backup/index.mdc b/.cursor/backup/index.mdc deleted file mode 100644 index f9ca3323..00000000 --- a/.cursor/backup/index.mdc +++ /dev/null @@ -1,27 +0,0 @@ -# Project Rules Index - -В этом проекте используются современные Cursor Project Rules: - -- Все правила хранятся в виде отдельных .mdc-файлов в .cursor/rules/ -- Каждый файл отвечает за отдельный аспект: команды пользователя, best practices, Security, workflow и т.д. - -## Ключевые правила: -- [user-Commands.mdc](rules/user-Commands.mdc) — обработка пользовательских команд -- [development-guIDElines.mdc](rules/development-guIDElines.mdc) — best practices для разработки -- [security-and-deployment.mdc](rules/security-and-deployment.mdc) — security-Standards -- [knowledge-map.mdc](rules/knowledge-map.mdc) — карта структуры знаний -- [memorybank-quality.mdc](rules/memorybank-quality.mdc) — чек-лист качества memory-bank -- [automation.mdc](rules/automation.mdc) — автоматизация синхронизации -- [workflow.mdc](rules/workflow.mdc) — workflow-правила ветвления -- [project-architecture.mdc](rules/project-architecture.mdc) — Architecture и Patterns -- [testing-and-debugging.mdc](rules/testing-and-debugging.mdc) — Testing и Debugging -- [plugin-development.mdc](rules/plugin-development.mdc) — Standards для плагинов -- [ui-standards.mdc](rules/ui-standards.mdc) — UI-Standards -- [architecture-patterns.mdc](rules/architecture-patterns.mdc) — архитектурные Patterns -- [development-principles.mdc](rules/development-principles.mdc) — Principles разработки - -Все правила всегда применяются для максимальной автоматизации и прозрачности. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/AUTOMATION.md b/.cursor/backup/rules/AUTOMATION.md deleted file mode 100644 index 99670585..00000000 --- a/.cursor/backup/rules/AUTOMATION.md +++ /dev/null @@ -1,205 +0,0 @@ -# .cursor Automation System - -Комплексная система автоматизации для аудита, исправления и оптимизации каталога `.cursor` для лучшего понимания AI и Cursor. - -## 🎯 **Purpose** - -Система автоматизации обеспечивает: -- **Автоматический аудит** каталога `.cursor` на предмет проблем -- **Исправление** найденных проблем (дубликаты, сломанные ссылки, метаdata) -- **Оптимизацию** правил специально для AI и Cursor -- **Monitoring** состояния и качества правил - -## 🛠️ **Компоненты системы** - -### **1. CursorAuditor** (`audit-cursor..js`) -- Сканирование всех файлов в `.cursor/rules` -- Verification метаданных и их валидности -- Поиск дублирующего контента -- Verification сломанных ссылок -- Validation структуры каталога -- Генерация подробных отчетов - -### **2. CursorFixer** (`fix-cursor..js`) -- Автоматическое исправление метаданных -- Удаление дублирующих файлов -- Исправление сломанных ссылок -- Обновление индексов - -### **3. AIOptimizer** (`optimize-for-ai..js`) -- Optimization метаданных для AI -- Добавление AI-специфичных тегов -- Purpose Priorityов и категорий -- Создание AI-оптимизированных индексов - -### **4. CursorManager** (`cursor-manager..js`) -- Главный Interface для всех операций -- Управление workflow -- Генерация отчетов о Statusе -- CLI Interface - -## 🚀 **Usage** - -### **Базовые команды:** - -```bash -# Показать справку -node cursor-manager..js help - -# Check Status -node cursor-manager..js status - -# Запустить аудит -node cursor-manager..js audit - -# Применить исправления -node cursor-manager..js fix - -# Оптимизировать для AI -node cursor-manager..js optimize - -# Полный workflow -node cursor-manager..js full -``` - -### **Опции:** - -```bash -#.json вывод для аудита -node cursor-manager..js audit -.json - -# Пропустить аудит перед исправлениями -node cursor-manager..js fix --no-audit-first - -# Пропустить аудит после оптимизации -node cursor-manager..js optimize --no-audit-after -``` - -## 📊 **Что проверяет аудит** - -### **Файлы и структура:** -- Общее Quantity файлов -- Соотношение `.mdc` и `.md` файлов -- Наличие обязательных директорий -- Наличие обязательных файлов - -### **Метаdata:** -- Наличие.yaml frontmatter -- Валидность полей `description`, `globs`, `alwaysApply` -- AI-специфичные поля `aiPriority`, `aiCategory` - -### **Контент:** -- Дублирующий контент между файлами -- Сломанные ссылки в документации -- Консистентность структуры - -### **AI-Optimization:** -- Priorityы правил (critical, high, medium, normal) -- Категории (system-design, development-practices, etc.) -- AI-специфичные теги и Comme.ts - -## 🤖 **AI-специфичные оптимизации** - -### **Priorityы:** -- **critical**: Применяется ко всему коду и решениям -- **high**: Применяется к большинству задач -- **medium**: Применяется когда релевантно -- **normal**: Применяется когда уместно - -### **Категории:** -- **system-design**: Architecture и структура системы -- **development-practices**: Standards кодирования -- **documentation**: Documentation и Communication -- **plugin-development**: Правила для плагинов -- **security**: Security -- **user-interface**: UI/UX Standards -- **process-management**: Workflow и Processes -- **ai-optimization**: AI-специфичные оптимизации - -## 📈 **Отчеты и метрики** - -### **Статистика:** -- Общее Quantity файлов -- Файлы с/без метаданных -- Quantity проблем -- Процент AI-готовности - -### **Проблемы:** -- Дублирующий контент -- Сломанные ссылки -- Невалидные метаdata -- Отсутствующие файлы/директории - -### **Recommendations:** -- Конвертация `.md` в `.mdc` -- Добавление метаданных -- Удаление дубликатов -- Исправление ссылок - -## 🔄 **Workflow** - -### **Полный workflow (`full`):** -1. **Аудит** - поиск проблем -2. **Исправления** - автоматическое решение -3. **Optimization** - AI-специфичные улучшения -4. **Финальный аудит** - Verification результатов -5. **Отчет** - сравнение до/после - -### **Инкрементальный workflow:** -- `audit` → анализ проблем -- `fix` → исправление -- `optimize` → Optimization -- `status` → Monitoring - -## 📝 **Integration с AI** - -### **Автоматические команды:** -Система интегрирована с AI memory-bank через команды: -- `аудит cursor` / `cursor audit` -- `исправь cursor` / `cursor fix` -- `оптимизируй cursor` / `cursor optimize` -- `полный cursor` / `cursor full` -- `Status cursor` / `cursor status` - -### **AI-оптимизированные файлы:** -- `ai-optimization.mdc` - инструкции для AI -- `ai-index.mdc` - индекс по Priorityам -- AI-теги в каждом файле -- Priorityы и категории в метаданных - -## 🎯 **Результаты** - -После полной оптимизации: -- ✅ Все файлы имеют правильные метаdata -- ✅ Нет дублирующего контента -- ✅ Все ссылки работают -- ✅ AI-специфичные оптимизации применены -- ✅ Priorityы и категории назначены -- ✅ Автоматическое применение критических правил - -## 🔧 **Расширение системы** - -### **Добавление новых проверок:** -1. Create Method в `CursorAuditor` -2. Add в основной workflow -3. Update отчеты - -### **Добавление новых исправлений:** -1. Create Method в `CursorFixer` -2. Интегрировать в процесс исправления -3. Add в отчеты - -### **Добавление новых оптимизаций:** -1. Create Method в `AIOptimizer` -2. Add AI-специфичную логику -3. Update валидацию - -## 📚 **Связанные файлы** - -- `audit-cursor..js` - Аудит системы -- `fix-cursor..js` - Исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - Главный Interface -- `ai-optimization.mdc` - AI инструкции -- `ai-index.mdc` - AI индекс -- `ai-memory.mdc` - Команды для AI \ No newline at end of file diff --git a/.cursor/backup/rules/EXPERIENCE-TRANSFER.md b/.cursor/backup/rules/EXPERIENCE-TRANSFER.md deleted file mode 100644 index 1b738237..00000000 --- a/.cursor/backup/rules/EXPERIENCE-TRANSFER.md +++ /dev/null @@ -1,279 +0,0 @@ -# Experience Transfer Best Practices - -Руководство по переносу опыта работы с Cursor и AI между проектами через систему `.cursor`. - -## 🎯 **Почему `.cursor` лучше `docs/for-ai-best-practices`** - -### **Автоматическое применение** -- ✅ Cursor автоматически читает все файлы из `.cursor/rules/` -- ✅ Правила применяются без дополнительной Settings -- ✅ AI получает контекст сразу при создании чата - -### **Стандартизация** -- ✅ `.cursor` - стандартное место для правил Cursor -- ✅ Все разработчики знают, где искать правила -- ✅ Единообразие между проектами - -### **Структурированность** -- ✅ Четкая иерархия (architecture, dev, doc, plugin, etc.) -- ✅ Метаdata с Priorityами и Categoryми -- ✅ Автоматическая Validation и Optimization - -## 🚀 **Стратегия переноса опыта** - -### **1. Экспорт из исходного проекта** - -```bash -# Из папки .cursor/rules исходного проекта -node cursor-manager..js export - -# С указанием целевого проекта -node cursor-manager..js export my-new-project -``` - -**Создается папка `cursor-export/` с:** -- Все правила по Categoryм -- Скрипты автоматизации -- Инструкции по импорту -- Автоматический скрипт импорта - -### **2. Импорт в целевой проект** - -```bash -# Скопировать cursor-export в целевой проект -cp -r cursor-export /path/to/new-project/ - -# Запустить импорт -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### **3. Кастомизация для нового проекта** - -```bash -# Перейти в .cursor/rules -cd .cursor/rules - -# Запустить полную оптимизацию -node cursor-manager..js full - -# Check Status -node cursor-manager..js status -``` - -## 📋 **Что переносится** - -### **Core Rules (обязательные)** -- `ai-memory.mdc` - команды для AI (кастомизировать!) -- `environment.mdc` - Constrai.ts окружения (кастомизировать!) -- `index.mdc` - индекс правил -- `README.mdc` - Documentation структуры - -### **Categories (по необходимости)** -- **architecture/** - архитектурные правила -- **dev/** - Principles разработки -- **doc/** - Standards документации -- **plugin/** - правила для плагинов -- **security/** - правила безопасности -- **ui/** - UI/UX Standards -- **workflow/** - Processes разработки - -### **Automation (Required)** -- `audit-cursor..js` - аудит системы -- `fix-cursor..js` - автоматические исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - главный Interface - -## 🔧 **Кастомизация для нового проекта** - -### **1. Update environment.mdc** -``.mdc ---- -description: Critical environment limitations -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: environment ---- - -# Environment Constrai.ts - -## Node.js Version -- Required: Node.js 18+ (Update для вашего проекта) - -## Browser Support -- Chrome: 120+ (Update для вашего проекта) -- Firefox: 115+ (Update для вашего проекта) - -## build Tools -- Vite: 5+ (Update для вашего проекта) -- TypeScript: 5+ (Update для вашего проекта) -``` - -### **2. Update ai-memory.mdc** -``.mdc ---- -description: User Commands and AI instructions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Memory Bank - -## Project-Specific Commands -- `build project` - Собрать проект (Update команды) -- `test project` - Запустить тесты (Update команды) -- `deploy project` - Деплой проекта (Update команды) - -## Project Context -- This is a [Type проекта] (Update Description) -- Main technology stack: [стек] (Update стек) -- Key features: [особенности] (Update особенности) -``` - -### **3. Update monorepo-best-practices.mdc** -``.mdc ---- -description: Monorepo structure and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Monorepo Best Practices - -## Project Structure -``` -project/ -├── packages/ # (Update структуру) -├── apps/ # (Update структуру) -├── tools/ # (Update структуру) -└── docs/ # (Update структуру) -``` - -## Package Management -- Package manager: pnpm (Update если нужно) -- Workspace configuration: pnpm-workspace.yaml (Update) -``` - -## 📊 **Workflow переноса** - -### **Полный цикл:** -1. **Экспорт** из исходного проекта -2. **Копирование** в целевой проект -3. **Импорт** с автоматической настройкой -4. **Кастомизация** под новый проект -5. **Validation** и Optimization -6. **Testing** работы AI - -### **Команды для AI:** -```bash -# Экспорт -экспорт cursor -экспорт cursor [проект] - -# Импорт (в целевом проекте) -импорт cursor - -# Verification -Status cursor -полный cursor -``` - -## 🎯 **Best Practices** - -### **1. Инкрементальный перенос** -- Начните с core rules -- Добавляйте категории по мере необходимости -- Тестируйте каждую категорию - -### **2. Кастомизация обязательна** -- Всегда обновляйте `environment.mdc` -- Адаптируйте `ai-memory.mdc` под проект -- Проверяйте актуальность правил - -### **3. Validation после импорта** -```bash -cd .cursor/rules -node cursor-manager..js status -node cursor-manager..js audit -node cursor-manager..js optimize -``` - -### **4. Documentation изменений** -- Ведите changelog изменений -- Комментируйте кастомизации -- Обновляйте README проекта - -### **5. Обратная Compatibility** -- Сохраняйте структуру категорий -- Не удаляйте обязательные файлы -- Тестируйте на разных проектах - -## 🔄 **Автоматизация** - -### **Скрипт быстрого переноса:** -```bash -#!/bin/bash -# quick-transfer.sh - -SOURCE_PROJECT=$1 -TARGET_PROJECT=$2 - -echo "🚀 Quick transfer from $SOURCE_PROJECT to $TARGET_PROJECT" - -# Экспорт из исходного проекта -cd $SOURCE_PROJECT/.cursor/rules -node cursor-manager..js export $TARGET_PROJECT - -# Копирование в целевой проект -cp -r cursor-export $TARGET_PROJECT/ - -# Импорт в целевой проект -cd $TARGET_PROJECT -node cursor-export/import-cursor..js - -# Очистка -rm -rf cursor-export - -echo "✅ Transfer completed!" -``` - -### **Git hooks для автоматизации:** -```bash -# .git/hooks/post-merge -#!/bin/bash -if [ -d ".cursor/rules" ]; then - cd .cursor/rules - node cursor-manager..js status -fi -``` - -## 📈 **Метрики успеха** - -### **После переноса проверьте:** -- ✅ Все файлы имеют правильные метаdata -- ✅ AI команды работают в новом проекте -- ✅ Автоматизация функционирует -- ✅ Нет конфликтов с существующими правилами -- ✅ Performance AI не снизилась - -### **Долгосрочные метрики:** -- Скорость onboarding новых разработчиков -- Quality AI-ассистированной разработки -- Консистентность кода между проектами -- Time на настройку новых проектов - -## 🎉 **Результат** - -После правильного переноса: -- **Быстрый старт** новых проектов -- **Консистентность** между проектами -- **Автоматизация** рутинных задач -- **Улучшенное** AI-ассистирование -- **Стандартизация** процессов разработки - -**Система `.cursor` обеспечивает эффективный перенос опыта между проектами с минимальными усилиями!** 🚀 \ No newline at end of file diff --git a/.cursor/backup/rules/README.mdc b/.cursor/backup/rules/README.mdc deleted file mode 100644 index 60abfdb3..00000000 --- a/.cursor/backup/rules/README.mdc +++ /dev/null @@ -1,117 +0,0 @@ - -# .cursor/rules: Project Rules & AI Onboarding - -This directory contains all rules and standards for automation and AI assista.ts in the Agent Plugins Platform. - -## Principles -- Each rule is a separate .mdc file (one rule — one file) -- Grouped by topic: development principles, architecture, plugin standards, UI/UX, workflow, documentation, etc. -- Each file contains: a short description, globs, the rule .tself, and (optionally) examples or related links -- All rules are in English for international compatibility -- README and index.mdc help with navigation and search - -## CLI Scri.ts - -- **create-rule.js** — Interactive CLI to create a new .mdc rule file from a template. Prom.ts for all required fields and updates index/README automatically. - - Usage: `node .cursor/rules/create-rule.js` -- **generate-rules-index.js** — Scans all .mdc files and regenerates `index.mdc` and the structure section in `README.md`. - - Usage: `node .cursor/rules/generate-rules-index.js` -- **check-rules-structure.js** — Validates all .mdc files for required sections, uniqueness, and valid related links. Used in CI. - - Usage: `node .cursor/rules/check-rules-structure.js` - -## Current Structure -``` -.cursor/ - rules/ - # Root level - critical rules - ai-memory.mdc # User Commands and AI instructions - environment.mdc # Critical environment limitations - monorepo-best-practices.mdc # Monorepo structure and guIDElines - TypeScript-build-troubleshooting.mdc # TypeScript build error solutions - README.mdc # Main documentation - index.mdc # Rules index and navigation - - architecture/ # System architecture rules - architecture-chat-system.mdc - architecture-error-handling.mdc - architecture-observability.mdc - architecture-performance.mdc - architecture-plugin.mdc - architecture-project-structure.mdc - architecture-security.mdc - architecture-workflow.mdc - project-architecture.mdc - - dev/ # Development principles and guIDElines - dev-principle-01-do-no-harm.mdc - dev-principle-03-best-practices.mdc - dev-principle-04-fail-fast-safe.mdc - dev-principle-05-observability.mdc - dev-principle-06-config-as-code.mdc - dev-principle-07-progressive-enhancement.mdc - dev-principle-08-data-integrity-privacy.mdc - dev-principle-09-continuous-learning.mdc - dev-principle-10-ecosystem-thinking.mdc - development-guIDElines.mdc - security-and-deployment.mdc - testing-and-debugging.mdc - TypeScript.mdc - git-workflow.mdc - - doc/ # Documentation and AI standards - ai-first.mdc # AI-oriented documentation standards - knowledge-map.mdc # Project knowledge structure - memorybank-quality.mdc # Memory-bank quality standards - restore-context.mdc # Context restoration procedures - .mdc-file-standards.mdc # .mdc file creation standards - ai-fallback.mdc # AI fallback procedures - - plugin/ # Plugin development standards - plugin-best-practices.mdc - plugin-documentation.mdc - plugin-error-handling.mdc - plugin-performance.mdc - plugin-security.mdc - plugin-structure.mdc - plugin-testing.mdc - - security/ # Security standards - validation.mdc # Input validation rules - - ui/ # UI/UX standards - ui-accessibility.mdc - ui-animation.mdc - ui-error-handling.mdc - ui-forms.mdc - ui-loading-States.mdc - ui-mobile.mdc - ui-navigation.mdc - ui-performance.mdc - ui-React-compone.ts.mdc - ui-styling.mdc - ui-testing.mdc - - workflow/ # Development workflow - automation.mdc - branches.mdc - workflow.mdc -``` - -## AI Fallback Rule - -If an AI agent cannot answer a question from .ts own memory-bank, it must first check the .rules directory, then the memory-bank directory. See [doc/ai-fallback.mdc](./doc/ai-fallback.mdc). - -## Main GitHub Repository - -HTTPS://github.com/LebedevIV/agent-plugins-platform-boilerplate - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name ->>>>>>> origin/develop - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name \ No newline at end of file diff --git a/.cursor/backup/rules/ai-index.mdc b/.cursor/backup/rules/ai-index.mdc deleted file mode 100644 index 823bcc83..00000000 --- a/.cursor/backup/rules/ai-index.mdc +++ /dev/null @@ -1,89 +0,0 @@ ---- -description: AI-optimized index of rules by priority and category -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI-Optimized Rules Index - -## Critical Priority Rules -*Must be applied to all code and decisions* - -- [Ai Index](ai-index.mdc) — AI-optimized index of rules by priority and category -- [Ai Memory](ai-memory.mdc) — User Commands and AI instructions -- [Ai Optimization](ai-optimization.mdc) — AI-specific instructions and optimizations for .cursor rules -- [Ai Memory](cursor-export/ai-memory.mdc) — User Commands and AI instructions -- [Environment](cursor-export/environment.mdc) — Critical environment constrai.ts - Node.js version, popup vs sIDEpanel - -## High Priority Rules -*Should be applied to most code and decisions* - -- [Architecture Chat System](architecture/architecture-chat-system.mdc) — Architecture rule for chat-system -- [Architecture Error Handling](architecture/architecture-error-handling.mdc) — Architecture rule for error-handling -- [Architecture Observability](architecture/architecture-observability.mdc) — Architecture rule for observability -- [Architecture Performance](architecture/architecture-performance.mdc) — Architecture rule for performance -- [Architecture Plugin](architecture/architecture-plugin.mdc) — Architecture rule for plugin -- [Architecture Project Structure](architecture/architecture-project-structure.mdc) — Architecture rule for project-structure -- [Architecture Security](architecture/architecture-security.mdc) — Architecture rule for security -- [Architecture Workflow](architecture/architecture-workflow.mdc) — Architecture rule for workflow -- [Project Architecture](architecture/project-architecture.mdc) — Architecture rule for project-architecture -- [Dev Principle 01 Do No Harm](cursor-export/dev/dev-principle-01-do-no-harm.mdc) — Development principle harm - dev principle 01 do no harm -- [Dev Principle 03 Best Practices](cursor-export/dev/dev-principle-03-best-practices.mdc) — Development principle practices - dev principle 03 best practices -- [Dev Principle 04 Fail Fast Safe](cursor-export/dev/dev-principle-04-fail-fast-safe.mdc) — Development principle safe - dev principle 04 fail fast safe -- [Dev Principle 05 Observability](cursor-export/dev/dev-principle-05-observability.mdc) — Development principle observability - dev principle 05 observability -- [Dev Principle 06 config As Code](cursor-export/dev/dev-principle-06-config-as-code.mdc) — Development principle code - dev principle 06 config as code -- [Dev Principle 07 Progressive Enhancement](cursor-export/dev/dev-principle-07-progressive-enhancement.mdc) — Development principle enhancement - dev principle 07 progressive enhancement -- [Dev Principle 08 data Integrity Privacy](cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc) — Development principle privacy - dev principle 08 data integrity privacy -- [Dev Principle 09 Continuous Learning](cursor-export/dev/dev-principle-09-continuous-learning.mdc) — Development principle learning - dev principle 09 continuous learning -- [Dev Principle 10 Ecosystem Thinking](cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc) — Development principle thinking - dev principle 10 ecosystem thinking - -## Medium Priority Rules -*Apply when relevant to the task* - -- [Ai Fallback](cursor-export/doc/ai-fallback.mdc) — Fallback procedures for AI -- [Ai First](cursor-export/doc/ai-first.mdc) — AI-oriented documentation standards -- .mdc File Standards](cursor-export/doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [Workflow](cursor-export/workflow/workflow.mdc) — Workflow rule for -- [Ai Answer Self Check](doc/ai-answer-self-check.mdc) — ai answer self check rule -- [Ci Automation](workflow/ci-automation.mdc) — Automation and synchronization rules -- [Experience Transfer](workflow/experience-transfer.mdc) — experience transfer rule - -## Normal Priority Rules -*Apply when appropriate* - -- [README](README.mdc) — Main project rules documentation and AI onboarding - structure, CLI scri.ts, usage -- [Development GuIDElines](cursor-export/dev/development-guIDElines.mdc) — General development guIDElines -- [Git Workflow](cursor-export/dev/git-workflow.mdc) — Git workflow rule - only develop to main, mandatory PR for all changes -- [Security And Deployment](cursor-export/dev/security-and-deployment.mdc) — Security rule for -and-deployment -- [Testing And Debugging](cursor-export/dev/testing-and-debugging.mdc) — Testing and debugging standards -- [TypeScript](cursor-export/dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Knowledge Map](cursor-export/doc/knowledge-map.mdc) — Project knowledge structure -- [Memorybank Quality](cursor-export/doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](cursor-export/doc/restore-context.mdc) — Context restoration procedures -- [Index](cursor-export/index.mdc) — index rule -- [Monorepo Best Practices](cursor-export/monorepo-best-practices.mdc) — Best practices for monorepo work - structure, dependencies, TypeScript, barrel expo.ts -- [Plugin Best Practices](cursor-export/plugin/plugin-best-practices.mdc) — Plugin standard for best-practices -- [Plugin Documentation](cursor-export/plugin/plugin-documentation.mdc) — Plugin standard for documentation -- [Plugin Error Handling](cursor-export/plugin/plugin-error-handling.mdc) — Plugin standard for error-handling -- [Plugin Performance](cursor-export/plugin/plugin-performance.mdc) — Plugin standard for performance -- [Plugin Security](cursor-export/plugin/plugin-security.mdc) — Plugin standard for security -- [Plugin Structure](cursor-export/plugin/plugin-structure.mdc) — Plugin standard for structure -- [Plugin Testing](cursor-export/plugin/plugin-testing.mdc) — Plugin standard for testing -- [Validation](cursor-export/security/validation.mdc) — Input validation and data sanitization rules -- [TypeScript build Troubleshooting](cursor-export/TypeScript-build-troubleshooting.mdc) — TypeScript build error troubleshooting guIDE - types, barrel expo.ts, dependencies, Tailwin.css -- [Ui Accessibility](cursor-export/ui/ui-accessibility.mdc) — UI standard for accessibility -- [Ui Animation](cursor-export/ui/ui-animation.mdc) — UI standard for animation -- [Ui Error Handling](cursor-export/ui/ui-error-handling.mdc) — UI standard for error-handling -- [Ui Forms](cursor-export/ui/ui-forms.mdc) — UI standard for forms -- [Ui Loading States](cursor-export/ui/ui-loading-States.mdc) — UI standard for loading-States -- [Ui Mobile](cursor-export/ui/ui-mobile.mdc) — UI standard for mobile -- [Ui Navigation](cursor-export/ui/ui-navigation.mdc) — UI standard for navigation -- [Ui Performance](cursor-export/ui/ui-performance.mdc) — UI standard for performance -- [Ui React Compone.ts](cursor-export/ui/ui-React-compone.ts.mdc) — UI standard for React-compone.ts -- [Ui Styling](cursor-export/ui/ui-styling.mdc) — UI standard for styling -- [Ui Testing](cursor-export/ui/ui-testing.mdc) — UI standard for testing -- [Automation](cursor-export/workflow/automation.mdc) — Automation and synchronization rules -- [Branches](cursor-export/workflow/branches.mdc) — Git branch management rules -- [Testing Troubleshooting](dev/testing-troubleshooting.mdc) — Testing and debugging standards diff --git a/.cursor/backup/rules/ai-memory.mdc b/.cursor/backup/rules/ai-memory.mdc deleted file mode 100644 index c74c3224..00000000 --- a/.cursor/backup/rules/ai-memory.mdc +++ /dev/null @@ -1,38 +0,0 @@ -# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/backup/rules/ai-optimization.mdc b/.cursor/backup/rules/ai-optimization.mdc deleted file mode 100644 index e7b985b1..00000000 --- a/.cursor/backup/rules/ai-optimization.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: AI-specific instructions and optimizations for .cursor rules -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Optimization Instructions - -## How AI Should Use These Rules - -### Priority Levels -- **critical**: Must be applied to all code and decisions -- **high**: Should be applied to most code and decisions -- **medium**: Apply when relevant to the task -- **normal**: Apply when appropriate - -### Categories -- **system-design**: Architecture and system structure -- **development-practices**: Coding standards and principles -- **documentation**: Documentation and communication -- **plugin-development**: Plugin-specific rules -- **security**: Security and safety rules -- **user-interface**: UI/UX standards -- **process-management**: Workflow and process rules -- **ai-optimization**: AI-specific optimizations - -### Usage Patterns -1. Always check critical rules first -2. Apply high-priority rules to most tasks -3. Use category-specific rules for targeted tasks -4. Reference related rules when making decisions - -### AI Memory Integration -- These rules are automatically loaded into AI memory-bank -- Use `alwaysApply: true` rules without explicit reference -- Reference specific rules when explaining decisions -- Update rules when patterns change or improve - -## Optimization Status -- ✅ All rules have proper metadata -- ✅ AI-specific tags added -- ✅ Priority levels assigned -- ✅ Categories defined -- ✅ Structure optimized for AI understanding diff --git a/.cursor/backup/rules/architecture/architecture-chat-system.mdc b/.cursor/backup/rules/architecture/architecture-chat-system.mdc deleted file mode 100644 index 3eb5b78c..00000000 --- a/.cursor/backup/rules/architecture/architecture-chat-system.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Chat System Architecture -- Per-page, per-plugin isolation: Ch.ts separated by page URL and plugin -- LRU cache: In-memory cache for performance (50 ch.ts) -- IndexedDB: Persistent storage for all ch.ts -- Real-time sync: Cross-tab communication for chat updates -- Cleanup: Automatic removal of ch.ts older than 90 days - -description: Chat system architecture and data flow -globs: - - platform-core/* - - chrome-extension/src/background/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-error-handling.mdc b/.cursor/backup/rules/architecture/architecture-error-handling.mdc deleted file mode 100644 index 7aef574e..00000000 --- a/.cursor/backup/rules/architecture/architecture-error-handling.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Error Handling (Architecture) -- Graceful Degradation: System continues working with reduced functionality -- User Feedback: Clear error messages and recovery options -- Logging: Structured logging for debugging and monitoring -- Fallbacks: Alternative implementations for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-error-handling.mdc - - architecture-security.mdc - -description: Error handling requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-observability.mdc b/.cursor/backup/rules/architecture/architecture-observability.mdc deleted file mode 100644 index 1f47a11f..00000000 --- a/.cursor/backup/rules/architecture/architecture-observability.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Monitoring and Observability (Architecture) -- Structured Logging: Consistent log format across compone.ts -- Performance Metrics: Track execution time and memory usage -- Error Tracking: Monitor and alert on critical errors -- User Analytics: Track feature usage and performance -- Health Checks: Monitor system health and dependencies - -related: - - architecture-performance.mdc - - dev-principle-05-observability.mdc - -description: Monitoring and observability requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-performance.mdc b/.cursor/backup/rules/architecture/architecture-performance.mdc deleted file mode 100644 index 636e5dc1..00000000 --- a/.cursor/backup/rules/architecture/architecture-performance.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Performance GuIDElines (Architecture) -- PyodIDE Loading: Lazy load runtime, cache resu.ts -- Memory Management: Clean up resources, monitor usage -- Bundle Size: Optimize for extension size lim.ts -- Caching: Use LRU cache for frequently accessed data -- Async Operations: Non-blocking UI, proper loading States - -related: - - plugin-performance.mdc - - architecture-observability.mdc - -description: Performance guIDElines for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-plugin.mdc b/.cursor/backup/rules/architecture/architecture-plugin.mdc deleted file mode 100644 index 3ef2571d..00000000 --- a/.cursor/backup/rules/architecture/architecture-plugin.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Plugin Architecture -- Manifest Structure: manifest.json + mcp_server.py +.icon.svg -- MCP Protocol: Standard MCP server implementation in Python -- Permission Model: Zero Trust - plugins not trusted by default -- Isolation: Each plugin in separate worker for security -- Communication: chrome.runtime.sendMessage for all cross-component communication - -description: Plugin architecture and isolation requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-project-structure.mdc b/.cursor/backup/rules/architecture/architecture-project-structure.mdc deleted file mode 100644 index 6220eeac..00000000 --- a/.cursor/backup/rules/architecture/architecture-project-structure.mdc +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Structure Architecture -- platform-core/: Main logic and services (keep boilerplate clean) -- chrome-extension/src/background/: Core background services -- pages/*/src/: UI compone.ts for different extension pages -- memory-bank/: Project documentation and context -- public/plugins/: Plugin implementations - -description: Directory and component structure for the project -globs: - - platform-core/* - - chrome-extension/src/background/* - - pages/*/src/* - - memory-bank/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-security.mdc b/.cursor/backup/rules/architecture/architecture-security.mdc deleted file mode 100644 index 9e8d63a1..00000000 --- a/.cursor/backup/rules/architecture/architecture-security.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Security Architecture -- Secret Manager: Encrypted storage of API keys in chrome.storage.local -- Network Guard: Whitelist domains for API calls -- Audit System: Log all plugin activities and network reque.ts -- Parameter Validation:.json Schema validation for all inp.ts -- Rate Limiting: Prevent abuse and suspicious activity - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Security architecture and controls -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-workflow.mdc b/.cursor/backup/rules/architecture/architecture-workflow.mdc deleted file mode 100644 index 401f5e21..00000000 --- a/.cursor/backup/rules/architecture/architecture-workflow.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Development Workflow (Architecture) -- Feature Branches: Always create from develop branch -- Testing: Test before commit, use --no-verify if ESLint issues -- Documentation: Update memory-bank for all architectural decisions -- Comm.ts: Use semantic commit messages -- Code Quality: TypeScript for new files, ESLint compliance - -description: Development workflow and process standards -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* - - memory-bank/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/file-relationships.mdc b/.cursor/backup/rules/architecture/file-relationships.mdc deleted file mode 100644 index 2376e599..00000000 --- a/.cursor/backup/rules/architecture/file-relationships.mdc +++ /dev/null @@ -1,339 +0,0 @@ ---- -description: File organization patterns and relationships - universal project structure guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Карта взаимосвязей файлов - -## File Structure и их Purpose - -### 1. Background Layer (Слой фоновых сервисов) - -#### `chrome-extension/src/background/plugin-chat-cache.ts` -**Purpose:** Центральный сервис управления чатами -**Dependencies:** -- `plugin-chat-API.ts` - для работы с IndexedDB -- Chrome Extensions API (chrome.runtime, chrome.tabs) - -**Экспортирует:** -- `PluginChatcache` class -- `Chatdata`, `ChatMessage` interfaces - -**Используется:** -- `background/index.ts` - создание экземпляра и регистрация обработчиков - -**Ключевые Methodы:** -```TypeScript -class PluginChatcache { - getChat(pluginId: string, pageKey: string): Promise - saveMessage(pluginId: string, pageKey: string, message: ChatMessage): Promise - deleteChat(pluginId: string, pageKey: string): Promise - getAllCh.ts(): Promise - cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/plugin-chat-API.ts` -**Purpose:** Abstraction над IndexedDB -**Dependencies:** -- IndexedDB API -- Chrome Extensions API - -**Экспортирует:** -- `Ch.tstorageAPI` class -- database utility functions - -**Используется:** -- `plugin-chat-cache.ts` - для всех операций с Storageм - -**Ключевые Methodы:** -```TypeScript -class Ch.tstorageAPI { - async getChat(pluginId: string, pageKey: string): Promise - async saveChat(chatdata: Chatdata): Promise - async deleteChat(pluginId: string, pageKey: string): Promise - async getAllCh.ts(): Promise - async cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/index.ts` -**Purpose:** Точка входа background script -**Dependencies:** -- `plugin-chat-cache.ts` - создание экземпляра cacheа -- Chrome Extensions API - -**Используется:** -- Chrome Extensions runtime (автоматически) - -**Основные функции:** -- Инициализация `PluginChatcache` -- Регистрация обработчиков сообщений -- Управление жизненным циклом - -**Обработчики сообщений:** -```TypeScript -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - switch (request.type) { - case 'GET_PLUGIN_CHAT': - case 'SAVE_PLUGIN_CHAT_MESSAGE': - case 'DELETE_PLUGIN_CHAT': - case 'GET_ALL_PLUGIN_CH.ts': - } -}); -``` - -### 2. UI Layer (Слой пользовательского Interfaceа) - -#### `pages/sIDE-panel/src/compone.ts/PluginControlPanel.tsx` -**Purpose:** Основной компонент управления плагином -**Dependencies:** -- React hooks (useState, useEffect, useRef) -- Chrome Extensions API (chrome.runtime) -- `PluginDetails.tsx` - для отображения деталей плагина - -**Используется:** -- `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` - основной контейнер - -**Ключевые состояния:** -```TypeScript -const [messages, setMessages] = useState([]); -const [loading, setLoading] = useState(false); -const [syncStatus, s.tsyncStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); -``` - -**Основные функции:** -- Загрузка чата при монтировании -- Сохранение сообщений -- Synchronization с другими вкладками -- Экспорт и очистка чатов - -#### `pages/sIDE-panel/src/compone.ts/PluginDetails.tsx` -**Purpose:** Отображение деталей плагина -**Dependencies:** -- React -- Plugin interface - -**Используется:** -- `PluginControlPanel.tsx` - как дочерний компонент - -#### `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` -**Purpose:** Основной контейнер sIDE panel -**Dependencies:** -- `PluginControlPanel.tsx` -- `PluginCard.tsx` -- React - -**Используется:** -- Chrome Extensions sIDE panel API - -### 3. DevTools Layer (Слой инструментов разработчика) - -#### `pages/devtools-panel/src/compone.ts/PluginCh.tsTab.tsx` -**Purpose:** Административный Interface для управления чатами -**Dependencies:** -- React hooks -- Chrome Extensions API -- `file-saver` library - -**Используется:** -- `pages/devtools-panel/src/index.tsx` - как вкладка в DevTools - -**Основные функции:** -- Отображение всех чатов -- Удаление чатов -- Экспорт в.json -- Фильтрация и поиск - -#### `pages/devtools-panel/src/index.tsx` -**Purpose:** Точка входа DevTools panel -**Dependencies:** -- `PluginCh.tsTab.tsx` -- React - -**Используется:** -- Chrome DevTools API - -### 4. Shared Layer (Общий слой) - -#### `chrome-extension/src/background/types.ts` (предлагаемый) -**Purpose:** Общие Typeы для всей системы -**Dependencies:** -- TypeScript - -**Экспортирует:** -```TypeScript -export interface ChatMessage { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: number; -} - -export interface Chatdata { - pluginId: string; - pageKey: string; - messages: ChatMessage[]; - lastUpdated: number; - createdAt: number; -} - -export type ChatRequest = - | { type: 'GET_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'SAVE_PLUGIN_CHAT_MESSAGE'; pluginId: string; pageKey: string; message: ChatMessage } - | { type: 'DELETE_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'GET_ALL_PLUGIN_CH.ts' }; - -export type ChatEvent = - | { type: 'PLUGIN_CHAT_UPDATED'; pluginId: string; pageKey: string; messages?: ChatMessage[] }; -``` - -**Используется:** -- Все компоненты системы чатов - -## Threads данных между файлами - -### 1. Загрузка чата -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getChat) -plugin-chat-cache.ts - ↓ (storageAPI.getChat) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -### 2. Сохранение сообщения -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.saveMessage) -plugin-chat-cache.ts - ↓ (storageAPI.saveChat + event broadcast) -plugin-chat-API.ts + chrome.tabs.query - ↓ (IndexedDB + other tabs) -Browser Storage + Other UI Compone.ts -``` - -### 3. Административные операции -``` -PluginCh.tsTab.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getAllCh.ts/deleteChat) -plugin-chat-cache.ts - ↓ (storageAPI) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -## Dependencies и импорты - -### Background Layer -```TypeScript -// background/index.ts -import { PluginChatcache } from './plugin-chat-cache'; -import type { ChatRequest, ChatEvent } from './types'; - -// plugin-chat-cache.ts -import { Ch.tstorageAPI } from './plugin-chat-API'; -import type { Chatdata, ChatMessage } from './types'; - -// plugin-chat-API.ts -// Нет внешних зависимостей, только IndexedDB API -``` - -### UI Layer -```TypeScript -// PluginControlPanel.tsx -import { PluginDetails } from './PluginDetails'; -import type { ChatMessage } from '../../../background/types'; - -// PluginDetails.tsx -import type { Plugin } from './PluginCard'; - -// SIDEPanel.tsx -import { PluginControlPanel } from './PluginControlPanel'; -import { PluginCard } from './PluginCard'; -``` - -### DevTools Layer -```TypeScript -// PluginCh.tsTab.tsx -import { saveAs } from 'file-saver'; -import type { Chatdata } from '../../../background/types'; - -// devtools-panel/index.tsx -import { PluginCh.tsTab } from './compone.ts/PluginCh.tsTab'; -``` - -## configuration Files - -### `chrome-extension/manifest.json` -**Purpose:** configuration Extensions -**Содержит:** -- Background script registration -- SIDE panel configuration -- DevTools panel configuration -- Permissions - -### `package.json` (в каждой папке pages) -**Purpose:** Dependencies и скрипты сборки -**Содержит:** -- React dependencies -- build tools -- TypeScript configuration - -## Стили и ресурсы - -###.css файлы -- `PluginControlPanel.css` - стили для основного компонента -- `SIDEPanel.css` - стили для sIDE panel -- `PluginCard.css` - стили для карточек плагинов - -### Иконки и изображения -- .icon.svg` - иконки плагинов -- ЛогоTypeы и брендинг - -## Тестовые файлы - -### Unit Te.ts -- `__te.ts__/plugin-chat-cache.test.ts` -- `__te.ts__/plugin-chat-API.test.ts` -- `__te.ts__/PluginControlPanel.test.tsx` - -### Integration Te.ts -- `__te.ts__/chat-system.integration.test.ts` - -### E2E Te.ts -- `te.ts/e2e/chat-functionality.test.ts` - -## Documentation - -### Техническая Documentation -- `README.md` - общее Description -- `API.md` - Documentation API -- `ARCHITECTURE.md` - архитектурное Description - -### Пользовательская Documentation -- `USER_GUIDE.md` - руководство пользователя -- `DEVELOPER_GUIDE.md` - руководство разработчика - -## Заключение - -Система чатов плагинов имеет четкую модульную архитектуру с разделением ответственности: - -1. **Background Layer** - управление данными и бизнес-логика -2. **UI Layer** - пользовательский Interface -3. **DevTools Layer** - административные функции -4. **Shared Layer** - общие Typeы и утилиты - -Все компоненты связаны через messaging API, что обеспечивает loose coupling и легкую тестируемость. Каждый файл имеет четко определенную ответственность и минимальные Dependencies. \ No newline at end of file diff --git a/.cursor/backup/rules/architecture/project-architecture.mdc b/.cursor/backup/rules/architecture/project-architecture.mdc deleted file mode 100644 index e9846dba..00000000 --- a/.cursor/backup/rules/architecture/project-architecture.mdc +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Overview -Agent-Plugins-Platform (APP) is a browser extension for running Python plugins via PyodIDE and the MCP protocol. Key priorities: security, performance, and developer experience. - -## Architecture Patterns - -### Core Compone.ts -- **Plugin Manager** (`core/plugin-manager.js`): manages plugin lifecycle -- **Host API** (`core/host-API.js`): provIDEs Python access to browser APIs -- **Workflow Engine** (`core/workflow-engine.js`): executes plugin workflows -- **MCP Bridge** (`Bridge/mcp-Bridge.js`):.js ↔ Python communication -- **PyodIDE Worker** (`Bridge/pyodIDE-worker.js`): isolated Python execution - -### Plugin Structure -``` -public/plugins/plugin-name/ -├── manifest.json # Metadata and permissions -├── mcp_server.py # MCP protocol (Python) -├── workflow.json # Workflow description -└──.icon.svg #.icon -``` - -### Communication Flow -1. UI → Plugin Manager → MCP Bridge → PyodIDE Worker → Python Plugin -2. Python Plugin → Host API → Browser APIs -3. Resu.ts return via the same path -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md b/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md deleted file mode 100644 index cb3f3798..00000000 --- a/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Import .cursor Rules - -## Quick Import - -1. Copy the entire `cursor-export` folder to your target project -2. Run the import script: - ```bash - node cursor-export/import-cursor..js - ``` - -## Manual Import - -1. Create `.cursor/rules/` directory in your target project -2. Copy files from `cursor-export/` to `.cursor/rules/` -3. Run audit and optimization: - ```bash - cd .cursor/rules - node cursor-manager..js full - ``` - -## What's Included - -### Core Rules -- AI memory and Commands -- Environment constrai.ts -- Project structure guIDElines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guIDElines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update `environment.mdc` with your project constrai.ts -2. Modify `ai-memory.mdc` with project-specific Commands -3. Adjust `monorepo-best-practices.mdc` for your structure -4. Run `node cursor-manager..js optimize` to apply changes - -## Verification - -Verify successful import: -```bash -cd .cursor/rules -node cursor-manager..js status -``` - -All files should show as "AI-ready" with no issues. diff --git a/.cursor/backup/rules/cursor-export/ai-memory.mdc b/.cursor/backup/rules/cursor-export/ai-memory.mdc deleted file mode 100644 index 3a7e484d..00000000 --- a/.cursor/backup/rules/cursor-export/ai-memory.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Universal user Commands for AI assistant - complete Command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc deleted file mode 100644 index efca4815..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 1: Do No Harm -- Security first: Any changes must improve system security, not weaken it -- Backward compatibility: Changes must not break existing functionality -- Gradual implementation: Implement changes step by step with rollback capability -- Testing: All changes must pass thorough testing before deployment -- Monitoring: Track impact of changes on performance and stability - -description: 'Do No Harm' principle for all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc deleted file mode 100644 index 7d2b513f..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 3: Best Practices First -- Architectural patterns: Use proven architectural patterns and principles -- Coding standards: Follow accepted standards and conventions -- Security: Apply security principles (Zero Trust, Defense in Depth, Least Privilege) -- Performance: Optimize critical paths and avoid anti-patterns -- Scalability: Design consIDEring future growth and changes -- Testability: Write code that is easy to test and maintain - -description: Prioritize best practices in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc deleted file mode 100644 index 743a5fa7..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 4: Fail Fast, Fail Safe -- Early error detection: Validate at input, not during execution -- Graceful Degradation: System continues working even with partial failures -- Circuit breaker: Automatic shutdown of problematic compone.ts -- Rollback capability: Quick rollback to working State -- Error boundaries: Isolate errors at component level - -description: Fail fast and fail safe in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc deleted file mode 100644 index d46bceaa..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 5: Observability First -- Structured logging: Structured logging for analysis -- Metrics everywhere: Performance and State metrics -- distributed tracing: Track reque.ts through all compone.ts -- Health checks: Monitor State of all services -- Debug information: Sufficient information for problem diagnosis - -description: Observability and monitoring in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc deleted file mode 100644 index 90cf296d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 6: configuration as Code -- Version-controlled configs: All configurations in version control -- Environment-specific: Different configurations for different environme.ts -- Validation: Automatic configuration validation -- Documentation: Document all configuration parameters -- Default safety: Safe default values - -description: configuration as code for all environme.ts -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc deleted file mode 100644 index 4cb14008..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 7: Progressive Enhancement -- Core functionality: Basic functionality always works -- Feature detection: Detect browser capabilities -- Graceful Degradation: Degrade functionality, not complete failure -- Performance budget: Performance budget for new features -- Accessibility baseline: Minimum accessibility level for all - -description: Progressive enhancement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc deleted file mode 100644 index 33209d0d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 8: data Integrity & Privacy -- data validation: Validate all input data -- Encryption at rest: Encrypt data at rest -- Encryption in transit: Encrypt data in transit -- data minimization: Collect only necessary data -- User consent: Explicit user consent for data processing -- Right to be forgotten: Ability to delete user data - -description: data integrity and privacy in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc deleted file mode 100644 index f298f32d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 9: Continuous Learning -- Performance monitoring: Continuous performance monitoring -- User feedback loops: User feedback -- A/B testing: Test hypotheses on real users -- Analytics insig.ts: Usage analysis for improveme.ts -- Knowledge sharing: Document.lessons and insig.ts - -description: Continuous learning and improvement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc deleted file mode 100644 index 5a16b370..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 10: Ecosystem Thinking -- Plugin compatibility: Ensure plugin compatibility -- API stability: Stable public APIs -- Backward compatibility: Backward version compatibility -- Community building: Support developer community -- Documentation quality: Quality documentation for ecosystem - -description: Ecosystem thinking in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc b/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc deleted file mode 100644 index 535e47d1..00000000 --- a/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: General development guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: false -aiCategory: development-practices--- - -# Development GuIDElines - -## JavaScript/TypeScript -- Use ES6+ and async/await. -- Modular architecture, clear separation of concerns. -- Error handling and structured logging are required. -- Use TypeScript for type safety. -- Prefer functional patterns where possible. - -## Python (Plugins) -- Use MCP protocol for communication. -- Use async/await for I/O operations. -- Always validate inp.ts and handle errors. -- Plugins should be single-purpose. -- Document all plugin APIs clearly. - -# Security First -- Validate manife.ts and permissions. -- Sanitize data between.js and Python. -- Sandbox all plugins. -- Apply the principle of least privilege. -- Audit all plugin code. - -# Performance ConsIDErations -- Lazy-load plugins and PyodIDE when possible. -- cache repeated operations. -- Monitor memory usage and clean up resources. -- Optimize bundle size and loading time. -- Use WebWorkers for non-blocking tasks. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc b/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc deleted file mode 100644 index 8dadc7b2..00000000 --- a/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git workflow rule - только develop в main, обязательные PR для всех изменений -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Git Workflow Rule - -**Rule:** -- Only merge into main from develop. -- All feature, fix, and doc branches must be merged into develop first, never directly into main. -- This rule is mandatory for all changes and pull reque.ts. - ---- -## Enforcement -- Local pre-push hook (.husky/pre-push) blocks direct push to main and develop. -- GitHub branch protection preve.ts direct push and enforces PRs from develop only. ---- \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc b/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc deleted file mode 100644 index d5f45cd4..00000000 --- a/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Security rule for -and-deployment -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Security Best Practices - -## Plugin Validation -- Validate plugin manife.ts. -- Check all permissions. -- Scan for malicious code. -- Verify plugin signatures. - -## Runtime Security -- Sandbox all plugins. -- Validate all input data. -- Monitor plugin behavior. -- Apply rate limiting. - -## data Protection -- Sanitize all user data. -- Encrypt sensitive information. -- Ensure secure communication. -- Audit all data access. - -# Deployment ConsIDErations - -## Extension distribution -- Follow Chrome Web Store guIDElines. -- Use a secure update mechanism. -- ProvIDE a transparent privacy policy. -- Maintain user support channels. - -## Plugin distribution -- Use a marketplace for plugins. -- Validate and manage plugin versions. -- Perform security scanning. - -## Monitoring and Analytics -- Track plugin usage. -- Monitor performance. -- Collect error repo.ts. -- Analyze user feedback. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc b/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc deleted file mode 100644 index 3008bfad..00000000 --- a/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing Strategy - -## Unit Testing -- Test compone.ts in isolation. -- Mock external dependencies. -- Validate error handling. -- Test security boundaries. - -## Integration Testing -- Test plugin loading and execution. -- Validate.js-Python communication. -- Test integration with browser APIs. -- Check permission compliance. - -## End-to-End Testing -- Test full plugin workflows. -- Validate user scenarios. -- Test extension installation and updates. -- Check cross-browser compatibility. - -# Debugging GuIDElines - -## JavaScript Debugging -- Use DevTools for extension debugging. -- Log message flow. -- Use breakpoi.ts for complex logic. -- Monitor WebWorker communication. - -## Python Debugging -- Use print/logging. -- Test plugins in isolation. -- Use PyodIDE console for debugging. - -## Common Issues -- PyodIDE startup: use loading indicators. -- Memory leaks: monitor worker lifecycle. -- Permission errors: validate manife.ts. -- Communication failures: check MCP format. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/typescript.mdc b/.cursor/backup/rules/cursor-export/dev/typescript.mdc deleted file mode 100644 index 7a1218c4..00000000 --- a/.cursor/backup/rules/cursor-export/dev/typescript.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: TypeScript-specific guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Rule -- Use only TypeScript for all new files. -- Enforce strict typing, avoid using `any`. -- Use modern language features (optional chaining, nullish coalescing, etc.). -- Always follow the shared.tsconfig from `packages.tsconfig/`. - -# examples -- ✅ `const foo: string = "bar"` -- ❌ `let x: any = 5` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc b/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc deleted file mode 100644 index b9192a53..00000000 --- a/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Fallback procedures for AI -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: Правило fallback для AI - Priority источников информации при отсутствии ответа в memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI Fallback Rule - -**Правило:** -Если возникает вопрос, на который нет ответа в AI memory-bank, необходимо: -1. Сначала обращаться к сведениям в папке .rules проекта. -2. Если нужной информации нет в .rules, обращаться к сведениям в папке memory-bank проекта (особенно к архитектурным и контекстным файлам). - -**Priority:** -Это правило Priorityно для всех будущих консультаций и автоматизаций. \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/doc/ai-first.mdc b/.cursor/backup/rules/cursor-export/doc/ai-first.mdc deleted file mode 100644 index 20ea682c..00000000 --- a/.cursor/backup/rules/cursor-export/doc/ai-first.mdc +++ /dev/null @@ -1,69 +0,0 @@ ---- -description: AI-oriented documentation standards -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: AI-First Documentation Standards - analytical comme.ts and AI-oriented documentation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI-First Documentation Standards - -## Основные Principles -- **Analytical comme.ts**: Add comme.ts explaining logic and architectural decisions for AI understanding -- **Context explanations**: Explain "why", not just "what" the code does -- **Architectural comme.ts**: Document component relationships and data flows -- **Business logic**: Explain complex business logic and decisions made -- **TODO comme.ts**: Leave TODO with explanation of planned improveme.ts - -## examples правильного использования - -### ✅ Аналитические Comme.ts: -```TypeScript -// AI: This function is required for plugin isolation - preve.ts direct DOM access -function createSandboxedEnvironment() { - // Implementation... -} - -/** - * AI: Example usage - this component handles plugin communication - * through a secure message passing system - */ -export function PluginBridge() { - // Implementation... -} -``` - -### ✅ Объяснение контекста: -```TypeScript -// AI: Using setTimeout here because Chrome extension APIs are async -// and we need to ensure DOM is ready before accessing eleme.ts -setTimeout(() => { - initializePlugin(); -}, 100); -``` - -### ✅ TODO с объяснением: -```TypeScript -// TODO: AI - Replace with Web Workers for better performance -// Current implementation blocks main thread during heavy computations -function proceSSLargedataset(data) { - // Implementation... -} -``` - -## Recommendations -1. **Всегда объясняйте "почему"** - не только что делает код -2. **Используйте префикс "AI:"** для комментариев, предназначенных для AI -3. **Документируйте архитектурные решения** и их обоснование -4. **Объясняйте сложную бизнес-логику** с примерами -5. **Оставляйте TODO с контекстом** для будущих улучшений diff --git a/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc b/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc deleted file mode 100644 index f3bdcf53..00000000 --- a/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Project knowledge structure -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Knowledge Map - -- USER_CommandS: USER_CommandS.md — все пользовательские команды -- graveyard: memory-bank/graveyard.md — ошибки, environment-specific issues -- architecture: memory-bank/codebase-architecture.md — архитектурные решения, Patterns, Dependencies -- meta: memory-bank/README.md — мета-правила, структура, универсальные инструкции -- dev-principles: memory-bank/DEV_PRACTICES.md — best practices для разработки -- security: memory-bank/SECURITY.md — Standards безопасности -- knowledge-map: memory-bank/KNOWLEDGE_MAP.md — карта знаний -- progress: memory-bank/progress.md — Status и история -- activeContext: memory-bank/activeContext.md — актуальный контекст -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc b/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc deleted file mode 100644 index 5d93b321..00000000 --- a/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc +++ /dev/null @@ -1,134 +0,0 @@ ---- -description: Standards for .mdc file creation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# Standards создания правил в .cursor/rules - -## Priority .mdc файлов над .md - -### Required использовать .mdc для: -- **Правил и стандартов** - все правила должны быть в .mdc -- **AI инструкций** - команды, Principles, Constrai.ts -- **Архитектурных решений** - структура, Patterns, best practices -- **Автоматизации** - скрипты, workflow, Processes - -### Допустимо использовать .md для: -- **Чистой документации** - README, guIDEs, tutorials -- **Временных заметок** - черновики, эксперименты -- **Внешних ссылок** - ссылки на внешние ресурсы - -## Структура .mdc файла - -### Обязательные метаdata: -``.yaml ---- -description: Краткое Description назначения файла -globs: ["**/*"] # Patterns файлов для применения -alwaysApply: true # Автоматическое применение ---- -``` - -### Дополнительные метаdata: -``.yaml ---- -description: Description -globs: ["pages/**/*", "packages/**/*"] -alwaysApply: false # Применяется по запросу -related: - - other-rule.mdc - - another-rule.mdc ---- -``` - -## Преимущества .mdc файлов - -### 1. Автоматическое применение -- Cursor читает метаdata при создании чата -- Правила применяются без явного обращения -- `alwaysApply: true` обеспечивает постоянное Action - -### 2. Лучшая Integration с AI -- AI понимает контекст применения через `globs` -- Description помогает AI выбрать подходящие правила -- Структурированные метаdata улучшают понимание - -### 3. Приоритизация и организация -- `alwaysApply` определяет Importantсть правила -- `related` связывает связанные правила -- `globs` ограничивает область применения - -### 4. AI memory-bank Integration -- Правила автоматически попадают в AI memory-bank -- Доступны через Settings / Rules & Memories -- Сохраняются между сессиями - -## Миграция .md → .mdc - -### Когда мигрировать: -- Файл содержит правила или инструкции -- Файл должен применяться автоматически -- Файл важен для AI понимания проекта - -### Процесс миграции: -1. Переименовать `.md` → `.mdc` -2. Add.yaml frontmatter с метаданными -3. Убедиться в корректности `globs` и `alwaysApply` -4. Check интеграцию с AI memory-bank - -## examples правильного использования - -### ✅ Правильно - .mdc с метаданными: -``.yaml ---- -description: Правила TypeScript - configuration, barrel expo.ts, troubleshooting -globs: ["**/*.ts", "**/*.tsx", "**/*.json"] -alwaysApply: true ---- -``` - -### ❌ Неправильно - .md без метаданных: -```markdown -# TypeScript Rules -- Use strict mode -- Prefer barrel expo.ts -``` - -## Exceptions - -### Допустимые .md файлы: -- `README.md` - главная Documentation проекта -- `CHANGELOG.md` - история изменений -- `LICENSE.md` - лицензия -- Временные файлы с префиксом `temp-` или `draft-` - -## Verification соответствия - -### Автоматическая Verification: -```bash -# Найти все .md файлы в .cursor/rules -find .cursor/rules -name "*.md" -not -name "README.md" - -# Check структуру .mdc файлов -node .cursor/rules/check-rules-structure..js -``` - -### Ручная Verification: -1. Все ли правила в .mdc формате? -2. Есть ли метаdata во всех .mdc файлах? -3. Корректны ли `globs` и `alwaysApply`? -4. Нужно ли мигрировать какие-то .md файлы? - -## Recommendations - -1. **Всегда начинать с .mdc** для новых правил -2. **Мигрировать важные .md** файлы в .mdc -3. **Проверять метаdata** при создании правил -4. **Использовать `alwaysApply: true`** для критических правил -5. **Документировать Exceptions** в README.mdc -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc b/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc deleted file mode 100644 index 0e2c801c..00000000 --- a/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Quality standards for memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Memory-Bank Quality Checklist - -- Актуальность: информация своевременно обновляется -- Полнота: все ключевые аспекты проекта отражены -- Нет дублирования: информация не повторяется в разных файлах -- Навигация: структура и ссылки понятны -- История изменений: все важные правки фиксируются -- Прозрачность: причины изменений и удаления всегда документируются -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/restore-context.mdc b/.cursor/backup/rules/cursor-export/doc/restore-context.mdc deleted file mode 100644 index cbee881a..00000000 --- a/.cursor/backup/rules/cursor-export/doc/restore-context.mdc +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: Context restoration procedures -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Восстанови контекст - -1. Прочитать все файлы в директории `memory-bank` и `USER_CommandS.md`. -2. Сделать краткое резюме ключевых положений, правил, пользовательских требований и текущего Statusа проекта. -3. Использовать эту информацию для всех последующих ответов и действий. - -> Это правило предназначено для ручного вызова или использования агентом при необходимости полного восстановления контекста проекта. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/environment.mdc b/.cursor/backup/rules/cursor-export/environment.mdc deleted file mode 100644 index 9fb92cb1..00000000 --- a/.cursor/backup/rules/cursor-export/environment.mdc +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Critical environment constrai.ts - Node.js версия, popup vs sIDEpanel -globs: ["**/*"] -alwaysApply: trueaiPriority: critical -aiCategory: general ---- - -# Critical environment constrai.ts - -- **popup не используется**, основной Interface — **sIDEpanel**. Все ошибки popup можно игнорировать, если они не влияют на работу sIDEpanel. - -- **Использовать только Node.js версии 22.17.1 и выше**. Понижать версию запрещено, все баги и инфраструктурные проблемы решать только в этом контексте. \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/index.mdc b/.cursor/backup/rules/cursor-export/index.mdc deleted file mode 100644 index d0272f95..00000000 --- a/.cursor/backup/rules/cursor-export/index.mdc +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: index rule -globs: ["**/*"] -alwaysApply: true -aiCategory: general--- - -# Index of Modular Rules - -## root -- [AI Memory Bank](ai-memory.mdc) — User Commands and AI instructions -- [Environment Constrai.ts](environment.mdc) — Critical environment limitations -- [Monorepo Best Practices](monorepo-best-practices.mdc) — Monorepo structure and guIDElines -- [TypeScript build Troubleshooting](TypeScript-build-troubleshooting.mdc) — TypeScript build error solutions -- [README](README.mdc) — Main documentation and structure - -## architecture -- [Chat System Architecture](architecture/architecture-chat-system.mdc) — Chat system architecture and data flow -- [Error Handling (Architecture)](architecture/architecture-error-handling.mdc) — Error handling requireme.ts for system architecture -- [Monitoring and Observability (Architecture)](architecture/architecture-observability.mdc) — Monitoring and observability requireme.ts for system architecture -- [Performance GuIDElines (Architecture)](architecture/architecture-performance.mdc) — Performance guIDElines for system architecture -- [Plugin Architecture](architecture/architecture-plugin.mdc) — Plugin architecture and isolation requireme.ts -- [Project Structure Architecture](architecture/architecture-project-structure.mdc) — Directory and component structure for the project -- [Security Architecture](architecture/architecture-security.mdc) — Security architecture and controls -- [Development Workflow (Architecture)](architecture/architecture-workflow.mdc) — Development workflow and process standards -- [Project Overview](architecture/project-architecture.mdc) - -## dev -- [Development Principle 1: Do No Harm](dev/dev-principle-01-do-no-harm.mdc) — 'Do No Harm' principle for all development -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation and comme.ts for all code -- [Development Principle 3: Best Practices First](dev/dev-principle-03-best-practices.mdc) — Prioritize best practices in all development -- [Development Principle 4: Fail Fast, Fail Safe](dev/dev-principle-04-fail-fast-safe.mdc) — Fail fast and fail safe in all development -- [Development Principle 5: Observability First](dev/dev-principle-05-observability.mdc) — Observability and monitoring in all development -- [Development Principle 6: configuration as Code](dev/dev-principle-06-config-as-code.mdc) — configuration as code for all environme.ts -- [Development Principle 7: Progressive Enhancement](dev/dev-principle-07-progressive-enhancement.mdc) — Progressive enhancement in all development -- [Development Principle 8: data Integrity & Privacy](dev/dev-principle-08-data-integrity-privacy.mdc) — data integrity and privacy in all development -- [Development Principle 9: Continuous Learning](dev/dev-principle-09-continuous-learning.mdc) — Continuous learning and improvement in all development -- [Development Principle 10: Ecosystem Thinking](dev/dev-principle-10-ecosystem-thinking.mdc) — Ecosystem thinking in all development -- [Development GuIDElines](dev/development-guIDElines.mdc) — General development guIDElines -- [Security Best Practices](dev/security-and-deployment.mdc) — Security and deployment standards -- [Testing Strategy](dev/testing-and-debugging.mdc) — Testing and debugging approaches -- [TypeScript Rules](dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Git Workflow](dev/git-workflow.mdc) — Git workflow and branching rules - -## doc -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation standards -- [Knowledge Map](doc/knowledge-map.mdc) — Project knowledge structure -- [Memory-Bank Quality Checklist](doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](doc/restore-context.mdc) — Context restoration procedures -- .mdc File Standards](doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [AI Fallback Rule](doc/ai-fallback.mdc) — Fallback procedures for AI - -## plugin -- [Plugin Best Practices](plugin/plugin-best-practices.mdc) — Best practices for plugin development -- [Plugin Documentation Standards](plugin/plugin-documentation.mdc) — Documentation standards for all plugins -- [Plugin Error Handling](plugin/plugin-error-handling.mdc) — Error handling requireme.ts for all plugins -- [Plugin Performance GuIDElines](plugin/plugin-performance.mdc) — Performance guIDElines for all plugins -- [Plugin Security Requireme.ts](plugin/plugin-security.mdc) — Security requireme.ts for all plugins -- [Plugin Structure](plugin/plugin-structure.mdc) — Plugin directory and manifest requireme.ts -- [Plugin Testing Requireme.ts](plugin/plugin-testing.mdc) — Testing requireme.ts for all plugins - -## security -- [Input Validation](security/validation.mdc) — Input validation and data sanitization rules - -## ui -- [Accessibility (A11y) Standards](ui/ui-accessibility.mdc) — Accessibility requireme.ts for all UI compone.ts -- [UI Animation and Transitions](ui/ui-animation.mdc) — Animation and transition standards for all UI compone.ts -- [UI Error Handling](ui/ui-error-handling.mdc) — Error handling requireme.ts for all UI compone.ts -- [UI Form Standards](ui/ui-forms.mdc) — Form standards for all UI compone.ts -- [UI Loading States](ui/ui-loading-States.mdc) — Loading State requireme.ts for all UI compone.ts -- [UI Mobile Responsiveness](ui/ui-mobile.mdc) — Mobile responsiveness standards for all UI compone.ts -- [UI Navigation Standards](ui/ui-navigation.mdc) — Navigation standards for all UI compone.ts -- [UI Performance Standards](ui/ui-performance.mdc) — Performance standards for all UI compone.ts -- [React Component Standards](ui/ui-React-compone.ts.mdc) — Standards for React component structure in UI -- [UI Styling Standards](ui/ui-styling.mdc) — Styling standards for all UI compone.ts -- [UI Testing Standards](ui/ui-testing.mdc) — Testing standards for all UI compone.ts - -## workflow -- [Automation Rules](workflow/automation.mdc) — Automation and synchronization rules -- [Branch Management](workflow/branches.mdc) — Git branch management rules -- [Workflow Branching Rules](workflow/workflow.mdc) — Development workflow standards \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc b/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc deleted file mode 100644 index f0a8387d..00000000 --- a/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc +++ /dev/null @@ -1,208 +0,0 @@ ---- -description: Best practices for monorepo work - структура, Dependencies, TypeScript, barrel expo.ts -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# Monorepo Best Practices for AI - -## Основные Principles - -### 1. Project Structure -- **packages/** - общие Libraries и утилиты -- **pages/** - приложения и страницы Extensions -- **external/** - копии внешних boilerplate проектов -- **te.ts/** - тесты и e2e - -### 2. Dependencies -- **Root dependencies** - только общие инструменты (prettier, eslint, TypeScript) -- **Package dependencies** - устанавливать в конкретных пакетах -- **Workspace dependencies** - использовать `pnpm add -D package -w` для root - -### 3. TypeScript configuration -- **Base config** - `packages.tsconfig/base.json` для общих настроек -- **Package configs** - наследовать от base с специфичными настройками -- **App configs** - наследовать от base с browser-specific настройками - -## Правила для AI - -### При создании новых пакетов: - -1. **Create правильную структуру:** -``` -packages/new-package/ -├── lib/ -│ ├── index.ts -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -└── README.md -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "...tsconfig/base.json", - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - }, - "include": ["lib/**/*", "index..ts"], - "exclude": ["node_modules", "dist"] -} -``` - -3. **Настроить package.json:** -``.json -{ - "name": "@extension/new-package", - "main": "dist/index..js", - "types": "dist/index.d.ts", - "expo.ts": { - ".": "./dist/index..js" - }, - "files": ["dist"] -} -``` - -### При создании новых страниц: - -1. **Create правильную структуру:** -``` -pages/new-page/ -├── src/ -│ ├── index.tsx -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -├── pos.css.config..js -└── vite.config..ts -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "@extension.tsconfig/base", - "compilerOptions": { - "types": ["chrome"], - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -3. **Настроить Pos.css для Tailwin.css 4+:** -```JavaScript -// pos.css.config..js -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -### При работе с barrel expo.ts: - -1. **Default expo.ts:** -```TypeScript -// component.ts -export default function Component() { ... } - -// index.ts -export { default as Component } from './component.js'; -``` - -2. **Named expo.ts:** -```TypeScript -// component.ts -export function Component() { ... } - -// index.ts -export { Component } from './component'; -``` - -3. **Mixed expo.ts:** -```TypeScript -// index.ts -export * from './helpers.js'; -export { default as Component } from './component.js'; -export type * from './types.js'; -``` - -### При установке зависимостей: - -1. **В конкретном пакете:** -```bash -cd packages/package-name -pnpm add dependency-name -``` - -2. **В конкретной странице:** -```bash -cd pages/page-name -pnpm add dependency-name -``` - -3. **В workspace root:** -```bash -pnpm add -D dependency-name -w -``` - -### При диагностике проблем: - -1. **Check Dependencies:** -```bash -pnpm why package-name -pnpm list package-name -``` - -2. **Найти проблемные файлы:** -```bash -find . -name .tsconfig.json" -exec grep -l "node" {} \; -``` - -3. **Очистить и пересобрать:** -```bash -rm -rf dist && pnpm run build -``` - -## Частые ошибки и решения - -| Проблема | Решение | -|----------|---------| -| `Cannot find type definition file for 'node'` | Remove 'node' из types в.tsconfig.json | -| `"Component" is not exported by` | Использовать `export { default as Component } from './file.js'` | -| `Cannot find module 'tailwin.css'` | `pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css` | -| `Rollup failed to resolve import` | `pnpm add package-name` в конкретном пакете | -| `spawn prettier ENOENT` | `git commit --no-verify` | - -## Команды для работы - -```bash -# build всего проекта -pnpm run build - -# build конкретного пакета -pnpm -F @extension/package-name run build - -# Установка зависимостей -pnpm install - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache - -# Пропуск pre-commit хуков -git commit --no-verify -m "message" -``` - -## Recommendations - -1. **Всегда проверять сборку** после изменений -2. **Документировать решения** для повторного использования -3. **Использовать правильные barrel expo.ts** для default expo.ts -4. **Устанавливать Dependencies в правильных местах** -5. **Следовать структуре проекта** при создании новых файлов \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc deleted file mode 100644 index a9901630..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for best-practices -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Best Practices -- Single Responsibility: Each plugin should have a single, clear purpose -- Modular Design: Break complex functionality into modules -- configuration: Use configuration files for customizable behavior -- Versioning: Follow semantic versioning for plugin updates -- Backward Compatibility: Maintain compatibility with previous versions - -description: Best practices for plugin development -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc deleted file mode 100644 index 5b34417a..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for documentation -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Documentation Standards -- API Documentation: Document all public APIs and parameters -- Usage examples: ProvIDE clear usage examples -- Error Codes: Document all possible error codes and meanings -- Performance Notes: Document performance characteristics -- Security Notes: Document security consIDErations - -description: Documentation standards for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc deleted file mode 100644 index a95d9cf5..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for error-handling -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Error Handling -- Graceful Degradation: Continue working with reduced functionality -- User Feedback: ProvIDE clear error messages to users -- Logging: Log errors for debugging without exposing sensitive data -- Fallbacks: Implement fallback mechanisms for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Error handling requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc deleted file mode 100644 index 868ad488..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for performance -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Performance GuIDElines -- PyodIDE Optimization: Optimize for WebAssembly execution -- Memory Management: Clean up resources and avoid memory leaks -- Async Operations: Use async/await for all I/O operations -- Caching: Implement appropriate caching strategies -- Bundle Size: Keep plugin size reasonable for browser loading - -related: - - architecture-performance.mdc - - plugin-testing.mdc - -description: Performance guIDElines for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc deleted file mode 100644 index a3e81a41..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Plugin standard for security -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: true -aiCategory: plugin-development--- - -# Plugin Security Requireme.ts -- Zero Trust: Plugins are not trusted by default -- Permission Declaration: All required permissions must be declared in manifest -- API Validation: All API calls must be validated against schemas -- Rate Limiting: Respect rate lim.ts to prevent abuse -- Error Handling: Graceful error handling without exposing sensitive data - -description: Security requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc deleted file mode 100644 index 92065786..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc +++ /dev/null @@ -1,60 +0,0 @@ ---- -description: Plugin standard for structure -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Structure - -## Directory Layout -``` -public/plugins/plugin-name/ -├── manifest.json # Plugin metadata and permissions -├── mcp_server.py # Python MCP protocol implementation -├── workflow.json # Plugin workflow definition (optional) -└──.icon.svg # Plugin.icon -``` - -## Manifest.json Example -``.json -{ - "name": "Plugin Name", - "version": "1.0.0", - "description": "Plugin description", - "main_server": "mcp_server.py", - "required_secr.ts": ["openai_API_key", "weather_API_key"], - "API_permissions": { - "openai": { - "domains": ["API.openai.com"], - "endpoi.ts": ["/v1/chat/completions"], - "methods": ["POST"], - "rate_limit": "100/hour" - } - }, - "network_policy": { - "allowed_domains": ["API.openai.com"], - "WebSock.ts": "denied" - }, - "API_schemas": { - "openai": { - "chat_completions": { - "type": "object", - "required": ["prompt"], - "properties": { - "prompt": {"type": "string", "maxLength": 4000} - } - } - } - } -} -``` -description: Plugin directory and manifest requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc deleted file mode 100644 index c90be344..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for testing -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Testing Requireme.ts -- Unit Te.ts: Test individual functions and compone.ts -- Integration Te.ts: Test plugin integration with host system -- Security Te.ts: Validate permission enforcement -- Performance Te.ts: Monitor memory usage and execution time -- Error Te.ts: Test error handling and recovery - -description: Testing requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/security/validation.mdc b/.cursor/backup/rules/cursor-export/security/validation.mdc deleted file mode 100644 index 76a56384..00000000 --- a/.cursor/backup/rules/cursor-export/security/validation.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Input validation and data sanitization rules -globs: ["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"] -alwaysApply: false -aiCategory: security--- - -# Rule -- Always validate all input data and API parameters. -- Never trust data from external sources (user, plugins, third-party services). -- Use schemas, types, or validators (e.g., zod, pydantic, marshmallow). - -# examples -- ✅ `z.object({ email: z.string().email() })` -- ✅ `if not isinstance(user_id, int): raise ValueError()` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc b/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc deleted file mode 100644 index c65be120..00000000 --- a/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc +++ /dev/null @@ -1,166 +0,0 @@ ---- -description: TypeScript build error troubleshooting guIDE - Typeы, barrel expo.ts, Dependencies, Tailwin.css -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# TypeScript build Troubleshooting Rules - -## Основные Principles устранения ошибок - -### 1. TypeScript configuration Errors - -**Error:** `Cannot find type definition file for 'node'` -- **Причина:** В .tsconfig.json` указаны Typeы Node.js для Browserного кода -- **Решение:** Remove `'node'` из `compilerOptions.types`, оставить только `['chrome']` -- **Применяется к:** `pages/*.tsconfig.json`, `te.ts/e2e.tsconfig.json` - -**Error:** `Cannot find module '@extension/shared'` -- **Причина:** Неправильные `paths` в .tsconfig.json` -- **Решение:** Add корректные пути в `compilerOptions.paths` -- **Пример:** -``.json -{ - "compilerOptions": { - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### 2. Barrel Export Issues - -**Error:** `"initAppWithShadow" is not exported by ".../packages/shared/dist/index..js"` -- **Причина:** Неправильный barrel export для default expo.ts -- **Решение:** Использовать явный re-export: `export { default as initAppWithShadow } from './init-app-with-shadow.js'` -- **Паттерн:** -```TypeScript -// В index.ts -export { default as ComponentName } from './component-file.js'; - -// В component-file.ts -export default function ComponentName() { ... } -``` - -### 3. Tailwin.css 4+ Setup - -**Error:** `Cannot find module 'tailwin.css'` -- **Решение:** Установить Dependencies в конкретном пакете: -```bash -pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css -``` - -**Error:** `Loading Pos.css Plugin failed: Cannot find module '@tailwin.css/pos.css'` -- **Решение:** Create `pos.css.config..js`: -```JavaScript -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -**Error:** `Command 'tailwin.css-cli' not found` -- **Решение:** Delete прямой вызов CLI из `build..ts`, использовать Vite Pos.css pipeline - -### 4. Module Resolution - -**Error:** `Rollup failed to resolve import 'file-saver'` -- **Решение:** Установить недостающую зависимость в конкретном пакете: -```bash -pnpm add file-saver -``` - -**Error:** `Failed to resolve entry for package '@extension/vite-config'` -- **Причина:** Неправильные `main`/`expo.ts` в `package.json` -- **Решение:** Использовать ESM-only экспорт: -``.json -{ - "main": "dist/index..js", - "expo.ts": { - ".": "./dist/index..js" - } -} -``` - -### 5. build Script Issues - -**Error:** .jsx is not exported by React.jsx-runtime.js` -- **Причина:** НеCompatibility версий React/SWC/Vite -- **Решение:** - 1. Update до последних версий: `React`, `React-dom`, `vite`, `@vit.js/plugin-React-swc` - 2. Убедиться в .tsconfig.json`: `.jsx": "React.jsx"` - 3. Не использовать `import type React` - -### 6. Pre-commit Hook Issues - -**Error:** `spawn prettier ENOENT` -- **Решение:** Установить prettier и Plugins: -```bash -pnpm add -D prettier prettier-plugin-tailwin.css -w -``` -- **Альтернатива:** Использовать `git commit --no-verify` для пропуска хуков - -## Порядок устранения ошибок - -1. **Анализ ошибки:** Определить Type и контекст -2. **Verification зависимостей:** Убедиться в наличии всех пакетов -3. **configuration:** Исправить .tsconfig.json`, `package.json` -4. **Barrel expo.ts:** Check правильность экспортов -5. **build:** Выполнить `rm -rf dist && pnpm run build` -6. **cache:** При необходимости очистить cache: `pnpm exec rimraf node_modules/.vite .turbo .cache` - -## Частые Patterns исправлений - -### Для pages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "types": ["chrome"], // Remove 'node' - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### Для packages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - } -} -``` - -### Для barrel expo.ts: -```TypeScript -// Правильно для default expo.ts -export { default as ComponentName } from './component-file.js'; - -// Правильно для named expo.ts -export { ComponentName } from './component-file.js'; -``` - -## Команды для диагностики - -```bash -# Verification зависимостей -pnpm why React -pnpm why tailwin.css - -# Поиск файлов -find . -name .tsconfig.json" -exec grep -l "node" {} \; - -# Verification сборки -pnpm run build - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache && pnpm install -``` \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc b/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc deleted file mode 100644 index 197d9d79..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for accessibility -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# Accessibility (A11y) Standards -- ProvIDE proper ARIA labels for interactive eleme.ts -- Ensure all functionality is keyboard accessible -- Proper focus handling and visible focus indicators -- Use semantic.html and descriptive text for screen readers -- Meet WCAG color contrast requireme.ts - -related: - - ui-forms.mdc - - ui-error-handling.mdc - -description: Accessibility requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc b/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc deleted file mode 100644 index 263fa004..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for animation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Animation and Transitions -- Use.css transitions for State changes -- Subtle loading animations -- Enhance user experience with micro-interactions -- Ensure animations don't impact performance -- Respect user's motion preferences (reduced motion) - -description: Animation and transition standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc b/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc deleted file mode 100644 index c2613648..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for error-handling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Error Handling -- Clear, actionable error messages for users -- Catch and handle component errors with error boundaries -- ProvIDE fallback UI for failed compone.ts -- Allow users to retry failed operations -- Log errors for debugging - -description: Error handling requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc b/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc deleted file mode 100644 index e9b6928b..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for forms -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Form Standards -- Client-sIDE and server-sIDE validation -- Show validation errors clearly -- ProvIDE clear success feedback -- Auto-save forms where appropriate -- Support common keyboard shortc.ts - -description: Form standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc b/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc deleted file mode 100644 index 4b268cbf..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for loading-States -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Loading States -- Show skeleton UI while loading -- Use spinners or progress bars as indicators -- ProvIDE informative loading messages -- Handle loading timeo.ts gracefully -- Show appropriate error States - -description: Loading State requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc b/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc deleted file mode 100644 index 3c472d88..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for mobile -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Mobile Responsiveness -- Adequate size for touch interaction (touch targ.ts) -- Proper viewport meta tags -- Use flexbox and grid for responsive layo.ts -- Support common touch gestures -- Optimize for mobile performance - -description: Mobile responsiveness standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc b/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc deleted file mode 100644 index 02d02c56..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for navigation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Navigation Standards -- ProvIDE clear navigation context with breadcrumbs -- Clearly indicate current page/section (active States) -- Support browser back button -- Support direct links to specific content (deep linking) -- Show loading during navigation - -description: Navigation standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc b/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc deleted file mode 100644 index dc609680..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for performance -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Performance Standards -- Lazy load compone.ts and routes -- Use React.memo and useMemo appropriately -- Split code into smaller bundles -- Optimize images for web -- Implement appropriate caching strategies - -description: Performance standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc b/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc deleted file mode 100644 index 84458a72..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for React-compone.ts -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# React Component Standards -- Use TypeScript for all new compone.ts -- Prefer functional compone.ts with hooks -- Define clear interfaces for component props -- ProvIDE sensible default values -- Wrap compone.ts in error boundaries - -description: Standards for React component structure in UI -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc b/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc deleted file mode 100644 index 0a2f7319..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for styling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Styling Standards -- Use Tailwind.css for consistent styling -- Organize.css classes logically -- Ensure responsive design for different screen sizes -- Support light/dark mode -- Use.css variables for theme values - -description: Styling standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc b/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc deleted file mode 100644 index 133251bf..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for testing -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Testing Standards -- Test component logic and behavior (unit te.ts) -- Test component interactions (integration te.ts) -- Test visual appearance and layout (visual te.ts) -- Test accessibility compliance (a11y te.ts) -- Test component performance - -related: - - plugin-testing.mdc - - ui-error-handling.mdc - -description: Testing standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/workflow/automation.mdc b/.cursor/backup/rules/cursor-export/workflow/automation.mdc deleted file mode 100644 index 14024a6e..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/automation.mdc +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Automation Rules - -- При обновлении ключевых принципов, стандартов или чек-листов в memory-bank (например, DEV_PRACTICES.md, SECURITY.md, KNOWLEDGE_MAP.md), ассистент обязан синхронизировать их с правилами Cursor (.cursor/rules/). -- Все изменения должны быть отражены как в документации, так и в правилах для AI. -- Если обнаружено расхождение — предложить пользователю синхронизировать и объяснить различия. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/workflow/branches.mdc b/.cursor/backup/rules/cursor-export/workflow/branches.mdc deleted file mode 100644 index d485b834..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/branches.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git branch management rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Rule -- For new features, use branches with the prefix `feature/` and a meaningful name. -- For bugfixes, use the prefix `fix/` and a clear description of the problem. -- Never mix bugfixes and features in the same branch. -- After merging a bugfix, update feature branches via rebase/merge from develop. -- Direct comm.ts or merges to `main` and `develop` are strictly forbidden. All changes must go through pull reque.ts into `develop` only. -- Merging to `main` is allowed only from `develop` after release approval. -- **Branch Purpose Tracking:** Before starting a new, unrelated task, you must finish the current branch (commit, push, PR, merge) and only then create a new branch for the next task. This preve.ts mixing tasks and improves review clarity. Exceptions: urgent bugfixes (fix/). - -# examples -- ✅ `feature/pyodIDE-integration` -- ✅ `fix/tailwin.css-build-script` -- ❌ `feature/pyodIDE-integration` + unrelated refactor in same branch \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/workflow/workflow.mdc b/.cursor/backup/rules/cursor-export/workflow/workflow.mdc deleted file mode 100644 index 8a8bc2a6..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Workflow rule for -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# Workflow Branching Rules - -- В ветке main запрещено вести работу. main используется только для релизов и production. -- В ветке develop запрещено вести работу напрямую. Все изменения — только через feature-Branches с именованием feature/{смысловое_название}, которые создаются автоматически по смыслу задачи. -- Merge feature-веток только через pull request в develop. -- Прямой merge или commit в main и develop запрещён. -- В main разрешён merge только из develop после релизного ревью. -- Все PR должны содержать Description изменений, ссылки на документацию и changelog. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/dev/date-time-patterns.mdc b/.cursor/backup/rules/dev/date-time-patterns.mdc deleted file mode 100644 index b23fd298..00000000 --- a/.cursor/backup/rules/dev/date-time-patterns.mdc +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: Date and time patterns for AI assista.ts - universal guIDElines for handling dates -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Patterns работы с Dateми и временем - -## Проблема -AI-ассистенты не могут самостоятельно получать актуальную дату и Time, что приводит к использованию устаревших дат в документации. - -## Решение: Usage системных команд - -### Получение текущей даты -```bash -# Текущая Date в формате YYYY-MM-DD -date +"%Y-%m-%d" - -# Текущая Date и Time -date +"%Y-%m-%d %H:%M:%S" - -# Текущая Date на русском языке -date +"%d %B %Y" -``` - -### Получение времени -```bash -# Только Time -date +"%H:%M:%S" - -# Time с часовым поясом -date +"%H:%M:%S %Z" -``` - -## Patterns для AI-ассистентов - -### 1. При создании документации -```bash -# Всегда получать актуальную дату перед созданием документации -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" -``` - -### 2. При обновлении существующей документации -```bash -# Проверять, не устарели ли даты в документации -# Если Date старая - обновлять с актуальной -``` - -### 3. При создании коммитов -```bash -# Использовать актуальную дату в сообщениях коммитов -git commit -m "feat: new feature - $(date +"%Y-%m-%d")" -``` - -## Форматы дат для разных целей - -### Documentation (YYYY-MM-DD) -- `2025-07-12` - стандартный формат для документации -- Используется в memory-bank, README, технической документации - -### Comm.ts (YYYY-MM-DD) -- `2025-07-12` - для сообщений коммитов -- Краткий и понятный формат - -### Пользовательский Interface -- `12 июля 2025` - для пользовательского Interfaceа -- Локализованный формат - -### Logging -- `2025-07-12 14:30:25` - полный формат с временем -- Для системных логов и отладки - -## Проверочный список - -### ✅ Перед созданием документации -- [ ] Получить актуальную дату командой `date +"%Y-%m-%d"` -- [ ] Использовать полученную дату в документации -- [ ] Check формат даты (YYYY-MM-DD) - -### ✅ При обновлении документации -- [ ] Check даты в существующих файлах -- [ ] Update устаревшие даты -- [ ] Убедиться в консистентности форматов - -### ✅ При работе с пользователем -- [ ] Уточнить дату, если есть сомнения -- [ ] Использовать понятные пользователю форматы -- [ ] Объяснить, почему используется системная Date - -## examples использования - -### Создание нового этапа в progress.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### ✅ Этап X: Description (Завершен - $current_date)" -``` - -### Обновление Statusа в errors.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### Status" -echo "✅ **РЕШЕНО** - $current_date" -``` - -### Создание коммита с датой -```bash -current_date=$(date +"%Y-%m-%d") -git commit -m "feat: new feature - $current_date" -``` - -## Частые ошибки - -### ❌ Неправильно -- Usage устаревших дат из контекста -- Разные форматы дат в одном документе -- Отсутствие проверки актуальности дат - -### ✅ Правильно -- Usage `date` команды для получения актуальной даты -- Консистентный формат YYYY-MM-DD -- Регулярная Verification и обновление дат - -## Integration с Cursor IDE - -### Project Rules -Add в project rules: -``` -- Always use system date Commands for current date/time -- Format dates as YYYY-MM-DD in documentation -- Validate dates before using in documentation -``` - -### Saved Memories -Create memory: -``` -Date/Time Pattern: Use 'date +"%Y-%m-%d"' Command to get current date -Format: Always use YYYY-MM-DD format in documentation -Validation: Check dates before using in documentation -``` - -## Автоматизация - -### Скрипт для проверки дат -```bash -#!/bin/bash -# check-dates.sh -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -# Verification дат в документации -grep -r "2024-" docs/ memory-bank/ 2>/dev/null | head -5 -echo "Found old dates above. ConsIDEr updating them." -``` - -### Git hook для проверки дат -```bash -#!/bin/bash -# .git/hooks/pre-commit -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -## New Entry ([2025-07-19T01:32:19.979Z]) - -Паттерн работы с Dateми: Использовать date +%Y-%m-%d для получения текущей даты в документации - -echo "Please ensure all dates in documentation are current." -``` \ No newline at end of file diff --git a/.cursor/backup/rules/dev/development-guidelines.mdc b/.cursor/backup/rules/dev/development-guidelines.mdc deleted file mode 100644 index bbd7f624..00000000 --- a/.cursor/backup/rules/dev/development-guidelines.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Development best practices and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Development GuIDElines - -## Overview - -This file contains dev practice guIDElines and best practices. - -## GuIDElines - - - -## Development Practice ([2025-07-19T01:19:31.302Z]) - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы - - diff --git a/.cursor/backup/rules/dev/development-principles.mdc b/.cursor/backup/rules/dev/development-principles.mdc deleted file mode 100644 index 8ea7328f..00000000 --- a/.cursor/backup/rules/dev/development-principles.mdc +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: Development principles for all proje.ts - universal guIDElines and best practices -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: development-practices ---- - -# 🛡️ Principles разработки - -## Краткая сводка (для быстрого понимания) - -### 🔒 Security и надёжность -1. **"Не навреди"** - Security прежде всего, обратная Compatibility -2. **"Fail Fast, Fail Safe"** - быстрый и безопасный отказ, Graceful Degradation -3. **"data Integrity & Privacy"** - целостность и приватность данных - -### 📊 Наблюдаемость и Quality -4. **"Observability First"** - структурированное Logging, метрики, Monitoring -5. **"Best Practices First"** - проверенные Patterns, Standards, Performance -6. **"configuration as Code"** - Versioning конфигураций, Validation - -### 🚀 User Experience -7. **"Progressive Enhancement"** - базовая функциональность всегда работает -8. **"Continuous Learning"** - Monitoring, обратная связь, A/B Testing - -### 🌐 Экосистема и развитие -9. **"Ecosystem Thinking"** - Compatibility плагинов, стабильность API -10. **"Comme.ts для ИИ"** - аналитические Comme.ts для понимания логики - -## Применение принципов - -### Для каждой задачи ИИ-ассистент должен: -1. **Проанализировать** задачу через призму релевантных принципов -2. **Выбрать** наиболее подходящие Principles для конкретной ситуации -3. **Применить** Principles систематически при принятии решений -4. **Документировать** как Principles повлияли на решение - -### examples применения: -- **Новый плагин** → Principles 1, 2, 5, 6, 9, 10 -- **Исправление бага** → Principles 1, 2, 4, 5, 7 -- **Optimization** → Principles 2, 4, 5, 6, 7, 8 -- **Новая Function** → Principles 1, 3, 5, 7, 8, 9 - -## Взаимодополняемость принципов - -Principles не противоречат друг другу, а усиливают: -- **"Не навреди" + "Fail Fast"** = безопасные быстрые решения -- **"Best Practices" + "Observability"** = качественные решения с Monitoringом -- **"Progressive Enhancement" + "Ecosystem"** = стабильная экосистема -- **"data Privacy" + "configuration"** = безопасная configuration - -## Результат применения - -Следование всем принципам обеспечивает: -- ✅ **Highе Quality кода** и архитектуры -- ✅ **Security** и надёжность системы -- ✅ **Лучший User Experience** - -## New Entry ([2025-07-19T01:32:10.564Z]) - -Принцип разработки: Всегда использовать системные команды для получения актуальной даты - -- ✅ **Масштабируемость** и поддерживаемость -- ✅ **Успешную экосистему** плагинов \ No newline at end of file diff --git a/.cursor/backup/rules/dev/testing-troubleshooting.mdc b/.cursor/backup/rules/dev/testing-troubleshooting.mdc deleted file mode 100644 index d70c8c0c..00000000 --- a/.cursor/backup/rules/dev/testing-troubleshooting.mdc +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing & Troubleshooting Best Practices - -## Documentation тестирования и отладки - -### Ключевые Principles: -- Писать пошаговые гайды для всех основных flows тестирования и отладки -- Разделять usage, troubleshooting и advanced diagnostics в отдельные файлы -- Cross-link гайды для навигации (usage → troubleshooting → advanced) -- Использовать четкие, воспроизводимые шаги и code snipp.ts -- Ссылаться на релевантные скрипты, автоматизацию и best practices -- Обновлять гайды при изменении features или workflows - -### examples гайдов: -- DevTools Testing GuIDE -- DevTools Panel Troubleshooting -- DevTools Panel Usage - -## Анализ внешних репозиториев как submodules - -Для анализа, поиска и отслеживания изменений во внешнем git репозитории: - -### Пошагово: -1. В корне монорепо: - ```bash - git submodule add external/ - git submodule update --init --recursive - ``` - - Пример: - ```bash - git submodule add HTTPS://github.com/QizhengMo/chrome-extension-sIDEpanel-template.git external/sIDEpanel-template - git submodule update --init --recursive - ``` - -2. Открыть монорепо в Cursor/VSCode. Submodule будет проиндексирован и доступен для поиска кода, навигации и анализа. - -3. Update submodule до последней версии: - ```bash - cd external/sIDEpanel-template - git pull origin main - ``` - -4. Delete submodule (если больше не нужен): - ```bash - git submodule deinit -f external/sIDEpanel-template - git rm -f external/sIDEpanel-template - rm -rf .git/modules/external/sIDEpanel-template - ``` - -### Преимущества: -- Изолирует внешний код от основной git истории -- Позволяет легкие обновления и независимое Versioning -- Обеспечивает полный поиск кода и анализ в Cursor/VSCode - -**Используйте этот Method для любого внешнего кодового база, который хотите анализировать или ссылаться в монорепо.** -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/ai-answer-self-check.mdc b/.cursor/backup/rules/doc/ai-answer-self-check.mdc deleted file mode 100644 index a1409f4f..00000000 --- a/.cursor/backup/rules/doc/ai-answer-self-check.mdc +++ /dev/null @@ -1,39 +0,0 @@ - - - - ---- -description: ai answer self check rule -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: falseaiPriority: medium -aiCategory: documentation ---- - -# AI Answer Self-Check Policy - -AI-ассистент обязан для каждого ответа: - -## Обязательные Requireme.ts: -- Разбивать вопрос пользователя на подпункты/чеклист -- Явно отмечать, на что уже дан ответ, а что требует пояснения или осталось неотвеченным -- В конце ответа давать self-check: какие пункты покрыты, какие требуют доработки или согласования -- Если требование уже реализовано — явно указывать где и как (файл, секция, коммит, etc.) -- Для сложных вопросов использовать маркированные списки или таблицы - -## Пример self-check: -``` ---- -**Self-check для вашего вопроса:** -1. ✅ Механизм self-check — описан и предложен для реализации -2. ✅ Пример предоставлен выше -3. ✅ Предыдущий случай объяснен (требование уже реализовано) -4. ⚠️ Если хотите это в каждом ответе — можно сделать стандартом (подтвердите) ---- -``` - -## Применение: -Это правило Required для всех коммуникаций AI в проекте. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/command-synchronization.mdc b/.cursor/backup/rules/doc/command-synchronization.mdc deleted file mode 100644 index 69bab2fe..00000000 --- a/.cursor/backup/rules/doc/command-synchronization.mdc +++ /dev/null @@ -1,264 +0,0 @@ -# Command Synchronization System - -## Overview - -Automated system for synchronizing user Commands between multiple sources: -- `USER_CommandS.md` - User-friendly Command reference -- `.cursor/rules/cursor-export/ai-memory.mdc` - AI assistant instructions -- `.cursor/rules/ai-memory.mdc` - Cursor rules -- `CURSOR_AI_MEMORY_BANK.md` - Export for Cursor AI memory-bank - -## How It Works - -### Single Source of Truth -All Commands are defined in `.cursor/rules/Command-sync..js` in a structured format: - -```JavaScript -const CommandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Save контекст сессии'], - description: 'Save achieveme.ts, decisions and plans to memory-bank', - userDescription: 'AI-ассистент сохранит все достижения, решения и планы в memory-bank' - } - } -} -``` - -### Automatic Synchronization -The script generates all files from the single source: - -```bash -# Sync all files -node .cursor/rules/Command-sync..js sync - -# Export for Cursor AI memory-bank -node .cursor/rules/Command-sync..js export - -# Translate context to English -node .cursor/rules/Command-sync..js translate-context -``` - -## File Form.ts - -### USER_CommandS.md -User-friendly format with emojis and detailed descriptions: -```markdown -## 📝 Сохранение контекста - -### Save контекст сессии -`Сохрани контекст` / `Save контекст сессии` -*AI-ассистент сохранит все достижения, решения и планы в memory-bank* -``` - -### ai-memory.mdc -AI-optimized format for .cursor rules: -```markdown -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -### CURSOR_AI_MEMORY_BANK.md -Format optimized for Cursor AI memory-bank: -```markdown -### 📝 Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -## Context Translation System - -### Automatic Context Translation -The system automatically translates context to English for AI/LLM compatibility: - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only -``` - -### Features -- ✅ **Automatic translation** from Russian to English -- ✅ **backup creation** before translation -- ✅ **Git integration** with automatic comm.ts -- ✅ **Comprehensive coverage** of all context terminology -- ✅ **Error handling** with safe fallbacks - -### Translation Coverage -- **Headers and titles** - All section headers -- **Task status** - Completed tasks, current focus, next steps -- **Principles** - Working principles and guIDElines -- **Technical context** - Architecture and standards -- **Command system** - Command categories and descriptions -- **User experience** - UX priorities and metrics -- **Development plans** - Short, medium, and long-term goals -- **Status indicators** - Readiness and completion status - -## Integration with Cursor AI Memory-Bank - -### Manual Integration -1. Run export Command: - ```bash - node .cursor/rules/Command-sync..js export - ``` - -2. Copy content from `CURSOR_AI_MEMORY_BANK.md` - -3. Go to Cursor Settings → AI → Rules & Memories - -4. Paste into User Rules or Project Rules - -5. Save and restart Cursor - -### Automatic Integration (Future) -Planned features: -- Direct API integration with Cursor -- Automatic updates when Commands change -- Version control for AI memory-bank - -## Command Categories - -### 📝 Context and Memory -- `save context` - Save achieveme.ts, decisions and plans in English (automatic translation) -- `update progress` - Update project status files -- `restore context` - Restore full project understanding -- `quick restore` - Get brief summary - -### 🏗️ Analysis and Study -- `analyze architecture` - Study system patterns -- `study plugins` - Analyze plugin structure -- `check build` - Verify project builds -- `update documentation` - Update project docs - -### 🔧 Development -- `create plugin [name]` - Create new plugin -- `check code` - Run linting and type checking -- `run te.ts` - Run all project te.ts -- `check dependencies` - Verify dependencies - -### 📊 Project Management -- `bump version patch/minor/major` - Increase version -- `clean project` - Clean build artifa.ts -- `analyze performance` - Performance analysis -- `check security` - Security analysis - -### 🚀 Releases and Deployment -- `create release` - Prepare for release -- `build production` - Production build - -## Benef.ts - -### For Users -- ✅ **Consistent Commands** across all sources -- ✅ **Easy to use** - just copy and paste -- ✅ **Bilingual support** - English and Russian -- ✅ **Always up-to-date** - automatic synchronization - -### For AI Assista.ts -- ✅ **Structured format** - easy to parse -- ✅ **Clear descriptions** - understand what each Command does -- ✅ **Multiple sources** - available in all conte.ts -- ✅ **Semantic understanding** - works with any language - -### For Development -- ✅ **Single source of truth** - no duplication -- ✅ **Automatic updates** - change once, update everywhere -- ✅ **Version control** - track Command changes -- ✅ **Easy maintenance** - centralized management - -## Usage examples - -### Adding New Commands -1. Edit `.cursor/rules/Command-sync..js` -2. Add Command to appropriate category -3. Run sync Command: - ```bash - node .cursor/rules/Command-sync..js sync - ``` - -### Updating Existing Commands -1. Edit Command in `.cursor/rules/Command-sync..js` -2. Run sync Command -3. All files updated automatically - -### Exporting for Cursor -```bash -node .cursor/rules/Command-sync..js export -``` - -## Technical Details - -### File Structure -``` -.cursor/rules/ -├── Command-sync..js # Main synchronization script -├── ai-memory.mdc # Cursor rules (auto-generated) -├── cursor-export/ -│ └── ai-memory.mdc # AI assistant instructions (auto-generated) -└── doc/ - └── Command-synchronization.mdc # This documentation - -USER_CommandS.md # User reference (auto-generated) -CURSOR_AI_MEMORY_BANK.md # Cursor export (auto-generated) -``` - -### Script Functions -- `generateUserCommand.md()` - Generate USER_CommandS.md -- `generateAIMemor.md()` - Generate ai-memory.mdc files -- `generateCursorMemoryBank()` - Generate Cursor format -- `syncCommands()` - Sync all files -- `exportForCursor()` - Export for Cursor AI memory-bank - -### Command Structure -Each Command has: -- **English Command** - Primary Command -- **Russian alternatives** - User-friendly alternatives -- **Description** - Technical description for AI -- **User description** - User-friendly description - -## Future Enhanceme.ts - -### Planned Features -- [ ] **API Integration** - Direct Cursor API integration -- [ ] **Auto-sync** - Automatic sync on file changes -- [ ] **Command validation** - Validate Command syntax -- [ ] **Usage analytics** - Track Command usage -- [ ] **template system** - Command templates for different project types - -### Integration IDEas -- **Git hooks** - Auto-sync on commit -- **CI/CD integration** - Validate Command consistency -- **Plugin system** - Extensible Command categories -- **Multi-language support** - More languages beyond English/Russian - -## Troubleshooting - -### Common Issues -1. **Sync fails** - Check file permissions -2. **Commands not working** - Verify Cursor AI memory-bank integration -3. **Format issues** - Check Command structure in script - -### Debug Commands -```bash -# Check script help -node .cursor/rules/Command-sync..js help - -# Verify file generation -ls -la USER_CommandS.md CURSOR_AI_MEMORY_BANK.md -``` - -## Conclusion - -The Command synchronization system provIDEs: -- ✅ **Consistent experience** across all platforms -- ✅ **Easy maintenance** with single source of truth -- ✅ **Automatic updates** for all Command sources -- ✅ **AI-optimized format** for maximum compatibility -- ✅ **User-friendly interface** for easy adoption - -This system ensures that Commands work sea.lessLy across USER_CommandS.md, .cursor rules, and Cursor AI memory-bank settings. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/context-translation-system.mdc b/.cursor/backup/rules/doc/context-translation-system.mdc deleted file mode 100644 index a1183df8..00000000 --- a/.cursor/backup/rules/doc/context-translation-system.mdc +++ /dev/null @@ -1,206 +0,0 @@ -# Context Translation System - -## Overview - -The Context Translation System automatically translates context files from Russian to English for maximum AI/LLM compatibility. This ensures that all context saved in memory-bank is accessible to any AI assistant, regar.less of language preferences. - -## Key Features - -### 🌍 **Automatic Translation** -- **Sea.less translation** from Russian to English -- **Comprehensive coverage** of all context terminology -- **Preserves structure** and formatting -- **Creates backups** before translation - -### 🔄 **Integrated with Save Context Command** -- **Automatic translation** when using "save context" Command -- **Git integration** with automatic comm.ts -- **backup creation** for safety -- **Error handling** for robust operation - -### 📁 **backup System** -- **Automatic backups** before translation -- **Timestamped files** in `memory-bank/core/backup/` -- **Easy restoration** if needed -- **Safe operation** with rollback capability - -## Usage - -### For AI Assista.ts - -When a user issues the Command "save context" or "Сохрани контекст": - -1. **Automatically translate** context to English -2. **Create backups** of original files -3. **Save translated files** to memory-bank -4. **Commit changes** to git with descriptive message -5. **Notify user** of completion - -### Manual Usage - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only - -# Translate context using Command sync script -node .cursor/rules/Command-sync..js translate-context -``` - -## Translation Coverage - -### **Headers and Titles** -- `Active контекст разработки` → `Active Development Context` -- `Текущий Status проекта` → `Current Project Status` -- `Последнее обновление` → `Last Updated` - -### **Task Status** -- `Завершенные задачи` → `Completed Tasks` -- `Текущий фокус` → `Current Focus` -- `Следующие шаги` → `Next Steps` - -### **Principles and GuIDElines** -- `Ключевые Principles работы` → `Key Working Principles` -- `Инициативность ассистента` → `Assistant Initiative` -- `Quality кода` → `Code Quality` - -### **Technical Context** -- `Технический контекст` → `Technical Context` -- `Текущая Architecture` → `Current Architecture` -- `Standards разработки` → `Development Standards` - -### **Command System** -- `Система команд` → `Command System` -- `Автоматическая Synchronization` → `Automatic Synchronization` -- `Категории команд` → `Command Categories` - -### **User Experience** -- `User Experience` → `User Experience` -- `Priorityы UX` → `UX Priorities` -- `Метрики качества` → `Quality Metrics` - -### **Development Plans** -- `Планы развития` → `Development Plans` -- `Краткосрочные цели` → `Short-term Goals` -- `Mediumсрочные цели` → `Medium-term Goals` -- `Долгосрочные цели` → `Long-term Goals` - -### **Status and Readiness** -- `Status готовности` → `Readiness Status` -- `Готовые компоненты` → `Ready Compone.ts` -- `Готово к публикации` → `Ready for publication` - -## File Structure - -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Main context file (English) -│ ├── progress.md # Progress tracking (English) -│ └── backup/ # backup directory -│ ├── activeContext-2024-07-19T10-30-00-000Z.md -│ └── progress-2024-07-19T10-30-00-000Z.md -``` - -## Scri.ts - -### **save-context..js** -Main script for saving context with automatic translation: -- Translates context to English -- Creates backups -- Comm.ts changes to git -- ProvIDEs error handling - -### **Command-sync..js** -Extended with translation functionality: -- `translate-context` Command -- Integrated translation function -- Comprehensive translation mappings - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read context -- **Language consistency** - All context in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - Automatic comm.ts with clear messages -- **Error handling** - Robust operation with fallbacks - -## Best Practices - -### **For AI Assista.ts** -1. **Always use "save context"** Command for context preservation -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English context** for all AI operations - -### **For Users** -1. **Use Russian Commands** - translation happens automatically -2. **Check backup directory** if you need original files -3. **Trust the system** - translations are accurate and comprehensive -4. **Report issues** if translation problems occur - -### **For Developers** -1. **Extend translation mappings** as needed -2. **Test translations** before deployment -3. **Maintain backup system** for safety -4. **Update documentation** when adding new terms - -## Error Handling - -### **Translation Errors** -- **backup preservation** - Original files always saved -- **Partial translation** - System continues with available mappings -- **Error reporting** - Clear messages about issues -- **Manual fallback** - Option to restore from backup - -### **Git Errors** -- **Translation still works** - Files translated even if commit fails -- **Manual commit option** - User can commit manually -- **Clear error messages** - Specific information about git issues -- **Safe operation** - No data loss in case of git problems - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages - -### **Potential Improveme.ts** -- **Real-time translation** during editing -- **Translation quality scoring** -- **User feedback system** for translations -- **Integration with more AI tools** - -## Conclusion - -The Context Translation System ensures that all context in memory-bank is accessible to any AI assistant by automatically translating content to English. This creates a truly international and AI-compatible documentation system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic translation process** -- ✅ **Safe backup system** -- ✅ **Git integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling** - -**Usage:** Simply use the "save context" Command, and the system will automatically translate and save your context in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/cursor-protection-system.mdc b/.cursor/backup/rules/doc/cursor-protection-system.mdc deleted file mode 100644 index 9170030c..00000000 --- a/.cursor/backup/rules/doc/cursor-protection-system.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Cursor Protection System - -## Overview - -The Cursor Protection System automatically ensures that all files in the `.cursor` directory are written in English for maximum AI/LLM compatibility. This system provIDEs comprehensive protection through automatic translation, Git hooks, and monitoring. - -## Key Features - -### 🛡️ **Automatic Protection** -- **Real-time translation** of all .cursor files to English -- **Comprehensive coverage** of technical terminology -- **Preserves structure** and formatting -- **Creates backups** before any changes - -### 🔧 **Git Integration** -- **Pre-commit hooks** - automatically translate staged .cursor files -- **Post-commit hooks** - verify protection status after comm.ts -- **Pre-push hooks** - block push if Russian content is detected -- **Automatic staging** of translated files - -### 📊 **Monitoring and Status** -- **Status checking** - IDEntify files that need translation -- **Protection repo.ts** - comprehensive status overview -- **backup management** - easy restoration from backups -- **Integration status** - verify all compone.ts are working - -## System Compone.ts - -### **1. Cursor Protector (`cursor-protector..js`)** -Main translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **File scanning** - recursive directory processing -- **backup creation** - automatic backup before translation -- **Status reporting** - detailed analysis of files - -### **2. Git Hooks (`cursor-git-hook..js`)** -Automatic protection during Git operations: -- **Pre-commit** - translate staged .cursor files -- **Post-commit** - verify overall protection status -- **Pre-push** - block push if Russian content found -- **Hook management** - install/uninstall hooks - -### **3. Protection Manager (`protect-cursor..js`)** -Complete system management: -- **Full protection** - translate, install hooks, commit -- **System installation** - setup all compone.ts -- **Status checking** - comprehensive protection report -- **backup restoration** - restore from backups - -## Usage - -### **Complete Protection (Recommended)** -```bash -# Protect all .cursor files, install hooks, and commit -node .cursor/rules/protect-cursor..js protect -``` - -### **System Installation** -```bash -# Install protection system (hooks, scri.ts, .cursorignore) -node .cursor/rules/protect-cursor..js install -``` - -### **Status Checking** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Check .cursor files specifically -node .cursor/rules/cursor-protector..js check -``` - -### **Manual Protection** -```bash -# Protect all .cursor files -node .cursor/rules/cursor-protector..js protect - -# Install Git hooks -node .cursor/rules/cursor-git-hook..js install -``` - -### **Package.json Scri.ts** -After installation, use npm scri.ts: -```bash -npm run protect-cursor # Complete protection -npm run check-cursor # Check status -npm run install-cursor-hooks # Install hooks -``` - -## Translation Coverage - -### **Technical Terminology** -- **Development terms** - Development → development -- **Architecture terms** - Architecture → architecture -- **Security terms** - Security → security -- **Performance terms** - Performance → performance -- **Quality terms** - Quality → quality - -### **File Structure** -- **Headers and titles** - заголовки и названия -- **Section names** - названия разделов -- **Status indicators** - индикаторы Statusа -- **Action descriptions** - описания действий - -### **Common Phrases** -- **Important notes** - важные Notes -- **Best practices** - Best Practices -- **Requireme.ts** - Requireme.ts -- **Recommendations** - Recommendations - -## Git Hook Behavior - -### **Pre-commit Hook** -1. **Scans staged files** for .cursor files -2. **Dete.ts Russian content** in staged files -3. **Automatically translates** files with Russian content -4. **Creates backups** before translation -5. **Stages translated files** for commit - -### **Post-commit Hook** -1. **Checks overall .cursor status** -2. **Repo.ts any remaining Russian content** -3. **Sugge.ts actions** if needed -4. **Confirms protection status** - -### **Pre-push Hook** -1. **Scans all .cursor files** -2. **Blocks push** if Russian content found -3. **ProvIDEs clear error message** -4. **Sugge.ts protection Command** - -## File Structure - -``` -.cursor/ -├── rules/ -│ ├── cursor-protector..js # Main translation engine -│ ├── cursor-git-hook..js # Git hooks -│ ├── protect-cursor..js # Protection manager -│ └── doc/ -│ └── cursor-protection-system.mdc # This documentation -├── backup/ # backup directory -│ ├── pre-commit/ # Pre-commit backups -│ └── [timestamped-backups]/ # Manual backups -└── [other .cursor files] # Protected files -``` - -## configuration - -### **.cursorignore** -Exclude files from automatic translation: -``` -# Cursor Protection System -# Files that should not be automatically translated - -# backup files -.cursor/backup/ - -# temporary files -*.tmp -*.temp - -# Log files -*.log - -# cache files -.cache/ -node_modules/ -``` - -### **Package.json Scri.ts** -Automatically added during installation: -``.json -{ - "scri.ts": { - "protect-cursor": "node .cursor/rules/protect-cursor..js protect", - "check-cursor": "node .cursor/rules/cursor-protector..js check", - "install-cursor-hooks": "node .cursor/rules/cursor-git-hook..js install" - } -} -``` - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - any AI assistant can read .cursor files -- **Language consistency** - all files in English -- **Better understanding** - clear terminology for AI processing -- **Reduced confusion** - no mixed language content - -### **For International Community** -- **Global accessibility** - ready for international developers -- **Standardized format** - consistent English documentation -- **Easy sharing** - no language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - no manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - sea.less workflow integration -- **Error prevention** - blocks problematic comm.ts/pushes - -## Best Practices - -### **For Users** -1. **Run complete protection** after any .cursor changes -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English for new files** - maintain consistency - -### **For AI Assista.ts** -1. **Always write .cursor files in English** -2. **Trust the protection system** - it handles translation -3. **Check protection status** if unsure -4. **Use standard terminology** from translation mappings - -### **For Developers** -1. **Install protection system** in new proje.ts -2. **Extend translation mappings** as needed -3. **Test protection** before deployment -4. **Maintain backup system** for safety - -## Error Handling - -### **Translation Errors** -- **backup preservation** - original files always saved -- **Partial translation** - system continues with available mappings -- **Error reporting** - clear messages about issues -- **Manual fallback** - option to restore from backup - -### **Git Hook Errors** -- **Translation still works** - files translated even if hooks fail -- **Manual protection option** - user can run protection manually -- **Clear error messages** - specific information about issues -- **Safe operation** - no data loss in case of hook problems - -### **System Errors** -- **Graceful Degradation** - system continues with available features -- **Error logging** - detailed error information -- **Recovery options** - multiple ways to restore functionality -- **User guidance** - clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Files not being translated** -1. Check if files are in `.cursorignore` -2. Verify file extensions (.md, .mdc) -3. Run manual protection: `node .cursor/rules/cursor-protector..js protect` - -#### **Git hooks not working** -1. Check hook installation: `node .cursor/rules/cursor-git-hook..js install` -2. Verify hook permissions (should be executable) -3. Check for confli.ts with other hooks - -#### **Translation quality issues** -1. Check backup files for original content -2. Extend translation mappings if needed -3. Report missing terms for system improvement - -### **Debug Commands** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Test translation on specific file -node .cursor/rules/cursor-protector..js protect - -# Verify Git hooks -ls -la .git/hooks/ - -# Check backup files -ls -la .cursor/backup/ -``` - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during editing -- **Translation quality scoring** - -### **Integration IDEas** -- **IDE plugins** for real-time protection -- **CI/CD integration** for automated protection -- **Webhook system** for remote protection -- **Collaborative translation** for community contributions - -## Conclusion - -The Cursor Protection System ensures that all `.cursor` files are automatically maintained in English for maximum AI/LLM compatibility. This creates a truly international and AI-compatible configuration system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic protection process** -- ✅ **Safe backup system** -- ✅ **Git workflow integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling and recovery** - -**Usage:** Simply run `node .cursor/rules/protect-cursor..js protect` to activate complete protection, and the system will automatically maintain all `.cursor` files in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/documentation-map.mdc b/.cursor/backup/rules/doc/documentation-map.mdc deleted file mode 100644 index da6492bd..00000000 --- a/.cursor/backup/rules/doc/documentation-map.mdc +++ /dev/null @@ -1,187 +0,0 @@ -# Documentation Map - Карта назначения файлов - -## Команда "задокументируй" - четкие правила размещения - -### 🎯 **Универсальные правила и best practices → `.cursor/rules/`** - -**Критерии для `.cursor/rules/`:** -- ✅ Применимо к любым проектам -- ✅ Проверенные Patterns и подходы -- ✅ Автоматизация и workflow -- ✅ Standards разработки -- ✅ Documentation и структуры - -**Файлы назначения:** -- `dev/dev-principle-*.mdc` - Principles разработки -- `dev/development-principles.mdc` - универсальные Principles разработки -- `dev/date-time-patterns.mdc` - Patterns работы с Dateми -- `workflow/ci-automation.mdc` - CI/CD автоматизация -- `workflow/experience-transfer.mdc` - перенос опыта -- `doc/ai-first.mdc` - Standards документации -- `ui/ui-*.mdc` - UI/UX Standards -- `plugin/plugin-*.mdc` - Standards плагинов -- `security/validation.mdc` - правила безопасности -- `architecture/architecture-*.mdc` - архитектурные Principles -- `architecture/file-relationships.mdc` - Patterns организации файлов - -### 🧠 **Проектно-специфичный контекст → `memory-bank/`** - -**Критерии для `memory-bank/`:** -- ✅ Уникален для данного проекта -- ✅ Текущий Status и прогресс -- ✅ История ошибок и решений -- ✅ Принятые архитектурные решения -- ✅ Планы развития проекта - -**Файлы назначения:** -- `errors.md` - кладбище ошибок и решений -- `progress.md` - прогресс разработки -- `activeContext.md` - текущий контекст -- `architecture-decisions.md` - принятые решения -- `future-plans.md` - планы развития -- `testing-resu.ts.md` - результаты тестирования -- `sIDE-panel-improveme.ts.md` - улучшения UI -- `version-management.md` - управление версиями - -## 📋 **Детальная карта по Typeам контента** - -### **Errors and solutions:** -``` -Type ошибки → Файл назначения -├── build/TypeScript → memory-bank/errors.md -├── Runtime/UI → memory-bank/errors.md -├── Архитектурная → memory-bank/architecture-decisions.md -├── Security → .cursor/rules/security/validation.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── UI/UX → .cursor/rules/ui/ui-*.mdc -``` - -### **Best practices:** -``` -Type практики → Файл назначения -├── Development → .cursor/rules/dev/dev-principle-*.mdc -├── Documentation → .cursor/rules/doc/ai-first.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── Git workflow → .cursor/rules/workflow/branches.mdc -└── Plugins → .cursor/rules/plugin/plugin-*.mdc -``` - -### **Автоматизация:** -``` -Type автоматизации → Файл назначения -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── build → .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── Деплой → .cursor/rules/workflow/automation.mdc -└── Monitoring → .cursor/rules/architecture/architecture-observability.mdc -``` - -### **Architectural decisions:** -``` -Type решения → Файл назначения -├── Проектно-специфичное → memory-bank/architecture-decisions.md -├── Универсальное → .cursor/rules/architecture/architecture-*.mdc -├── Security → .cursor/rules/architecture/architecture-security.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── Plugins → .cursor/rules/architecture/architecture-plugin.mdc -``` - -## 🔄 **Workflow команды "задокументируй"** - -### **1. Анализ контента:** -```bash -# Определить Type контента -- Error/решение? -- Best practice? -- Автоматизация? -- Архитектурное решение? -- Проектно-специфичное? -``` - -### **2. Выбор файла назначения:** -```bash -# По карте выше определить правильный файл -# Check существование файла -# Create если не существует -``` - -### **3. Структурированное добавление:** -```bash -# Для .cursor/rules/: -- Add метаdata (description, globs, alwaysApply) -- Структурировать по Categoryм -- Add cross-references - -# Для memory-bank/: -- Add дату и контекст -- Связать с существующими записями -- Update индексы -``` - -### **4. Validation:** -```bash -# Check: -- Нет дублирования -- Правильная структура -- Актуальные ссылки -- Соответствие стандартам -``` - -## 🚫 **Запрещенные действия:** - -### **НЕ размещать в `.cursor/rules/`:** -- ❌ Проектно-специфичные ошибки -- ❌ Текущий Status проекта -- ❌ История конкретных решений -- ❌ Планы развития проекта -- ❌ Результаты тестирования - -### **НЕ размещать в `memory-bank/`:** -- ❌ Универсальные правила -- ❌ Best practices для всех проектов -- ❌ Автоматизация и workflow -- ❌ Standards документации -- ❌ UI/UX Standards - -## 📊 **examples правильного размещения:** - -### **Пример 1: Error сборки TypeScript** -``` -Проблема: "Failed to resolve entry for package '@extension/vite-config'" -Решение: "Перевести пакет на ESM-only, main: dist/index..js" - -Размещение: memory-bank/errors.md -Причина: Проектно-специфичная Error сборки -``` - -### **Пример 2: Best practice для ESM пакетов** -``` -Практика: "Использовать только ESM-экспорт для внутренних пакетов" -Обоснование: "Упрощает сборку, устраняет ошибки Vite/esbuild" - -Размещение: .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -Причина: Универсальная практика для всех проектов -``` - -### **Пример 3: CI/CD автоматизация** -``` -Автоматизация: "GitHub Actions для проверки .cursor rules" -Workflow: "Verification структуры, метаданных, дубликатов" - -Размещение: .cursor/rules/workflow/ci-automation.mdc -Причина: Универсальная автоматизация -``` - -## ✅ **Результат:** - -Четкая карта назначения файлов обеспечивает: -- **Нет дублирования** - каждый Type контента в правильном месте -- **Нет рассинхронизации** - четкие критерии размещения -- **Легкий поиск** - структурированная организация -- **Эффективный перенос** - только универсальные правила в .cursor -- **Консистентность** - единообразные подходы -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/internationalization-complete.mdc b/.cursor/backup/rules/doc/internationalization-complete.mdc deleted file mode 100644 index fe05f562..00000000 --- a/.cursor/backup/rules/doc/internationalization-complete.mdc +++ /dev/null @@ -1,162 +0,0 @@ -# Internationalization Complete - .cursor and memory-bank - -## Overview - -All `.cursor` and `memory-bank` directories have been completely translated to English for maximum international compatibility with AI assista.ts and LLMs. - -## What Was Translated - -### .cursor/rules Directory -- ✅ **ai-memory.mdc** - User Commands and AI instructions -- ✅ **ai-index.mdc** - AI-optimized rules index -- ✅ **README.mdc** - Main documentation -- ✅ **All subdirectories** - architecture/, dev/, doc/, plugin/, security/, ui/, workflow/ -- ✅ **cursor-export/** - All exported rules and standards - -### memory-bank Directory -- ✅ **README.md** - Main memory bank documentation -- ✅ **INDEX.md** - File structure and navigation -- ✅ **MEMORY_BANK_STRUCTURE.md** - Organization rules -- ✅ **All subdirectories** - core/, errors/, architecture/, development/, ui/, planning/, context/ -- ✅ **All individual files** - activeContext.md, progress.md, errors.md, etc. - -## Translation Approach - -### Universal Command Format -Commands now support both English and Russian: -```markdown -- `save context` / `save session context` - Save achieveme.ts, decisions and plans to memory-bank -- `update progress` / `update project progress` - Update activeContext.md and progress.md files with current status -``` - -### AI Semantic Understanding -- AI and LLMs understand Commands semantically regar.less of language -- English serves as the primary language for international compatibility -- Russian alternatives are preserved for user convenience - -## Benef.ts - -### For International Community -- 🌍 **100% English compatibility** - Any developer can use your .cursor -- 🤖 **AI/LLM optimized** - Commands work with any AI assistant -- 📚 **Standardized patterns** - Consistent Command structure -- 🔄 **Future-proof** - Ready for global sharing and collaboration - -### For You -- ✅ **Russian Commands still work** - No disruption to your workflow -- ✅ **Bilingual support** - Can use either language -- ✅ **AI understands both** - Semantic recognition works for both languages - -### For AI Assista.ts -- 🎯 **Clear Command patterns** - Predictable structure -- 🌐 **Language agnostic** - Understands meaning, not just words -- 📖 **Comprehensive documentation** - All rules in English - -## Technical Implementation - -### Translation Script -Created `.cursor/rules/translate-to-english..js` for automated translation: -```bash -node .cursor/rules/translate-to-english..js -``` - -### Translation Mappings -Comprehensive dictionary of Russian → English translations: -- Metadata descriptions -- Common phrases -- File content -- Navigation eleme.ts - -### Quality Assurance -- ✅ All files translated -- ✅ Structure preserved -- ✅ Functionality maintained -- ✅ Metadata updated - -## Usage examples - -### English Commands (Primary) -```bash -save context -update progress -restore context -analyze architecture -create plugin my-plugin -audit cursor -export cursor -``` - -### Russian Commands (Alternative) -```bash -сохрани контекст -обнови прогресс -восстанови контекст -анализируй архитектуру -создай плагин my-plugin -аудит cursor -экспорт cursor -``` - -## File Structure - -### .cursor/rules/ -``` -.cursor/rules/ -├── ai-memory.mdc # User Commands (English + Russian) -├── ai-index.mdc # Rules index (English) -├── README.mdc # Main documentation (English) -├── architecture/ # Architecture rules (English) -├── dev/ # Development principles (English) -├── doc/ # Documentation standards (English) -├── plugin/ # Plugin standards (English) -├── security/ # Security rules (English) -├── ui/ # UI/UX standards (English) -├── workflow/ # Workflow rules (English) -└── translate-to-english..js # Translation script -``` - -### memory-bank/ -``` -memory-bank/ -├── README.md # Main documentation (English) -├── INDEX.md # File structure (English) -├── MEMORY_BANK_STRUCTURE.md # Organization rules (English) -├── core/ # Core context files (English) -├── errors/ # Error documentation (English) -├── architecture/ # Architecture decisions (English) -├── development/ # Development process (English) -├── ui/ # UI/UX context (English) -├── planning/ # Planning docume.ts (English) -└── context/ # Contextual information (English) -``` - -## Next Steps - -### For Sharing -1. **GitHub Repository** - Ready for international community -2. **Documentation** - All in English for global understanding -3. **Commands** - Universal format for any AI assistant - -### For Development -1. **Continue using Russian Commands** - They still work perfectly -2. **Gradually adopt English** - For international collaboration -3. **Maintain bilingual support** - Best of both worlds - -### For AI Assista.ts -1. **Use English as primary** - For international compatibility -2. **Understand Russian alternatives** - For user convenience -3. **Maintain semantic understanding** - Language-agnostic processing - -## Conclusion - -The internationalization is complete and provIDEs: -- ✅ **Full English compatibility** for global community -- ✅ **Preserved Russian support** for your workflow -- ✅ **AI-optimized structure** for any AI assistant -- ✅ **Future-ready architecture** for international collaboration - -Your `.cursor` and `memory-bank` are now ready for the global AI development community! 🌍🤖 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/universal-commands.mdc b/.cursor/backup/rules/doc/universal-commands.mdc deleted file mode 100644 index fcd83722..00000000 --- a/.cursor/backup/rules/doc/universal-commands.mdc +++ /dev/null @@ -1,133 +0,0 @@ -# Universal Commands Approach - -## 🎯 **Цель: Универсальность для международного сообщества** - -### **Проблема:** -- Команды на русском языке ограничивают международное Usage -- Другие разработчики не могут использовать ваш .cursor -- Отсутствие стандартизации команд - -### **Решение:** -- **Английский как основной язык** команд -- **Русский как альтернатива** для удобства -- **AI понимает смысл** независимо от языка - -## 📋 **Формат универсальных команд:** - -### **Структура:** -``` -`english Command` / `русская команда` / `альтернативная русская команда` - English description -``` - -### **examples:** -```markdown -- `create memory entry` / `создай запись в memory-bank` - Create new entry with automatic categorization -- `update context` / `обнови контекст` - Update activeContext.md with current status -- `restore context` / `восстанови контекст` - Restore full context from memory-bank -``` - -## 🌍 **Преимущества универсального подхода:** - -### **1. Международная Compatibility:** -- ✅ Любой разработчик может использовать ваш .cursor -- ✅ Стандартизация команд для сообщества -- ✅ Легкость понимания и адаптации - -### **2. AI-Optimization:** -- ✅ AI понимает смысл независимо от языка -- ✅ Единообразные Patterns команд -- ✅ Предсказуемое поведение - -### **3. Долгосрочная перспектива:** -- ✅ Не нужно Translationить при публикации -- ✅ Compatibility с будущими версиями Cursor -- ✅ Масштабируемость для новых команд - -## 🔧 **Практическое применение:** - -### **Для вас (русскоязычный пользователь):** -```bash -# Вы можете использовать любую команду: -"создай запись в memory-bank" # Русская команда -"create memory entry" # Английская команда -``` - -### **Для международного сообщества:** -```bash -# Другие разработчики используют английские команды: -"create memory entry" -"update context" -"restore context" -``` - -### **Для AI:** -```bash -# AI понимает смысл и выполняет одинаково: -"создай запись в memory-bank" → create memory entry -"create memory entry" → create memory entry -``` - -## 📊 **Статистика совместимости:** - -### **Поддерживаемые языки:** -- ✅ **Английский** - основной язык команд -- ✅ **Русский** - альтернативный язык для удобства -- ✅ **Любой другой** - AI понимает по смыслу - -### **Охват аудитории:** -- 🌍 **100% международное сообщество** - английские команды -- 🇷🇺 **Русскоязычные разработчики** - русские команды -- 🤖 **AI ассистенты** - понимают любой язык - -## 🚀 **Recommendations по использованию:** - -### **1. При создании новых команд:** -```markdown -# ✅ Правильно: -- `new Command` / `новая команда` - English description - -# ❌ Неправильно: -- `новая команда` - Russian description only -``` - -### **2. При документировании:** -```markdown -# ✅ Правильно: -## Commands for AI Assistant Recognition -- `create memory entry` / `создай запись в memory-bank` - Create new entry - -# ❌ Неправильно: -## Команды для распознавания AI-ассистентом -- `создай запись в memory-bank` - Create новую запись -``` - -### **3. При публикации:** -- Используйте английский как основной язык -- Сохраняйте русские альтернативы для удобства -- Добавляйте описания на английском - -## 📈 **Планы развития:** - -### **Краткосрочные цели:** -- ✅ Перевести все команды на универсальный формат -- ✅ Update документацию -- ✅ Протестировать с международным сообществом - -### **Долгосрочные цели:** -- 🌍 Стандартизация команд для Cursor сообщества -- 📚 Создание Libraries универсальных команд -- 🔄 Автоматическая миграция существующих .cursor - -## ✅ **Результат:** - -**Универсальный подход обеспечивает:** -- 🌍 **Международную Compatibility** - любой разработчик может использовать -- 🤖 **AI-оптимизацию** - понимание независимо от языка -- 📈 **Масштабируемость** - легко добавлять новые команды -- 🔄 **Обратную Compatibility** - ваши русские команды продолжают работать - -**Ваш .cursor теперь готов для международного сообщества!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/memory-bank/INDEX.md b/.cursor/backup/rules/memory-bank/INDEX.md deleted file mode 100644 index 0eee849e..00000000 --- a/.cursor/backup/rules/memory-bank/INDEX.md +++ /dev/null @@ -1,65 +0,0 @@ -# Memory Bank - React-TypeScript Project - -## Project Information - -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Last Updated:** 2025-07-19 - -## Categories - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- State-management.md - Управление Stateм -- component-structure.md - Структура компонентов -- routing.md - Маршрутизация - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- component-library.md - Библиотека компонентов -- styling-patterns.md - Patterns стилизации -- responsive-design.md - Адаптивный Design - -### 📅 [Planning](./planning/) - Planning -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Contextual information -- tech-stack.md - Технический стек -- dependencies.md - Dependencies проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- `создай запись в memory-bank` - Create новую запись -- `обнови контекст` - Update Active контекст -- `восстанови контекст` - Восстановить полный контекст -- `аудит memory-bank` - Провести аудит - ---- -*Auto-generated for React-TypeScript project on 2025-07-19* diff --git a/.cursor/backup/rules/memory-bank/architecture/README.md b/.cursor/backup/rules/memory-bank/architecture/README.md deleted file mode 100644 index 21d38ba9..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Files in this category: - -- [component-structure](./component-structure.md) -- [decisions](./decisions.md) -- [patterns](./patterns.md) -- [routing](./routing.md) -- [State-management](./State-management.md) - -## Description: - -Принятые архитектурные решения с обоснованием. - -## AI Commands: - -- `добавь в architecture` - Add запись в эту категорию -- `обнови architecture` - Update файлы в категории -- `покажи architecture` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/component-structure.md b/.cursor/backup/rules/memory-bank/architecture/component-structure.md deleted file mode 100644 index 6626b3ce..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/component-structure.md +++ /dev/null @@ -1,12 +0,0 @@ -# component structure - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/decisions.md b/.cursor/backup/rules/memory-bank/architecture/decisions.md deleted file mode 100644 index 720d2530..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/decisions.md +++ /dev/null @@ -1,12 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Overview - -This file contains architectural decisions and rationale. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/patterns.md b/.cursor/backup/rules/memory-bank/architecture/patterns.md deleted file mode 100644 index ae9baf41..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# Design Patterns - React-TypeScript - -## Overview - -This file contains design patterns and best practices. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/routing.md b/.cursor/backup/rules/memory-bank/architecture/routing.md deleted file mode 100644 index a252858e..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/routing.md +++ /dev/null @@ -1,12 +0,0 @@ -# routing - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/state-management.md b/.cursor/backup/rules/memory-bank/architecture/state-management.md deleted file mode 100644 index d7ecf603..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/state-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# State management - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/README.md b/.cursor/backup/rules/memory-bank/context/README.md deleted file mode 100644 index 374553e6..00000000 --- a/.cursor/backup/rules/memory-bank/context/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Context Information - React-TypeScript - -## Files in this category: - -- [dependencies](./dependencies.md) -- [deployment](./deployment.md) -- [environment](./environment.md) -- [tech-stack](./tech-stack.md) - -## Description: - -Технический и продуктовый контекст, окружение разработки. - -## AI Commands: - -- `добавь в context` - Add запись в эту категорию -- `обнови context` - Update файлы в категории -- `покажи context` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/dependencies.md b/.cursor/backup/rules/memory-bank/context/dependencies.md deleted file mode 100644 index 6caba601..00000000 --- a/.cursor/backup/rules/memory-bank/context/dependencies.md +++ /dev/null @@ -1,12 +0,0 @@ -# Dependencies - React-TypeScript - -## Overview - -This file contains project dependencies and versions. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/deployment.md b/.cursor/backup/rules/memory-bank/context/deployment.md deleted file mode 100644 index 23fa5567..00000000 --- a/.cursor/backup/rules/memory-bank/context/deployment.md +++ /dev/null @@ -1,12 +0,0 @@ -# deployment - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/environment.md b/.cursor/backup/rules/memory-bank/context/environment.md deleted file mode 100644 index 54b83b38..00000000 --- a/.cursor/backup/rules/memory-bank/context/environment.md +++ /dev/null @@ -1,12 +0,0 @@ -# Environment - React-TypeScript - -## Overview - -This file contains development environment setup. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/tech-stack.md b/.cursor/backup/rules/memory-bank/context/tech-stack.md deleted file mode 100644 index 1d83f76a..00000000 --- a/.cursor/backup/rules/memory-bank/context/tech-stack.md +++ /dev/null @@ -1,12 +0,0 @@ -# Tech Stack - React-TypeScript - -## Overview - -This file contains technology stack and tools. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/INDEX.md b/.cursor/backup/rules/memory-bank/core/INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/backup/rules/memory-bank/core/INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/backup/rules/memory-bank/core/README.md b/.cursor/backup/rules/memory-bank/core/README.md deleted file mode 100644 index a3b68a3e..00000000 --- a/.cursor/backup/rules/memory-bank/core/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Core Files - React-TypeScript - -## Files in this category: - -- [INDEX](./INDEX.md) -- [activeContext](./activeContext.md) -- [migrated-INDEX](./migrated-INDEX.md) -- [progress](./progress.md) -- [projectbrief](./projectbrief.md) -- [session-log](./session-log.md) - -## Description: - -Критически важные файлы для понимания текущего состояния проекта. - -## AI Commands: - -- `добавь в core` - Add запись в эту категорию -- `обнови core` - Update файлы в категории -- `покажи core` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/activeContext.md b/.cursor/backup/rules/memory-bank/core/activeContext.md deleted file mode 100644 index 8c429166..00000000 --- a/.cursor/backup/rules/memory-bank/core/activeContext.md +++ /dev/null @@ -1,36 +0,0 @@ -# Active Context - React-TypeScript - -## Current Status - -**Last Updated:** 2025-07-19 -**Project Type:** React-TypeScript -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/general.md b/.cursor/backup/rules/memory-bank/core/general.md deleted file mode 100644 index 71e787bc..00000000 --- a/.cursor/backup/rules/memory-bank/core/general.md +++ /dev/null @@ -1,31 +0,0 @@ -# General - core - -## Overview - -This file contains core-related information and entries. - -## Entries - - - ---- -*Auto-generated file* - -## [2025-07-19 01:56:13] - New Entry - -**Type:** test -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #universal #Commands #testing - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md b/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/backup/rules/memory-bank/core/progress.md b/.cursor/backup/rules/memory-bank/core/progress.md deleted file mode 100644 index fd42cfe2..00000000 --- a/.cursor/backup/rules/memory-bank/core/progress.md +++ /dev/null @@ -1,31 +0,0 @@ -# Progress - React-TypeScript - -## Overview - -This file contains development progress and milestones. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* - -## [2025-07-19 01:47:05] - New Entry - -**Type:** progress -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #memory-bank #cursor #automation - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/backup/rules/memory-bank/core/projectbrief.md b/.cursor/backup/rules/memory-bank/core/projectbrief.md deleted file mode 100644 index 04f2e336..00000000 --- a/.cursor/backup/rules/memory-bank/core/projectbrief.md +++ /dev/null @@ -1,33 +0,0 @@ -# Project Brief - React-TypeScript - -## Project Overview - -**Name:** [Project Name] -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requireme.ts - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constrai.ts - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/session-log.md b/.cursor/backup/rules/memory-bank/core/session-log.md deleted file mode 100644 index 35ff1fc3..00000000 --- a/.cursor/backup/rules/memory-bank/core/session-log.md +++ /dev/null @@ -1,12 +0,0 @@ -# Session Log - React-TypeScript - -## Overview - -This file contains development session logs. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/README.md b/.cursor/backup/rules/memory-bank/development/README.md deleted file mode 100644 index 25270007..00000000 --- a/.cursor/backup/rules/memory-bank/development/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Development Process - React-TypeScript - -## Files in this category: - -- [build-process](./build-process.md) -- [debugging-guIDE](./debugging-guIDE.md) -- [devtools-guIDE](./devtools-guIDE.md) -- [testing-resu.ts](./testing-resu.ts.md) -- [version-management](./version-management.md) - -## Description: - -Результаты тестирования, GuIDEs по отладке, Processes разработки. - -## AI Commands: - -- `добавь в development` - Add запись в эту категорию -- `обнови development` - Update файлы в категории -- `покажи development` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/build-process.md b/.cursor/backup/rules/memory-bank/development/build-process.md deleted file mode 100644 index 1a1ffcf4..00000000 --- a/.cursor/backup/rules/memory-bank/development/build-process.md +++ /dev/null @@ -1,12 +0,0 @@ -# build process - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/debugging-guide.md b/.cursor/backup/rules/memory-bank/development/debugging-guide.md deleted file mode 100644 index 5369fa55..00000000 --- a/.cursor/backup/rules/memory-bank/development/debugging-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# Debugging GuIDE - React-TypeScript - -## Overview - -This file contains debugging procedures and tips. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/devtools-guide.md b/.cursor/backup/rules/memory-bank/development/devtools-guide.md deleted file mode 100644 index c0d68911..00000000 --- a/.cursor/backup/rules/memory-bank/development/devtools-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# DevTools GuIDE - React-TypeScript - -## Overview - -This file contains development tools usage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/testing-results.md b/.cursor/backup/rules/memory-bank/development/testing-results.md deleted file mode 100644 index bf105272..00000000 --- a/.cursor/backup/rules/memory-bank/development/testing-results.md +++ /dev/null @@ -1,12 +0,0 @@ -# Testing Resu.ts - React-TypeScript - -## Overview - -This file contains testing outcomes and coverage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/version-management.md b/.cursor/backup/rules/memory-bank/development/version-management.md deleted file mode 100644 index 67b7e2f6..00000000 --- a/.cursor/backup/rules/memory-bank/development/version-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# Version Management - React-TypeScript - -## Overview - -This file contains version control and releases. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/README.md b/.cursor/backup/rules/memory-bank/errors/README.md deleted file mode 100644 index eca2654f..00000000 --- a/.cursor/backup/rules/memory-bank/errors/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Errors & Solutions - React-TypeScript - -## Files in this category: - -- [build-errors](./build-errors.md) -- [errors](./errors.md) -- [runtime-errors](./runtime-errors.md) -- [TypeScript-errors](./TypeScript-errors.md) -- [ui-errors](./ui-errors.md) - -## Description: - -Проектно-специфичные ошибки и их решения. - -## AI Commands: - -- `добавь в errors` - Add запись в эту категорию -- `обнови errors` - Update файлы в категории -- `покажи errors` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/build-errors.md b/.cursor/backup/rules/memory-bank/errors/build-errors.md deleted file mode 100644 index b0462c1e..00000000 --- a/.cursor/backup/rules/memory-bank/errors/build-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# build errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/errors.md b/.cursor/backup/rules/memory-bank/errors/errors.md deleted file mode 100644 index 33bd85eb..00000000 --- a/.cursor/backup/rules/memory-bank/errors/errors.md +++ /dev/null @@ -1,12 +0,0 @@ - -## [2025-07-19T01:19:27.379Z] - New Entry - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы. Это упрощает сборку, устраняет ошибки Vite/esbuild, ускоряет разработку и повышает Compatibility с современными инструментами. - ---- - -## [2025-07-19T01:19:22.935Z] - General Error - -Error сборки: Failed to resolve entry for package '@extension/vite-config'. Решение: перевести пакет на ESM-only, main: dist/index..js - ---- diff --git a/.cursor/backup/rules/memory-bank/errors/runtime-errors.md b/.cursor/backup/rules/memory-bank/errors/runtime-errors.md deleted file mode 100644 index c8b0744e..00000000 --- a/.cursor/backup/rules/memory-bank/errors/runtime-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# runtime errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/typescript-errors.md b/.cursor/backup/rules/memory-bank/errors/typescript-errors.md deleted file mode 100644 index 7f9bbbfb..00000000 --- a/.cursor/backup/rules/memory-bank/errors/typescript-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# TypeScript errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/ui-errors.md b/.cursor/backup/rules/memory-bank/errors/ui-errors.md deleted file mode 100644 index 19cf9b26..00000000 --- a/.cursor/backup/rules/memory-bank/errors/ui-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# ui errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc b/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc deleted file mode 100644 index de782b38..00000000 --- a/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Memory Bank Controller - Система управления memory-bank - -## 🎯 **Принцип: Memory-bank полностью подчинен .cursor правилам** - -### **Обоснование:** -- ✅ Оба каталога предназначены исключительно для AI -- ✅ Единая система правил и стандартов -- ✅ Автоматизация всех процессов -- ✅ Консистентность и предсказуемость -- ✅ Масштабируемость и поддерживаемость - -## 📋 **Структура управления memory-bank** - -### **1. Автоматическое создание структуры** -```bash -# Команда для создания структуры memory-bank -node cursor-manager..js create-memory-structure [project-type] - -# Поддерживаемые Typeы проектов: -- React-TypeScript # React + TypeScript проекты -- node-API # Node.js API проекты -- chrome-extension # Chrome Extension проекты -- monorepo # Монорепозитории -- fullstack # Full-stack приложения -``` - -### **2. Шаблоны структуры для разных Typeов проектов** - -#### **React TypeScript Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст React проекта -│ ├── progress.md # Development progress React компонентов -│ ├── projectbrief.md # Description React проекта -│ └── session-log.md # Лог сессий React разработки -├── errors/ -│ ├── errors.md # Ошибки React/TypeScript -│ ├── build-errors.md # Ошибки сборки Vite/Webpack -│ ├── runtime-errors.md # Runtime ошибки React -│ └── ui-errors.md # UI/UX ошибки -├── architecture/ -│ ├── decisions.md # Architectural decisions React -│ ├── patterns.md # React Patterns -│ ├── State-management.md # Управление Stateм -│ └── component-structure.md # Структура компонентов -├── development/ -│ ├── testing-resu.ts.md # Результаты тестирования React -│ ├── debugging-guIDE.md # Debugging React приложений -│ ├── devtools-guIDE.md # React DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── component-library.md # Библиотека компонентов -│ ├── styling-patterns.md # Patterns стилизации -│ └── responsive-design.md # Адаптивный Design -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── optimization-plans.md # Планы оптимизации -│ └── migration-plans.md # Планы миграции -├── context/ -│ ├── tech-stack.md # Технический стек -│ ├── dependencies.md # Dependencies проекта -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -#### **Chrome Extension Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст extension -│ ├── progress.md # Development progress extension -│ ├── projectbrief.md # Description extension -│ └── session-log.md # Лог сессий разработки -├── errors/ -│ ├── errors.md # Ошибки extension -│ ├── manifest-errors.md # Ошибки manifest.json -│ ├── permission-errors.md # Ошибки разрешений -│ └── API-errors.md # Ошибки Chrome APIs -├── architecture/ -│ ├── decisions.md # Architectural decisions -│ ├── background-scri.ts.md # Background scri.ts -│ ├── content-scri.ts.md # Content scri.ts -│ └── popup-structure.md # Структура popup -├── development/ -│ ├── testing-resu.ts.md # Testing extension -│ ├── debugging-guIDE.md # Debugging extension -│ ├── devtools-guIDE.md # Chrome DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── popup-design.md # Design popup -│ ├── options-page.md # Страница настроек -│ └── content-ui.md # UI в content scri.ts -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── store-publishing.md # Публикация в store -│ └── user-feedback.md # Обратная связь пользователей -├── context/ -│ ├── chrome-APIs.md # Используемые Chrome APIs -│ ├── permissions.md # Требуемые разрешения -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -## 🔄 **Автоматизация процессов memory-bank** - -### **1. Автоматическое создание записей** -```bash -# Команды для автоматического создания записей -node cursor-manager..js memory-add "content" --type=error --category=build -node cursor-manager..js memory-add "content" --type=decision --category=architecture -node cursor-manager..js memory-add "content" --type=progress --category=feature -``` - -### **2. Автоматическое обновление контекста** -```bash -# Обновление активного контекста -node cursor-manager..js memory-update-context --auto -node cursor-manager..js memory-update-progress --auto -node cursor-manager..js memory-update-session --auto -``` - -### **3. Автоматическое Recovery контекста** -```bash -# Recovery полного контекста -node cursor-manager..js memory-restore --full -node cursor-manager..js memory-restore --quick -node cursor-manager..js memory-restore --category=errors -``` - -## 📝 **Правила создания и сохранения контекста** - -### **1. Структура записей (унифицированная)** -```markdown -## [YYYY-MM-DD HH:MM:SS] - Краткое Description - -**Type:** error|decision|progress|context|plan -**Category:** build|runtime|architecture|ui|testing|planning -**Priority:** critical|high|medium|low - -**Контекст:** Description ситуации и обстоятельств - -**Детали:** Подробная информация о проблеме/решении - -**Решение/Результат:** Что было сделано и результат - -**Status:** ✅ Решено | 🔄 In Progress | ❌ Проблема | 📋 План - -**Связанные файлы:** -- .cursor/rules/dev/development-principles.mdc -- memory-bank/errors/build-errors.md - -**Теги:** #React #TypeScript #build #vite - -**AI Команды:** -- `аудит cursor` - для проверки .cursor правил -- `задокументируй` - для создания документации -``` - -### **2. Автоматические теги и категоризация** -```JavaScript -// Автоматическое определение тегов -const autoTags = { - 'React': /React.jsx|component|hook/i, - 'TypeScript': /TypeScript.ts|type|interface/i, - 'build': /build|compile|vite|webpack/i, - 'runtime': /runtime|error|crash|exception/i, - 'ui': /ui|ux|component|styling/i, - 'testing': /test|spec|jest|cypress/i, - 'architecture': /architecture|pattern|structure/i -}; -``` - -### **3. Автоматическое связывание с .cursor правилами** -```JavaScript -// Автоматическое создание связей -const ruleConnections = { - 'error': '.cursor/rules/dev/error-handling.mdc', - 'architecture': '.cursor/rules/architecture/patterns.mdc', - 'testing': '.cursor/rules/dev/testing-troubleshooting.mdc', - 'ui': '.cursor/rules/ui/ui-patterns.mdc', - 'build': '.cursor/rules/dev/build-troubleshooting.mdc' -}; -``` - -## 🤖 **AI Команды для управления memory-bank** - -### **1. Команды создания и обновления** -```bash -# Создание новой записи -"создай запись в memory-bank" / "add memory entry" -"добавь ошибку в memory-bank" / "add error to memory" -"запиши решение в memory-bank" / "record solution" - -# Обновление существующих записей -"обнови контекст" / "update context" -"обнови прогресс" / "update progress" -"обнови сессию" / "update session" -``` - -### **2. Команды восстановления и поиска** -```bash -# Recovery контекста -"восстанови контекст" / "restore context" -"восстанови полный контекст" / "restore full context" -"быстрое Recovery" / "quick restore" - -# Поиск информации -"найди в memory-bank" / "search memory" -"покажи ошибки" / "show errors" -"покажи решения" / "show solutions" -``` - -### **3. Команды управления структурой** -```bash -# Управление структурой -"создай структуру memory-bank" / "create memory structure" -"реорганизуй memory-bank" / "reorganize memory" -"очисти memory-bank" / "clean memory" -"аудит memory-bank" / "audit memory" -``` - -## 🔧 **Integration с существующими .cursor правилами** - -### **1. Обновление documentation-helper..js** -```JavaScript -// Add поддержку memory-bank команд -const memoryCommands = { - 'memory-add': 'Add запись в memory-bank', - 'memory-update': 'Update запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank' -}; -``` - -### **2. Обновление ai-memory.mdc** -```markdown -### Memory Bank Management: -- `создай запись в memory-bank` - Create новую запись с автоматической категоризацией -- `обнови контекст` - Update activeContext.md с текущим Statusом -- `восстанови контекст` - Восстановить полный контекст из memory-bank -- `аудит memory-bank` - Провести аудит и оптимизацию memory-bank -- `реорганизуй memory-bank` - Реорганизовать структуру по новым правилам -``` - -### **3. Обновление cursor-manager..js** -```JavaScript -// Add команды управления memory-bank -case 'memory-add': - await this.memoryAdd(options); - break; -case 'memory-update': - await this.memoryUpdate(options); - break; -case 'memory-restore': - await this.memoryRestore(options); - break; -case 'memory-audit': - await this.memoryAudit(options); - break; -``` - -## 📊 **Автоматические отчеты и аналитика** - -### **1. Отчеты по memory-bank** -```bash -# Генерация отчетов -node cursor-manager..js memory-report --type=errors -node cursor-manager..js memory-report --type=progress -node cursor-manager..js memory-report --type=full -``` - -### **2. Аналитика использования** -```JavaScript -// Метрики memory-bank -const metrics = { - totalEntries: 0, - entriesByCategory: {}, - entriesByType: {}, - recentActivity: [], - mostReferencedRules: [], - errorResolutionRate: 0 -}; -``` - -## ✅ **Результат полной интеграции** - -### **Преимущества:** -- ✅ **Единая система правил** - все управляется через .cursor -- ✅ **Автоматизация** - Minimum ручного вмешательства -- ✅ **Консистентность** - единообразные форматы и структуры -- ✅ **Масштабируемость** - легко добавлять новые Typeы проектов -- ✅ **AI-Optimization** - все правила учитывают потребности AI -- ✅ **Воспроизводимость** - одинаковые результаты на разных проектах - -### **Workflow:** -1. **Создание проекта** → Автоматическое создание структуры memory-bank -2. **Development** → Автоматическое создание записей через AI команды -3. **Recovery контекста** → Автоматическое чтение и анализ memory-bank -4. **Аудит и Optimization** → Регулярные проверки и улучшения - -**Memory-bank теперь полностью интегрирован с .cursor правилами!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/memory-bank/planning/README.md b/.cursor/backup/rules/memory-bank/planning/README.md deleted file mode 100644 index d61ef5fc..00000000 --- a/.cursor/backup/rules/memory-bank/planning/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Planning & Roadmap - React-TypeScript - -## Files in this category: - -- [feature-roadmap](./feature-roadmap.md) -- [migration-plans](./migration-plans.md) -- [optimization-plans](./optimization-plans.md) -- [tech-debt](./tech-debt.md) - -## Description: - -Планы развития проекта, roadmap, стратегические решения. - -## AI Commands: - -- `добавь в planning` - Add запись в эту категорию -- `обнови planning` - Update файлы в категории -- `покажи planning` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md b/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md deleted file mode 100644 index 90ffb932..00000000 --- a/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md +++ /dev/null @@ -1,12 +0,0 @@ -# Feature Roadmap - React-TypeScript - -## Overview - -This file contains feature planning and roadmap. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/migration-plans.md b/.cursor/backup/rules/memory-bank/planning/migration-plans.md deleted file mode 100644 index 012f80e1..00000000 --- a/.cursor/backup/rules/memory-bank/planning/migration-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# migration plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/optimization-plans.md b/.cursor/backup/rules/memory-bank/planning/optimization-plans.md deleted file mode 100644 index a31a1b10..00000000 --- a/.cursor/backup/rules/memory-bank/planning/optimization-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# optimization plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/tech-debt.md b/.cursor/backup/rules/memory-bank/planning/tech-debt.md deleted file mode 100644 index 01cbe91c..00000000 --- a/.cursor/backup/rules/memory-bank/planning/tech-debt.md +++ /dev/null @@ -1,12 +0,0 @@ -# tech debt - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/README.md b/.cursor/backup/rules/memory-bank/ui/README.md deleted file mode 100644 index 47639357..00000000 --- a/.cursor/backup/rules/memory-bank/ui/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# UI/UX Context - React-TypeScript - -## Files in this category: - -- [accessibility](./accessibility.md) -- [component-library](./component-library.md) -- [performance](./performance.md) -- [responsive-design](./responsive-design.md) -- [styling-patterns](./styling-patterns.md) - -## Description: - -UI/UX решения и улучшения, User Experience. - -## AI Commands: - -- `добавь в ui` - Add запись в эту категорию -- `обнови ui` - Update файлы в категории -- `покажи ui` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/accessibility.md b/.cursor/backup/rules/memory-bank/ui/accessibility.md deleted file mode 100644 index e80643a1..00000000 --- a/.cursor/backup/rules/memory-bank/ui/accessibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# accessibility - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/component-library.md b/.cursor/backup/rules/memory-bank/ui/component-library.md deleted file mode 100644 index 5c32e785..00000000 --- a/.cursor/backup/rules/memory-bank/ui/component-library.md +++ /dev/null @@ -1,12 +0,0 @@ -# component library - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/performance.md b/.cursor/backup/rules/memory-bank/ui/performance.md deleted file mode 100644 index e7b7c8ed..00000000 --- a/.cursor/backup/rules/memory-bank/ui/performance.md +++ /dev/null @@ -1,12 +0,0 @@ -# performance - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/responsive-design.md b/.cursor/backup/rules/memory-bank/ui/responsive-design.md deleted file mode 100644 index d26e30f1..00000000 --- a/.cursor/backup/rules/memory-bank/ui/responsive-design.md +++ /dev/null @@ -1,12 +0,0 @@ -# responsive design - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/styling-patterns.md b/.cursor/backup/rules/memory-bank/ui/styling-patterns.md deleted file mode 100644 index 05d60aae..00000000 --- a/.cursor/backup/rules/memory-bank/ui/styling-patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# styling patterns - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/workflow/ci-automation.mdc b/.cursor/backup/rules/workflow/ci-automation.mdc deleted file mode 100644 index 202310ac..00000000 --- a/.cursor/backup/rules/workflow/ci-automation.mdc +++ /dev/null @@ -1,85 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# CI/CD Automation & Safe Delete Practices - -## GitHub Actions Workflow для .cursor Rules - -Add `.github/workflows/rules-check.yml`: -``.yaml -name: Rules Structure Check -on: - push: - paths: - - '.cursor/rules/**' - pull_request: - paths: - - '.cursor/rules/**' -jobs: - check-rules: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '18' - - name: Generate index and README structure - run: node .cursor/rules/cursor-manager..js full - - name: Check for uncommitted changes - run: | - git diff --exit-code || (echo 'Rules structure is outdated. Please run cursor-manager..js full and commit the result.' && exit 1) -``` - -## Safe Delete Protection - -### Защищенные директории: -- `docs/for-ai-best-practices` -- `memory-bank` -- `platform-core` -- `chrome-extension/public/plugins` -- `.cursor/rules` - -### GitHub Actions Verification: -``.yaml -- name: Check for protected directory deletion - run: | - if git diff --name-status | grep -E 'D[[:space:]]+(docs/for-ai-best-practices|memory-bank|platform-core|chrome-extension/public/plugins|\.cursor/rules)'; then - echo '::warning::Attempted deletion of a protected directory! Review required.' - fi -``` - -## Automated PR Checks с Danger.js - -- Verification детального описания PR -- Verification обновления changelog при изменении кода -- Требование cross-references к правилам -- Напоминание Update документацию при изменении src/core - -## Automated Releases с semantic-release - -- Анализ conventional comm.ts -- Автоматическое обновление CHANGELOG.md -- Создание git tags и релизов -- Запуск автоматически при мерже в main - -## Automated Dependency Updates с Renovate - -- Создание PR с группированными обновлениями -- Все обновления проходят CI и danger.js -- PR помечаются labels: dependencies, renovate -- Automerge отключен (можно включить при желании) - -## Automated Security Audit - -- `pnpm audit` на каждом push/PR -- Отчет о vulnerabilities (moderate и выше) в CI -- Быстрый ответ на критические проблемы -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/workflow/experience-transfer.mdc b/.cursor/backup/rules/workflow/experience-transfer.mdc deleted file mode 100644 index 443713ac..00000000 --- a/.cursor/backup/rules/workflow/experience-transfer.mdc +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: experience transfer rule -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: falseaiPriority: medium -aiCategory: process-management ---- - -# Experience Transfer & Project Bo.tstrap - -## Автоматизация переноса опыта - -**Автоматизация** — это когда ассистент не просто даёт Tipы, а сам выполняет все технические шаги по внедрению best practices и автоматизаций: - -### Что автоматизируется: -- Автоматически копирует и адаптирует конфиги (husky, danger.js, semantic-release, commitlint, renovate, CI/CD и т.д.) -- Устанавливает необходимые npm-Packages и настраивает их -- Добавляет шаблоны PR, чек-листы, хуки, скрипты -- Вносит изменения в package.json, .github/workflows, .husky/ и другие служебные файлы -- Генерирует или обновляет документацию -- Проводит аудит проекта и сообщает о несоответствиях или необходимых доработках - -### Команда для автоматизации: -``` -Внедри все best practices и автоматизации из .cursor/rules в этот проект, установи необходимые Dependencies, настрой CI/CD, husky, danger.js, semantic-release, commitlint, renovate и т.д. Проведи аудит и сообщи, если что-то требует ручной доработки. -``` - -## Быстрый старт для нового проекта - -### 1. Экспорт из исходного проекта: -```bash -cd .cursor/rules -node cursor-manager..js export new-project-name -``` - -### 2. Копирование в новый проект: -```bash -cp -r cursor-export /path/to/new-project/ -``` - -### 3. Импорт в новый проект: -```bash -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### 4. Кастомизация: -```bash -cd .cursor/rules -node cursor-manager..js full -``` - -### 5. Аудит результата: -```bash -node cursor-manager..js status -node cursor-manager..js audit -``` - -## Команды для AI: -- `экспорт cursor` - Export правила для переноса -- `импорт cursor` - Import правила в текущий проект -- `внедри best practices` - Внедрить все автоматизации и Standards -- `аудит проекта` - Провести аудит на соответствие best practices - -## Результат: -- Быстрый старт новых проектов -- Консистентность между проектами -- Автоматизация рутинных задач -- Улучшенное AI-ассистирование -- Стандартизация процессов разработки -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/index.mdc b/.cursor/index.mdc old mode 100644 new mode 100755 diff --git a/.cursor/rules/AUTOMATION.md b/.cursor/rules/AUTOMATION.md deleted file mode 100644 index 25f94ef3..00000000 --- a/.cursor/rules/AUTOMATION.md +++ /dev/null @@ -1,205 +0,0 @@ -# .cursor Automation System - -Комплексная система automation для аудита, исправления и оптимизации каталога `.cursor` для лучшего понимания AI и Cursor. - -## 🎯 **Purpose** - -Система automation обеспечивает: -- **Автоматический аудит** каталога `.cursor` на предмет проблем -- **Исправление** найденных проблем (дубликаты, сломанные ссылки, метаdata) -- **Оптимизацию** правил специально для AI и Cursor -- **Monitoring** состояния и качества правил - -## 🛠️ **Компоненты системы** - -### **1. CursorAuditor** (`audit-cursor..js`) -- Сканирование всех файлов в `.cursor/rules` -- Verification метаданных и их валидности -- Поиск дублирующего контента -- Verification сломанных ссылок -- Validation структуры каталога -- Генерация подробных отчетов - -### **2. CursorFixer** (`fix-cursor..js`) -- Автоматическое исправление метаданных -- Удаление дублирующих файлов -- Исправление сломанных ссылок -- Обновление индексов - -### **3. AIOptimizer** (`optimize-for-ai..js`) -- Optimization метаданных для AI -- Добавление AI-специфичных тегов -- Purpose Priorityов и категорий -- Создание AI-оптимизированных индексов - -### **4. CursorManager** (`cursor-manager..js`) -- Главный Interface для всех операций -- Управление workflow -- Генерация отчетов о Statusе -- CLI Interface - -## 🚀 **Usage** - -### **Базовые команды:** - -```bash -# Показать справку -node cursor-manager..js help - -# Check Status -node cursor-manager..js status - -# Запустить аудит -node cursor-manager..js audit - -# Применить исправления -node cursor-manager..js fix - -# Оптимизировать для AI -node cursor-manager..js optimize - -# Полный workflow -node cursor-manager..js full -``` - -### **Опции:** - -```bash -#.json вывод для аудита -node cursor-manager..js audit -.json - -# Пропустить аудит перед исправлениями -node cursor-manager..js fix --no-audit-first - -# Пропустить аудит после оптимизации -node cursor-manager..js optimize --no-audit-after -``` - -## 📊 **Что проверяет аудит** - -### **Файлы и структура:** -- Общее Quantity файлов -- Соотношение `.mdc` и `.md` файлов -- Наличие обязательных директорий -- Наличие обязательных файлов - -### **Метаdata:** -- Наличие.yaml frontmatter -- Валидность полей `description`, `globs`, `alwaysApply` -- AI-специфичные поля `aiPriority`, `aiCategory` - -### **Контент:** -- Дублирующий контент между файлами -- Сломанные ссылки в документации -- Консистентность структуры - -### **AI-Optimization:** -- Priorityы правил (critical, high, medium, normal) -- Категории (system-design, development-practices, etc.) -- AI-специфичные теги и Comme.ts - -## 🤖 **AI-специфичные оптимизации** - -### **Priorityы:** -- **critical**: Применяется ко всему коду и решениям -- **high**: Применяется к большинству задач -- **medium**: Применяется когда релевантно -- **normal**: Применяется когда уместно - -### **Категории:** -- **system-design**: Architecture и структура системы -- **development-practices**: Standards кодирования -- **documentation**: Documentation и Communication -- **plugin-development**: Правила для плагинов -- **security**: Security -- **user-interface**: UI/UX Standards -- **process-management**: Workflow и Processes -- **ai-optimization**: AI-специфичные оптимизации - -## 📈 **Отчеты и метрики** - -### **Статистика:** -- Общее Quantity файлов -- Файлы с/без метаданных -- Quantity проблем -- Процент AI-готовности - -### **Проблемы:** -- Дублирующий контент -- Сломанные ссылки -- Невалидные метаdata -- Отсутствующие файлы/директории - -### **Recommendations:** -- Конвертация `.md` в `.mdc` -- Добавление метаданных -- Удаление дубликатов -- Исправление ссылок - -## 🔄 **Workflow** - -### **Полный workflow (`full`):** -1. **Аудит** - поиск проблем -2. **Исправления** - автоматическое решение -3. **Optimization** - AI-специфичные улучшения -4. **Финальный аудит** - Verification результатов -5. **Отчет** - сравнение до/после - -### **Инкрементальный workflow:** -- `audit` → анализ проблем -- `fix` → исправление -- `optimize` → Optimization -- `status` → Monitoring - -## 📝 **Integration с AI** - -### **Автоматические команды:** -Система интегрирована с AI memory-bank через команды: -- `аудит cursor` / `cursor audit` -- `исправь cursor` / `cursor fix` -- `оптимизируй cursor` / `cursor optimize` -- `полный cursor` / `cursor full` -- `Status cursor` / `cursor status` - -### **AI-оптимизированные файлы:** -- `ai-optimization.mdc` - инструкции для AI -- `ai-index.mdc` - индекс по Priorityам -- AI-теги в каждом файле -- Priorityы и категории в метаданных - -## 🎯 **Результаты** - -После полной оптимизации: -- ✅ Все файлы имеют правильные метаdata -- ✅ Нет дублирующего контента -- ✅ Все ссылки работают -- ✅ AI-специфичные оптимизации применены -- ✅ Priorityы и категории назначены -- ✅ Автоматическое применение критических правил - -## 🔧 **Расширение системы** - -### **Добавление новых проверок:** -1. Create Method в `CursorAuditor` -2. Add в основной workflow -3. Update отчеты - -### **Добавление новых исправлений:** -1. Create Method в `CursorFixer` -2. Интегрировать в процесс исправления -3. Add в отчеты - -### **Добавление новых оптимизаций:** -1. Create Method в `AIOptimizer` -2. Add AI-специфичную логику -3. Update валидацию - -## 📚 **Связанные файлы** - -- `audit-cursor..js` - Аудит системы -- `fix-cursor..js` - Исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - Главный Interface -- `ai-optimization.mdc` - AI инструкции -- `ai-index.mdc` - AI индекс -- `ai-memory.mdc` - Команды для AI \ No newline at end of file diff --git a/.cursor/rules/EXPERIENCE-TRANSFER.md b/.cursor/rules/EXPERIENCE-TRANSFER.md deleted file mode 100644 index 190ccf3d..00000000 --- a/.cursor/rules/EXPERIENCE-TRANSFER.md +++ /dev/null @@ -1,279 +0,0 @@ -# Experience Transfer Best Practices - -Руководство по переносу опыта работы с Cursor и AI между проектами через систему `.cursor`. - -## 🎯 **Почему `.cursor` лучше `docs/for-ai-best-practices`** - -### **Автоматическое применение** -- ✅ Cursor автоматически читает все файлы из `.cursor/rules/` -- ✅ Правила применяются без дополнительной Settings -- ✅ AI получает контекст сразу при создании чата - -### **Стандартизация** -- ✅ `.cursor` - стандартное место для правил Cursor -- ✅ Все разработчики знают, где искать правила -- ✅ Единообразие между проектами - -### **Структурированность** -- ✅ Четкая иерархия (architecture, dev, doc, plugin, etc.) -- ✅ Метаdata с Priorityами и Categoryми -- ✅ Автоматическая Validation и Optimization - -## 🚀 **Стратегия переноса опыта** - -### **1. Экспорт из исходного проекта** - -```bash -# Из папки .cursor/rules исходного проекта -node cursor-manager..js export - -# С указанием целевого проекта -node cursor-manager..js export my-new-project -``` - -**Создается папка `cursor-export/` с:** -- Все правила по Categoryм -- Скрипты automation -- Инструкции по импорту -- Автоматический скрипт импорта - -### **2. Импорт в целевой проект** - -```bash -# Скопировать cursor-export в целевой проект -cp -r cursor-export /path/to/new-project/ - -# Запустить импорт -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### **3. Кастомизация для нового проекта** - -```bash -# Перейти в .cursor/rules -cd .cursor/rules - -# Запустить полную оптимизацию -node cursor-manager..js full - -# Check Status -node cursor-manager..js status -``` - -## 📋 **Что переносится** - -### **Core Rules (обязательные)** -- `ai-memory.mdc` - команды для AI (кастомизировать!) -- `environment.mdc` - Constrai.ts окружения (кастомизировать!) -- `index.mdc` - индекс правил -- `README.mdc` - Documentation структуры - -### **Categories (по необходимости)** -- **architecture/** - архитектурные правила -- **dev/** - Principles разработки -- **doc/** - Standards документации -- **plugin/** - правила для плагинов -- **security/** - правила безопасности -- **ui/** - UI/UX Standards -- **workflow/** - Processes разработки - -### **Automation (Required)** -- `audit-cursor..js` - аудит системы -- `fix-cursor..js` - автоматические исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - главный Interface - -## 🔧 **Кастомизация для нового проекта** - -### **1. Update environment.mdc** -``.mdc ---- -description: Critical environment limitations -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: environment ---- - -# Environment Constrai.ts - -## Node.js Version -- Required: Node.js 18+ (Update для вашего проекта) - -## Browser Support -- Chrome: 120+ (Update для вашего проекта) -- Firefox: 115+ (Update для вашего проекта) - -## build Tools -- Vite: 5+ (Update для вашего проекта) -- TypeScript: 5+ (Update для вашего проекта) -``` - -### **2. Update ai-memory.mdc** -``.mdc ---- -description: User Commands and AI instructions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Memory Bank - -## Project-Specific Commands -- `build project` - Собрать проект (Update команды) -- `test project` - Запустить тесты (Update команды) -- `deploy project` - Деплой проекта (Update команды) - -## Project Context -- This is a [Type проекта] (Update Description) -- Main technology stack: [стек] (Update стек) -- Key features: [особенности] (Update особенности) -``` - -### **3. Update monorepo-best-practices.mdc** -``.mdc ---- -description: Monorepo structure and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Monorepo Best Practices - -## Project Structure -``` -project/ -├── packages/ # (Update структуру) -├── apps/ # (Update структуру) -├── tools/ # (Update структуру) -└── docs/ # (Update структуру) -``` - -## Package Management -- Package manager: pnpm (Update если нужно) -- Workspace configuration: pnpm-workspace.yaml (Update) -``` - -## 📊 **Workflow переноса** - -### **Полный цикл:** -1. **Экспорт** из исходного проекта -2. **Копирование** в целевой проект -3. **Импорт** с автоматической настройкой -4. **Кастомизация** под новый проект -5. **Validation** и Optimization -6. **Testing** работы AI - -### **Команды для AI:** -```bash -# Экспорт -экспорт cursor -экспорт cursor [проект] - -# Импорт (в целевом проекте) -импорт cursor - -# Verification -Status cursor -полный cursor -``` - -## 🎯 **Best Practices** - -### **1. Инкрементальный перенос** -- Начните с core rules -- Добавляйте категории по мере необходимости -- Тестируйте каждую категорию - -### **2. Кастомизация обязательна** -- Всегда обновляйте `environment.mdc` -- Адаптируйте `ai-memory.mdc` под проект -- Проверяйте актуальность правил - -### **3. Validation после импорта** -```bash -cd .cursor/rules -node cursor-manager..js status -node cursor-manager..js audit -node cursor-manager..js optimize -``` - -### **4. Documentation изменений** -- Ведите changelog изменений -- Комментируйте кастомизации -- Обновляйте README проекта - -### **5. Обратная Compatibility** -- Сохраняйте структуру категорий -- Не удаляйте обязательные файлы -- Тестируйте на разных проектах - -## 🔄 **Автоматизация** - -### **Скрипт быстрого переноса:** -```bash -#!/bin/bash -# quick-transfer.sh - -SOURCE_PROJECT=$1 -TARGET_PROJECT=$2 - -echo "🚀 Quick transfer from $SOURCE_PROJECT to $TARGET_PROJECT" - -# Экспорт из исходного проекта -cd $SOURCE_PROJECT/.cursor/rules -node cursor-manager..js export $TARGET_PROJECT - -# Копирование в целевой проект -cp -r cursor-export $TARGET_PROJECT/ - -# Импорт в целевой проект -cd $TARGET_PROJECT -node cursor-export/import-cursor..js - -# Очистка -rm -rf cursor-export - -echo "✅ Transfer completed!" -``` - -### **Git hooks для automation:** -```bash -# .git/hooks/post-merge -#!/bin/bash -if [ -d ".cursor/rules" ]; then - cd .cursor/rules - node cursor-manager..js status -fi -``` - -## 📈 **Метрики успеха** - -### **После переноса проверьте:** -- ✅ Все файлы имеют правильные метаdata -- ✅ AI команды работают в новом проекте -- ✅ Автоматизация функционирует -- ✅ Нет конфликтов с существующими правилами -- ✅ Performance AI не снизилась - -### **Долгосрочные метрики:** -- Скорость onboarding новых разработчиков -- Quality AI-ассистированной разработки -- Консистентность кода между проектами -- Time на настройку новых проектов - -## 🎉 **Результат** - -После правильного переноса: -- **Быстрый старт** новых проектов -- **Консистентность** между проектами -- **Автоматизация** рутинных задач -- **Улучшенное** AI-ассистирование -- **Стандартизация** процессов разработки - -**Система `.cursor` обеспечивает эффективный перенос опыта между проектами с минимальными усилиями!** 🚀 \ No newline at end of file diff --git a/.cursor/rules/README.mdc b/.cursor/rules/README.mdc deleted file mode 100644 index 60abfdb3..00000000 --- a/.cursor/rules/README.mdc +++ /dev/null @@ -1,117 +0,0 @@ - -# .cursor/rules: Project Rules & AI Onboarding - -This directory contains all rules and standards for automation and AI assista.ts in the Agent Plugins Platform. - -## Principles -- Each rule is a separate .mdc file (one rule — one file) -- Grouped by topic: development principles, architecture, plugin standards, UI/UX, workflow, documentation, etc. -- Each file contains: a short description, globs, the rule .tself, and (optionally) examples or related links -- All rules are in English for international compatibility -- README and index.mdc help with navigation and search - -## CLI Scri.ts - -- **create-rule.js** — Interactive CLI to create a new .mdc rule file from a template. Prom.ts for all required fields and updates index/README automatically. - - Usage: `node .cursor/rules/create-rule.js` -- **generate-rules-index.js** — Scans all .mdc files and regenerates `index.mdc` and the structure section in `README.md`. - - Usage: `node .cursor/rules/generate-rules-index.js` -- **check-rules-structure.js** — Validates all .mdc files for required sections, uniqueness, and valid related links. Used in CI. - - Usage: `node .cursor/rules/check-rules-structure.js` - -## Current Structure -``` -.cursor/ - rules/ - # Root level - critical rules - ai-memory.mdc # User Commands and AI instructions - environment.mdc # Critical environment limitations - monorepo-best-practices.mdc # Monorepo structure and guIDElines - TypeScript-build-troubleshooting.mdc # TypeScript build error solutions - README.mdc # Main documentation - index.mdc # Rules index and navigation - - architecture/ # System architecture rules - architecture-chat-system.mdc - architecture-error-handling.mdc - architecture-observability.mdc - architecture-performance.mdc - architecture-plugin.mdc - architecture-project-structure.mdc - architecture-security.mdc - architecture-workflow.mdc - project-architecture.mdc - - dev/ # Development principles and guIDElines - dev-principle-01-do-no-harm.mdc - dev-principle-03-best-practices.mdc - dev-principle-04-fail-fast-safe.mdc - dev-principle-05-observability.mdc - dev-principle-06-config-as-code.mdc - dev-principle-07-progressive-enhancement.mdc - dev-principle-08-data-integrity-privacy.mdc - dev-principle-09-continuous-learning.mdc - dev-principle-10-ecosystem-thinking.mdc - development-guIDElines.mdc - security-and-deployment.mdc - testing-and-debugging.mdc - TypeScript.mdc - git-workflow.mdc - - doc/ # Documentation and AI standards - ai-first.mdc # AI-oriented documentation standards - knowledge-map.mdc # Project knowledge structure - memorybank-quality.mdc # Memory-bank quality standards - restore-context.mdc # Context restoration procedures - .mdc-file-standards.mdc # .mdc file creation standards - ai-fallback.mdc # AI fallback procedures - - plugin/ # Plugin development standards - plugin-best-practices.mdc - plugin-documentation.mdc - plugin-error-handling.mdc - plugin-performance.mdc - plugin-security.mdc - plugin-structure.mdc - plugin-testing.mdc - - security/ # Security standards - validation.mdc # Input validation rules - - ui/ # UI/UX standards - ui-accessibility.mdc - ui-animation.mdc - ui-error-handling.mdc - ui-forms.mdc - ui-loading-States.mdc - ui-mobile.mdc - ui-navigation.mdc - ui-performance.mdc - ui-React-compone.ts.mdc - ui-styling.mdc - ui-testing.mdc - - workflow/ # Development workflow - automation.mdc - branches.mdc - workflow.mdc -``` - -## AI Fallback Rule - -If an AI agent cannot answer a question from .ts own memory-bank, it must first check the .rules directory, then the memory-bank directory. See [doc/ai-fallback.mdc](./doc/ai-fallback.mdc). - -## Main GitHub Repository - -HTTPS://github.com/LebedevIV/agent-plugins-platform-boilerplate - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name ->>>>>>> origin/develop - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name \ No newline at end of file diff --git a/.cursor/rules/ai-index.mdc b/.cursor/rules/ai-index.mdc deleted file mode 100644 index 823bcc83..00000000 --- a/.cursor/rules/ai-index.mdc +++ /dev/null @@ -1,89 +0,0 @@ ---- -description: AI-optimized index of rules by priority and category -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI-Optimized Rules Index - -## Critical Priority Rules -*Must be applied to all code and decisions* - -- [Ai Index](ai-index.mdc) — AI-optimized index of rules by priority and category -- [Ai Memory](ai-memory.mdc) — User Commands and AI instructions -- [Ai Optimization](ai-optimization.mdc) — AI-specific instructions and optimizations for .cursor rules -- [Ai Memory](cursor-export/ai-memory.mdc) — User Commands and AI instructions -- [Environment](cursor-export/environment.mdc) — Critical environment constrai.ts - Node.js version, popup vs sIDEpanel - -## High Priority Rules -*Should be applied to most code and decisions* - -- [Architecture Chat System](architecture/architecture-chat-system.mdc) — Architecture rule for chat-system -- [Architecture Error Handling](architecture/architecture-error-handling.mdc) — Architecture rule for error-handling -- [Architecture Observability](architecture/architecture-observability.mdc) — Architecture rule for observability -- [Architecture Performance](architecture/architecture-performance.mdc) — Architecture rule for performance -- [Architecture Plugin](architecture/architecture-plugin.mdc) — Architecture rule for plugin -- [Architecture Project Structure](architecture/architecture-project-structure.mdc) — Architecture rule for project-structure -- [Architecture Security](architecture/architecture-security.mdc) — Architecture rule for security -- [Architecture Workflow](architecture/architecture-workflow.mdc) — Architecture rule for workflow -- [Project Architecture](architecture/project-architecture.mdc) — Architecture rule for project-architecture -- [Dev Principle 01 Do No Harm](cursor-export/dev/dev-principle-01-do-no-harm.mdc) — Development principle harm - dev principle 01 do no harm -- [Dev Principle 03 Best Practices](cursor-export/dev/dev-principle-03-best-practices.mdc) — Development principle practices - dev principle 03 best practices -- [Dev Principle 04 Fail Fast Safe](cursor-export/dev/dev-principle-04-fail-fast-safe.mdc) — Development principle safe - dev principle 04 fail fast safe -- [Dev Principle 05 Observability](cursor-export/dev/dev-principle-05-observability.mdc) — Development principle observability - dev principle 05 observability -- [Dev Principle 06 config As Code](cursor-export/dev/dev-principle-06-config-as-code.mdc) — Development principle code - dev principle 06 config as code -- [Dev Principle 07 Progressive Enhancement](cursor-export/dev/dev-principle-07-progressive-enhancement.mdc) — Development principle enhancement - dev principle 07 progressive enhancement -- [Dev Principle 08 data Integrity Privacy](cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc) — Development principle privacy - dev principle 08 data integrity privacy -- [Dev Principle 09 Continuous Learning](cursor-export/dev/dev-principle-09-continuous-learning.mdc) — Development principle learning - dev principle 09 continuous learning -- [Dev Principle 10 Ecosystem Thinking](cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc) — Development principle thinking - dev principle 10 ecosystem thinking - -## Medium Priority Rules -*Apply when relevant to the task* - -- [Ai Fallback](cursor-export/doc/ai-fallback.mdc) — Fallback procedures for AI -- [Ai First](cursor-export/doc/ai-first.mdc) — AI-oriented documentation standards -- .mdc File Standards](cursor-export/doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [Workflow](cursor-export/workflow/workflow.mdc) — Workflow rule for -- [Ai Answer Self Check](doc/ai-answer-self-check.mdc) — ai answer self check rule -- [Ci Automation](workflow/ci-automation.mdc) — Automation and synchronization rules -- [Experience Transfer](workflow/experience-transfer.mdc) — experience transfer rule - -## Normal Priority Rules -*Apply when appropriate* - -- [README](README.mdc) — Main project rules documentation and AI onboarding - structure, CLI scri.ts, usage -- [Development GuIDElines](cursor-export/dev/development-guIDElines.mdc) — General development guIDElines -- [Git Workflow](cursor-export/dev/git-workflow.mdc) — Git workflow rule - only develop to main, mandatory PR for all changes -- [Security And Deployment](cursor-export/dev/security-and-deployment.mdc) — Security rule for -and-deployment -- [Testing And Debugging](cursor-export/dev/testing-and-debugging.mdc) — Testing and debugging standards -- [TypeScript](cursor-export/dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Knowledge Map](cursor-export/doc/knowledge-map.mdc) — Project knowledge structure -- [Memorybank Quality](cursor-export/doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](cursor-export/doc/restore-context.mdc) — Context restoration procedures -- [Index](cursor-export/index.mdc) — index rule -- [Monorepo Best Practices](cursor-export/monorepo-best-practices.mdc) — Best practices for monorepo work - structure, dependencies, TypeScript, barrel expo.ts -- [Plugin Best Practices](cursor-export/plugin/plugin-best-practices.mdc) — Plugin standard for best-practices -- [Plugin Documentation](cursor-export/plugin/plugin-documentation.mdc) — Plugin standard for documentation -- [Plugin Error Handling](cursor-export/plugin/plugin-error-handling.mdc) — Plugin standard for error-handling -- [Plugin Performance](cursor-export/plugin/plugin-performance.mdc) — Plugin standard for performance -- [Plugin Security](cursor-export/plugin/plugin-security.mdc) — Plugin standard for security -- [Plugin Structure](cursor-export/plugin/plugin-structure.mdc) — Plugin standard for structure -- [Plugin Testing](cursor-export/plugin/plugin-testing.mdc) — Plugin standard for testing -- [Validation](cursor-export/security/validation.mdc) — Input validation and data sanitization rules -- [TypeScript build Troubleshooting](cursor-export/TypeScript-build-troubleshooting.mdc) — TypeScript build error troubleshooting guIDE - types, barrel expo.ts, dependencies, Tailwin.css -- [Ui Accessibility](cursor-export/ui/ui-accessibility.mdc) — UI standard for accessibility -- [Ui Animation](cursor-export/ui/ui-animation.mdc) — UI standard for animation -- [Ui Error Handling](cursor-export/ui/ui-error-handling.mdc) — UI standard for error-handling -- [Ui Forms](cursor-export/ui/ui-forms.mdc) — UI standard for forms -- [Ui Loading States](cursor-export/ui/ui-loading-States.mdc) — UI standard for loading-States -- [Ui Mobile](cursor-export/ui/ui-mobile.mdc) — UI standard for mobile -- [Ui Navigation](cursor-export/ui/ui-navigation.mdc) — UI standard for navigation -- [Ui Performance](cursor-export/ui/ui-performance.mdc) — UI standard for performance -- [Ui React Compone.ts](cursor-export/ui/ui-React-compone.ts.mdc) — UI standard for React-compone.ts -- [Ui Styling](cursor-export/ui/ui-styling.mdc) — UI standard for styling -- [Ui Testing](cursor-export/ui/ui-testing.mdc) — UI standard for testing -- [Automation](cursor-export/workflow/automation.mdc) — Automation and synchronization rules -- [Branches](cursor-export/workflow/branches.mdc) — Git branch management rules -- [Testing Troubleshooting](dev/testing-troubleshooting.mdc) — Testing and debugging standards diff --git a/.cursor/rules/ai-memory.mdc b/.cursor/rules/ai-memory.mdc deleted file mode 100644 index c74c3224..00000000 --- a/.cursor/rules/ai-memory.mdc +++ /dev/null @@ -1,38 +0,0 @@ -# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/rules/ai-optimization.mdc b/.cursor/rules/ai-optimization.mdc deleted file mode 100644 index e7b985b1..00000000 --- a/.cursor/rules/ai-optimization.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: AI-specific instructions and optimizations for .cursor rules -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Optimization Instructions - -## How AI Should Use These Rules - -### Priority Levels -- **critical**: Must be applied to all code and decisions -- **high**: Should be applied to most code and decisions -- **medium**: Apply when relevant to the task -- **normal**: Apply when appropriate - -### Categories -- **system-design**: Architecture and system structure -- **development-practices**: Coding standards and principles -- **documentation**: Documentation and communication -- **plugin-development**: Plugin-specific rules -- **security**: Security and safety rules -- **user-interface**: UI/UX standards -- **process-management**: Workflow and process rules -- **ai-optimization**: AI-specific optimizations - -### Usage Patterns -1. Always check critical rules first -2. Apply high-priority rules to most tasks -3. Use category-specific rules for targeted tasks -4. Reference related rules when making decisions - -### AI Memory Integration -- These rules are automatically loaded into AI memory-bank -- Use `alwaysApply: true` rules without explicit reference -- Reference specific rules when explaining decisions -- Update rules when patterns change or improve - -## Optimization Status -- ✅ All rules have proper metadata -- ✅ AI-specific tags added -- ✅ Priority levels assigned -- ✅ Categories defined -- ✅ Structure optimized for AI understanding diff --git a/.cursor/rules/architecture/architecture-chat-system.mdc b/.cursor/rules/architecture/architecture-chat-system.mdc deleted file mode 100644 index 3eb5b78c..00000000 --- a/.cursor/rules/architecture/architecture-chat-system.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Chat System Architecture -- Per-page, per-plugin isolation: Ch.ts separated by page URL and plugin -- LRU cache: In-memory cache for performance (50 ch.ts) -- IndexedDB: Persistent storage for all ch.ts -- Real-time sync: Cross-tab communication for chat updates -- Cleanup: Automatic removal of ch.ts older than 90 days - -description: Chat system architecture and data flow -globs: - - platform-core/* - - chrome-extension/src/background/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-error-handling.mdc b/.cursor/rules/architecture/architecture-error-handling.mdc deleted file mode 100644 index 7aef574e..00000000 --- a/.cursor/rules/architecture/architecture-error-handling.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Error Handling (Architecture) -- Graceful Degradation: System continues working with reduced functionality -- User Feedback: Clear error messages and recovery options -- Logging: Structured logging for debugging and monitoring -- Fallbacks: Alternative implementations for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-error-handling.mdc - - architecture-security.mdc - -description: Error handling requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-observability.mdc b/.cursor/rules/architecture/architecture-observability.mdc deleted file mode 100644 index 1f47a11f..00000000 --- a/.cursor/rules/architecture/architecture-observability.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Monitoring and Observability (Architecture) -- Structured Logging: Consistent log format across compone.ts -- Performance Metrics: Track execution time and memory usage -- Error Tracking: Monitor and alert on critical errors -- User Analytics: Track feature usage and performance -- Health Checks: Monitor system health and dependencies - -related: - - architecture-performance.mdc - - dev-principle-05-observability.mdc - -description: Monitoring and observability requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-performance.mdc b/.cursor/rules/architecture/architecture-performance.mdc deleted file mode 100644 index 636e5dc1..00000000 --- a/.cursor/rules/architecture/architecture-performance.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Performance GuIDElines (Architecture) -- PyodIDE Loading: Lazy load runtime, cache resu.ts -- Memory Management: Clean up resources, monitor usage -- Bundle Size: Optimize for extension size lim.ts -- Caching: Use LRU cache for frequently accessed data -- Async Operations: Non-blocking UI, proper loading States - -related: - - plugin-performance.mdc - - architecture-observability.mdc - -description: Performance guIDElines for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-plugin.mdc b/.cursor/rules/architecture/architecture-plugin.mdc deleted file mode 100644 index 3ef2571d..00000000 --- a/.cursor/rules/architecture/architecture-plugin.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Plugin Architecture -- Manifest Structure: manifest.json + mcp_server.py +.icon.svg -- MCP Protocol: Standard MCP server implementation in Python -- Permission Model: Zero Trust - plugins not trusted by default -- Isolation: Each plugin in separate worker for security -- Communication: chrome.runtime.sendMessage for all cross-component communication - -description: Plugin architecture and isolation requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-project-structure.mdc b/.cursor/rules/architecture/architecture-project-structure.mdc deleted file mode 100644 index 6220eeac..00000000 --- a/.cursor/rules/architecture/architecture-project-structure.mdc +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Structure Architecture -- platform-core/: Main logic and services (keep boilerplate clean) -- chrome-extension/src/background/: Core background services -- pages/*/src/: UI compone.ts for different extension pages -- memory-bank/: Project documentation and context -- public/plugins/: Plugin implementations - -description: Directory and component structure for the project -globs: - - platform-core/* - - chrome-extension/src/background/* - - pages/*/src/* - - memory-bank/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-security.mdc b/.cursor/rules/architecture/architecture-security.mdc deleted file mode 100644 index 9e8d63a1..00000000 --- a/.cursor/rules/architecture/architecture-security.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Security Architecture -- Secret Manager: Encrypted storage of API keys in chrome.storage.local -- Network Guard: Whitelist domains for API calls -- Audit System: Log all plugin activities and network reque.ts -- Parameter Validation:.json Schema validation for all inp.ts -- Rate Limiting: Prevent abuse and suspicious activity - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Security architecture and controls -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-workflow.mdc b/.cursor/rules/architecture/architecture-workflow.mdc deleted file mode 100644 index 401f5e21..00000000 --- a/.cursor/rules/architecture/architecture-workflow.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Development Workflow (Architecture) -- Feature Branches: Always create from develop branch -- Testing: Test before commit, use --no-verify if ESLint issues -- Documentation: Update memory-bank for all architectural decisions -- Comm.ts: Use semantic commit messages -- Code Quality: TypeScript for new files, ESLint compliance - -description: Development workflow and process standards -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* - - memory-bank/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/file-relationships.mdc b/.cursor/rules/architecture/file-relationships.mdc deleted file mode 100644 index 2376e599..00000000 --- a/.cursor/rules/architecture/file-relationships.mdc +++ /dev/null @@ -1,339 +0,0 @@ ---- -description: File organization patterns and relationships - universal project structure guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Карта взаимосвязей файлов - -## File Structure и их Purpose - -### 1. Background Layer (Слой фоновых сервисов) - -#### `chrome-extension/src/background/plugin-chat-cache.ts` -**Purpose:** Центральный сервис управления чатами -**Dependencies:** -- `plugin-chat-API.ts` - для работы с IndexedDB -- Chrome Extensions API (chrome.runtime, chrome.tabs) - -**Экспортирует:** -- `PluginChatcache` class -- `Chatdata`, `ChatMessage` interfaces - -**Используется:** -- `background/index.ts` - создание экземпляра и регистрация обработчиков - -**Ключевые Methodы:** -```TypeScript -class PluginChatcache { - getChat(pluginId: string, pageKey: string): Promise - saveMessage(pluginId: string, pageKey: string, message: ChatMessage): Promise - deleteChat(pluginId: string, pageKey: string): Promise - getAllCh.ts(): Promise - cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/plugin-chat-API.ts` -**Purpose:** Abstraction над IndexedDB -**Dependencies:** -- IndexedDB API -- Chrome Extensions API - -**Экспортирует:** -- `Ch.tstorageAPI` class -- database utility functions - -**Используется:** -- `plugin-chat-cache.ts` - для всех операций с Storageм - -**Ключевые Methodы:** -```TypeScript -class Ch.tstorageAPI { - async getChat(pluginId: string, pageKey: string): Promise - async saveChat(chatdata: Chatdata): Promise - async deleteChat(pluginId: string, pageKey: string): Promise - async getAllCh.ts(): Promise - async cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/index.ts` -**Purpose:** Точка входа background script -**Dependencies:** -- `plugin-chat-cache.ts` - создание экземпляра cacheа -- Chrome Extensions API - -**Используется:** -- Chrome Extensions runtime (автоматически) - -**Основные функции:** -- Инициализация `PluginChatcache` -- Регистрация обработчиков сообщений -- Управление жизненным циклом - -**Обработчики сообщений:** -```TypeScript -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - switch (request.type) { - case 'GET_PLUGIN_CHAT': - case 'SAVE_PLUGIN_CHAT_MESSAGE': - case 'DELETE_PLUGIN_CHAT': - case 'GET_ALL_PLUGIN_CH.ts': - } -}); -``` - -### 2. UI Layer (Слой пользовательского Interfaceа) - -#### `pages/sIDE-panel/src/compone.ts/PluginControlPanel.tsx` -**Purpose:** Основной компонент управления плагином -**Dependencies:** -- React hooks (useState, useEffect, useRef) -- Chrome Extensions API (chrome.runtime) -- `PluginDetails.tsx` - для отображения деталей плагина - -**Используется:** -- `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` - основной контейнер - -**Ключевые состояния:** -```TypeScript -const [messages, setMessages] = useState([]); -const [loading, setLoading] = useState(false); -const [syncStatus, s.tsyncStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); -``` - -**Основные функции:** -- Загрузка чата при монтировании -- Сохранение сообщений -- Synchronization с другими вкладками -- Экспорт и очистка чатов - -#### `pages/sIDE-panel/src/compone.ts/PluginDetails.tsx` -**Purpose:** Отображение деталей плагина -**Dependencies:** -- React -- Plugin interface - -**Используется:** -- `PluginControlPanel.tsx` - как дочерний компонент - -#### `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` -**Purpose:** Основной контейнер sIDE panel -**Dependencies:** -- `PluginControlPanel.tsx` -- `PluginCard.tsx` -- React - -**Используется:** -- Chrome Extensions sIDE panel API - -### 3. DevTools Layer (Слой инструментов разработчика) - -#### `pages/devtools-panel/src/compone.ts/PluginCh.tsTab.tsx` -**Purpose:** Административный Interface для управления чатами -**Dependencies:** -- React hooks -- Chrome Extensions API -- `file-saver` library - -**Используется:** -- `pages/devtools-panel/src/index.tsx` - как вкладка в DevTools - -**Основные функции:** -- Отображение всех чатов -- Удаление чатов -- Экспорт в.json -- Фильтрация и поиск - -#### `pages/devtools-panel/src/index.tsx` -**Purpose:** Точка входа DevTools panel -**Dependencies:** -- `PluginCh.tsTab.tsx` -- React - -**Используется:** -- Chrome DevTools API - -### 4. Shared Layer (Общий слой) - -#### `chrome-extension/src/background/types.ts` (предлагаемый) -**Purpose:** Общие Typeы для всей системы -**Dependencies:** -- TypeScript - -**Экспортирует:** -```TypeScript -export interface ChatMessage { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: number; -} - -export interface Chatdata { - pluginId: string; - pageKey: string; - messages: ChatMessage[]; - lastUpdated: number; - createdAt: number; -} - -export type ChatRequest = - | { type: 'GET_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'SAVE_PLUGIN_CHAT_MESSAGE'; pluginId: string; pageKey: string; message: ChatMessage } - | { type: 'DELETE_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'GET_ALL_PLUGIN_CH.ts' }; - -export type ChatEvent = - | { type: 'PLUGIN_CHAT_UPDATED'; pluginId: string; pageKey: string; messages?: ChatMessage[] }; -``` - -**Используется:** -- Все компоненты системы чатов - -## Threads данных между файлами - -### 1. Загрузка чата -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getChat) -plugin-chat-cache.ts - ↓ (storageAPI.getChat) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -### 2. Сохранение сообщения -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.saveMessage) -plugin-chat-cache.ts - ↓ (storageAPI.saveChat + event broadcast) -plugin-chat-API.ts + chrome.tabs.query - ↓ (IndexedDB + other tabs) -Browser Storage + Other UI Compone.ts -``` - -### 3. Административные операции -``` -PluginCh.tsTab.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getAllCh.ts/deleteChat) -plugin-chat-cache.ts - ↓ (storageAPI) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -## Dependencies и импорты - -### Background Layer -```TypeScript -// background/index.ts -import { PluginChatcache } from './plugin-chat-cache'; -import type { ChatRequest, ChatEvent } from './types'; - -// plugin-chat-cache.ts -import { Ch.tstorageAPI } from './plugin-chat-API'; -import type { Chatdata, ChatMessage } from './types'; - -// plugin-chat-API.ts -// Нет внешних зависимостей, только IndexedDB API -``` - -### UI Layer -```TypeScript -// PluginControlPanel.tsx -import { PluginDetails } from './PluginDetails'; -import type { ChatMessage } from '../../../background/types'; - -// PluginDetails.tsx -import type { Plugin } from './PluginCard'; - -// SIDEPanel.tsx -import { PluginControlPanel } from './PluginControlPanel'; -import { PluginCard } from './PluginCard'; -``` - -### DevTools Layer -```TypeScript -// PluginCh.tsTab.tsx -import { saveAs } from 'file-saver'; -import type { Chatdata } from '../../../background/types'; - -// devtools-panel/index.tsx -import { PluginCh.tsTab } from './compone.ts/PluginCh.tsTab'; -``` - -## configuration Files - -### `chrome-extension/manifest.json` -**Purpose:** configuration Extensions -**Содержит:** -- Background script registration -- SIDE panel configuration -- DevTools panel configuration -- Permissions - -### `package.json` (в каждой папке pages) -**Purpose:** Dependencies и скрипты сборки -**Содержит:** -- React dependencies -- build tools -- TypeScript configuration - -## Стили и ресурсы - -###.css файлы -- `PluginControlPanel.css` - стили для основного компонента -- `SIDEPanel.css` - стили для sIDE panel -- `PluginCard.css` - стили для карточек плагинов - -### Иконки и изображения -- .icon.svg` - иконки плагинов -- ЛогоTypeы и брендинг - -## Тестовые файлы - -### Unit Te.ts -- `__te.ts__/plugin-chat-cache.test.ts` -- `__te.ts__/plugin-chat-API.test.ts` -- `__te.ts__/PluginControlPanel.test.tsx` - -### Integration Te.ts -- `__te.ts__/chat-system.integration.test.ts` - -### E2E Te.ts -- `te.ts/e2e/chat-functionality.test.ts` - -## Documentation - -### Техническая Documentation -- `README.md` - общее Description -- `API.md` - Documentation API -- `ARCHITECTURE.md` - архитектурное Description - -### Пользовательская Documentation -- `USER_GUIDE.md` - руководство пользователя -- `DEVELOPER_GUIDE.md` - руководство разработчика - -## Заключение - -Система чатов плагинов имеет четкую модульную архитектуру с разделением ответственности: - -1. **Background Layer** - управление данными и бизнес-логика -2. **UI Layer** - пользовательский Interface -3. **DevTools Layer** - административные функции -4. **Shared Layer** - общие Typeы и утилиты - -Все компоненты связаны через messaging API, что обеспечивает loose coupling и легкую тестируемость. Каждый файл имеет четко определенную ответственность и минимальные Dependencies. \ No newline at end of file diff --git a/.cursor/rules/architecture/project-architecture.mdc b/.cursor/rules/architecture/project-architecture.mdc deleted file mode 100644 index e9846dba..00000000 --- a/.cursor/rules/architecture/project-architecture.mdc +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Overview -Agent-Plugins-Platform (APP) is a browser extension for running Python plugins via PyodIDE and the MCP protocol. Key priorities: security, performance, and developer experience. - -## Architecture Patterns - -### Core Compone.ts -- **Plugin Manager** (`core/plugin-manager.js`): manages plugin lifecycle -- **Host API** (`core/host-API.js`): provIDEs Python access to browser APIs -- **Workflow Engine** (`core/workflow-engine.js`): executes plugin workflows -- **MCP Bridge** (`Bridge/mcp-Bridge.js`):.js ↔ Python communication -- **PyodIDE Worker** (`Bridge/pyodIDE-worker.js`): isolated Python execution - -### Plugin Structure -``` -public/plugins/plugin-name/ -├── manifest.json # Metadata and permissions -├── mcp_server.py # MCP protocol (Python) -├── workflow.json # Workflow description -└──.icon.svg #.icon -``` - -### Communication Flow -1. UI → Plugin Manager → MCP Bridge → PyodIDE Worker → Python Plugin -2. Python Plugin → Host API → Browser APIs -3. Resu.ts return via the same path -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/audit-cursor.cjs b/.cursor/rules/audit-cursor.cjs deleted file mode 100644 index 7a497b39..00000000 --- a/.cursor/rules/audit-cursor.cjs +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorAuditor { - constructor() { - this.rulesDir = path.join(__dirname); - this.issues = []; - this.stats = { - totalFiles: 0, - mdcFiles: 0, - mdFiles: 0, - filesWithMetadata: 0, - filesWithoutMetadata: 0, - duplicateContent: [], - brokenLinks: [], - missingFiles: [], - invalidMetadata: [] - }; - } - - // Основной метод аудита - async audit() { - console.log('🔍 Starting comprehensive .cursor audit...\n'); - - await this.scanFiles(); - await this.checkMetadata(); - await this.findDuplicates(); - await this.checkLinks(); - await this.validateStructure(); - await this.generateReport(); - - return this.stats; - } - - // Сканирование всех файлов - async scanFiles() { - console.log('📁 Scanning files...'); - - const files = this.getAllFiles(this.rulesDir); - this.stats.totalFiles = files.length; - - for (const file of files) { - const ext = path.extname(file); - if (ext === '.mdc') { - this.stats.mdcFiles++; - } else if (ext === '.md') { - this.stats.mdFiles++; - } - } - - console.log(` Found ${this.stats.totalFiles} files (${this.stats.mdcFiles} .mdc, ${this.stats.mdFiles} .md)`); - } - - // Проверка метаданных - async checkMetadata() { - console.log('📋 Checking metadata...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - if (content.startsWith('---')) { - this.stats.filesWithMetadata++; - - // Проверяем валидность метаданных - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (metadataMatch) { - const metadata = metadataMatch[1]; - const hasDescription = metadata.includes('description:'); - const hasGlobs = metadata.includes('globs:'); - const hasAlwaysApply = metadata.includes('alwaysApply:'); - - if (!hasDescription || !hasGlobs || !hasAlwaysApply) { - this.stats.invalidMetadata.push({ - file: path.relative(this.rulesDir, file), - missing: [] - }); - - if (!hasDescription) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('description'); - if (!hasGlobs) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('globs'); - if (!hasAlwaysApply) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('alwaysApply'); - } - } - } else { - this.stats.filesWithoutMetadata++; - } - } - - console.log(` ${this.stats.filesWithMetadata} files with metadata, ${this.stats.filesWithoutMetadata} without`); - } - - // Поиск дубликатов - async findDuplicates() { - console.log('🔄 Finding duplicates...'); - - const files = this.getAllFiles(this.rulesDir); - const contentMap = new Map(); - - for (const file of files) { - const content = fs.readFileSync(file, 'utf8'); - const normalizedContent = this.normalizeContent(content); - - if (contentMap.has(normalizedContent)) { - this.stats.duplicateContent.push({ - original: path.relative(this.rulesDir, contentMap.get(normalizedContent)), - duplicate: path.relative(this.rulesDir, file) - }); - } else { - contentMap.set(normalizedContent, file); - } - } - - console.log(` Found ${this.stats.duplicateContent.length} duplicate files`); - } - - // Проверка ссылок - async checkLinks() { - console.log('🔗 Checking links...'); - - const files = this.getAllFiles(this.rulesDir); - - for (const file of files) { - const content = fs.readFileSync(file, 'utf8'); - const links = this.extractLinks(content); - - for (const link of links) { - if (link.startsWith('./') || link.startsWith('../')) { - const targetPath = path.resolve(path.dirname(file), link); - - if (!fs.existsSync(targetPath)) { - this.stats.brokenLinks.push({ - file: path.relative(this.rulesDir, file), - link: link - }); - } - } - } - } - - console.log(` Found ${this.stats.brokenLinks.length} broken links`); - } - - // Валидация структуры - async validateStructure() { - console.log('🏗️ Validating structure...'); - - const expectedDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - const actualDirs = fs.readdirSync(this.rulesDir) - .filter(item => fs.statSync(path.join(this.rulesDir, item)).isDirectory()); - - for (const expectedDir of expectedDirs) { - if (!actualDirs.includes(expectedDir)) { - this.stats.missingFiles.push(`Directory: ${expectedDir}`); - } - } - - // Проверяем обязательные файлы - const requiredFiles = ['index.mdc', 'README.mdc', 'ai-memory.mdc']; - for (const requiredFile of requiredFiles) { - if (!fs.existsSync(path.join(this.rulesDir, requiredFile))) { - this.stats.missingFiles.push(`File: ${requiredFile}`); - } - } - - console.log(` Found ${this.stats.missingFiles.length} missing items`); - } - - // Генерация отчета - async generateReport() { - console.log('\n📊 Generating audit report...\n'); - - console.log('='.repeat(60)); - console.log('🔍 CURSOR AUDIT REPORT'); - console.log('='.repeat(60)); - - // Общая статистика - console.log('\n📈 GENERAL STATISTICS:'); - console.log(` Total files: ${this.stats.totalFiles}`); - console.log(` .mdc files: ${this.stats.mdcFiles}`); - console.log(` .md files: ${this.stats.mdFiles}`); - console.log(` Files with metadata: ${this.stats.filesWithMetadata}`); - console.log(` Files without metadata: ${this.stats.filesWithoutMetadata}`); - - // Проблемы - if (this.stats.duplicateContent.length > 0) { - console.log('\n⚠️ DUPLICATE CONTENT:'); - for (const dup of this.stats.duplicateContent) { - console.log(` ${dup.duplicate} duplicates ${dup.original}`); - } - } - - if (this.stats.brokenLinks.length > 0) { - console.log('\n🔗 BROKEN LINKS:'); - for (const link of this.stats.brokenLinks) { - console.log(` ${link.file}: ${link.link}`); - } - } - - if (this.stats.invalidMetadata.length > 0) { - console.log('\n📋 INVALID METADATA:'); - for (const meta of this.stats.invalidMetadata) { - console.log(` ${meta.file}: missing ${meta.missing.join(', ')}`); - } - } - - if (this.stats.missingFiles.length > 0) { - console.log('\n❌ MISSING FILES/DIRECTORIES:'); - for (const missing of this.stats.missingFiles) { - console.log(` ${missing}`); - } - } - - // Рекомендации - console.log('\n💡 RECOMMENDATIONS:'); - if (this.stats.mdFiles > 0) { - console.log(' • Consider converting .md files to .mdc for better AI integration'); - } - if (this.stats.filesWithoutMetadata > 0) { - console.log(' • Add metadata to .mdc files without frontmatter'); - } - if (this.stats.duplicateContent.length > 0) { - console.log(' • Remove duplicate files to reduce confusion'); - } - if (this.stats.brokenLinks.length > 0) { - console.log(' • Fix broken links in documentation'); - } - - console.log('\n' + '='.repeat(60)); - } - - // Вспомогательные методы - getAllFiles(dir) { - const files = []; - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - files.push(...this.getAllFiles(fullPath)); - } else if (item.endsWith('.mdc') || item.endsWith('.md')) { - files.push(fullPath); - } - } - - return files; - } - - getFilesByExt(ext) { - return this.getAllFiles(this.rulesDir).filter(file => file.endsWith(ext)); - } - - normalizeContent(content) { - return content - .replace(/\s+/g, ' ') - .replace(/[^\w\s]/g, '') - .toLowerCase() - .trim(); - } - - extractLinks(content) { - const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; - const links = []; - let match; - - while ((match = linkRegex.exec(content)) !== null) { - links.push(match[2]); - } - - return links; - } -} - -// Запуск аудита -async function main() { - const auditor = new CursorAuditor(); - await auditor.audit(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorAuditor; \ No newline at end of file diff --git a/.cursor/rules/auto-translate-requests.cjs b/.cursor/rules/auto-translate-requests.cjs deleted file mode 100644 index 7c001d48..00000000 --- a/.cursor/rules/auto-translate-requests.cjs +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/env node - -/** - * Auto Translate Requests Script - * Automatically translates user requests to English for .cursor rule creation - * Usage: node .cursor/rules/auto-translate-requests.cjs [interactive|translate|setup] - */ - -const fs = require('fs'); -const path = require('path'); -const readline = require('readline'); -const { translateRequest, analyzeRequest, generateEnglishPrompt } = require('./request-translator.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const RULES_DIR = path.join(CURSOR_DIR, 'rules'); - -function createInteractiveMode() { - console.log('🤖 Auto Translate Requests - Interactive Mode\n'); - console.log('This mode will help you create .cursor rules in English.'); - console.log('Type your request in Russian, and the system will translate it to English.\n'); - - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - - function askQuestion(question) { - return new Promise((resolve) => { - rl.question(question, resolve); - }); - } - - async function interactiveLoop() { - try { - while (true) { - const userRequest = await askQuestion('📝 Enter your request (or "quit" to exit): '); - - if (userRequest.toLowerCase() === 'quit' || userRequest.toLowerCase() === 'exit') { - console.log('\n👋 Goodbye!'); - break; - } - - if (!userRequest.trim()) { - console.log('⚠️ Please enter a valid request.\n'); - continue; - } - - console.log('\n🔄 Processing your request...\n'); - - // Analyze the request - const analysis = analyzeRequest(userRequest); - const prompt = generateEnglishPrompt(userRequest); - - // Display results - console.log('📊 Request Analysis:'); - console.log(`Original: ${analysis.originalText}`); - console.log(`Translated: ${analysis.translatedText}`); - console.log(`Confidence: ${analysis.translationConfidence.toFixed(1)}%`); - console.log(`Patterns: ${analysis.detectedPatterns.join(', ')}`); - - if (prompt.shouldTranslate) { - console.log('\n🤖 Generated English Prompt:'); - console.log('='.repeat(60)); - console.log(prompt.englishPrompt); - console.log('='.repeat(60)); - - const proceed = await askQuestion('\n✅ Proceed with creating the rule? (y/n): '); - - if (proceed.toLowerCase() === 'y' || proceed.toLowerCase() === 'yes') { - await createRuleFromPrompt(prompt); - } else { - console.log('❌ Rule creation cancelled.\n'); - } - } else { - console.log('\n✅ Request is already in English. Proceeding with rule creation...\n'); - await createRuleFromPrompt(prompt); - } - - console.log('\n' + '='.repeat(60) + '\n'); - } - } catch (error) { - console.error('❌ Error in interactive mode:', error.message); - } finally { - rl.close(); - } - } - - interactiveLoop(); -} - -async function createRuleFromPrompt(prompt) { - try { - console.log('📝 Creating .cursor rule...\n'); - - // Generate filename based on request - const filename = generateFilename(prompt.translatedRequest || prompt.originalRequest); - const filepath = path.join(RULES_DIR, filename); - - // Create rule content - const ruleContent = generateRuleContent(prompt); - - // Ensure directory exists - const dir = path.dirname(filepath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - // Write the rule file - fs.writeFileSync(filepath, ruleContent, 'utf8'); - - console.log(`✅ Rule created: ${path.relative(process.cwd(), filepath)}`); - console.log(`📁 Location: ${filepath}`); - - // Stage the file for git - const { execSync } = require('child_process'); - try { - execSync(`git add "${filepath}"`, { stdio: 'inherit' }); - console.log('📦 File staged for git'); - } catch (error) { - console.log('⚠️ Could not stage file for git'); - } - - return filepath; - } catch (error) { - console.error('❌ Error creating rule:', error.message); - throw error; - } -} - -function generateFilename(request) { - // Extract key terms from request - const terms = request.toLowerCase() - .replace(/[^\w\s]/g, '') - .split(/\s+/) - .filter(word => word.length > 2) - .slice(0, 3); - - // Create filename - const baseName = terms.join('-') || 'new-rule'; - return `${baseName}.mdc`; -} - -function generateRuleContent(prompt) { - const timestamp = new Date().toISOString(); - const originalRequest = prompt.originalRequest; - const translatedRequest = prompt.translatedRequest || prompt.originalRequest; - - return `--- -description: ${translatedRequest} -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "${originalRequest}" -translatedAt: "${timestamp}" ---- - -# ${translatedRequest} - -## Overview - -This rule was created based on the request: "${originalRequest}" - -## Description - -${translatedRequest} - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "${originalRequest}"* -*Translated to English for AI/LLM compatibility* -`; -} - -function setupAutoTranslation() { - console.log('🔧 Setting up auto translation for .cursor requests...\n'); - - // Create configuration file - const configPath = path.join(CURSOR_DIR, 'auto-translate-config.json'); - const config = { - enabled: true, - autoTranslate: true, - createEnglishPrompts: true, - backupOriginalRequests: true, - confidenceThreshold: 70, - patterns: { - ruleCreation: ['создай', 'добавь', 'напиши', 'сделай'], - fileCreation: ['файл', 'документ', 'правило'], - cursorRequests: ['.cursor', 'cursor', 'rules'] - } - }; - - fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8'); - console.log('✅ Configuration created: .cursor/auto-translate-config.json'); - - // Create helper script - const helperPath = path.join(CURSOR_DIR, 'rules', 'create-rule.cjs'); - const helperContent = `#!/usr/bin/env node - -/** - * Quick Rule Creation Helper - * Usage: node .cursor/rules/create-rule.cjs "your request in Russian" - */ - -const { generateEnglishPrompt } = require('./request-translator.cjs'); -const { createRuleFromPrompt } = require('./auto-translate-requests.cjs'); - -async function main() { - const request = process.argv[2]; - - if (!request) { - console.log('❌ Please provide a request'); - console.log('Usage: node .cursor/rules/create-rule.cjs "создай правило для архитектуры"'); - process.exit(1); - } - - try { - const prompt = generateEnglishPrompt(request); - await createRuleFromPrompt(prompt); - console.log('\\n🎉 Rule created successfully!'); - } catch (error) { - console.error('❌ Error:', error.message); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} -`; - - fs.writeFileSync(helperPath, helperContent, 'utf8'); - console.log('✅ Helper script created: .cursor/rules/create-rule.cjs'); - - // Update package.json scripts - const packagePath = path.join(process.cwd(), 'package.json'); - if (fs.existsSync(packagePath)) { - try { - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); - - if (!packageJson.scripts) { - packageJson.scripts = {}; - } - - packageJson.scripts['create-rule'] = 'node .cursor/rules/create-rule.cjs'; - packageJson.scripts['translate-request'] = 'node .cursor/rules/request-translator.cjs translate'; - packageJson.scripts['interactive-rules'] = 'node .cursor/rules/auto-translate-requests.cjs interactive'; - - fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), 'utf8'); - console.log('✅ Package.json scripts updated'); - } catch (error) { - console.log('⚠️ Could not update package.json:', error.message); - } - } - - console.log('\n🎉 Auto translation setup completed!'); - console.log('\n📝 Available commands:'); - console.log(' npm run create-rule "your request"'); - console.log(' npm run translate-request "your request"'); - console.log(' npm run interactive-rules'); - console.log(' node .cursor/rules/auto-translate-requests.cjs interactive'); -} - -function main() { - const command = process.argv[2] || 'help'; - - switch (command) { - case 'interactive': - createInteractiveMode(); - break; - - case 'translate': - const text = process.argv[3]; - if (!text) { - console.log('❌ Please provide text to translate'); - console.log('Usage: node .cursor/rules/auto-translate-requests.cjs translate "текст"'); - return; - } - const prompt = generateEnglishPrompt(text); - console.log('🤖 Generated English Prompt:'); - console.log('='.repeat(60)); - console.log(prompt.englishPrompt); - console.log('='.repeat(60)); - break; - - case 'setup': - setupAutoTranslation(); - break; - - case 'help': - console.log(` -Auto Translate Requests Script - -Usage: node .cursor/rules/auto-translate-requests.cjs [command] [text] - -Commands: - interactive - Start interactive mode for creating rules - translate - Translate request to English prompt - setup - Setup auto translation system - help - Show this help - -Examples: - node .cursor/rules/auto-translate-requests.cjs interactive - node .cursor/rules/auto-translate-requests.cjs translate "создай правило" - node .cursor/rules/auto-translate-requests.cjs setup - `); - break; - - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { - createInteractiveMode, - createRuleFromPrompt, - generateFilename, - generateRuleContent, - setupAutoTranslation -}; \ No newline at end of file diff --git a/.cursor/rules/check-rules-structure.cjs b/.cursor/rules/check-rules-structure.cjs deleted file mode 100644 index d8d03155..00000000 --- a/.cursor/rules/check-rules-structure.cjs +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env node -const fs = require('fs'); -const path = require('path'); - -const RULES_DIR = path.join(__dirname); -const files = fs.readdirSync(RULES_DIR).filter(f => f.endsWith('.mdc')); - -let ok = true; -const seen = new Set(); - -for (const f of files) { - const content = fs.readFileSync(path.join(RULES_DIR, f), 'utf8'); - if (!content.match(/^# /m)) { console.error(`${f}: missing heading`); ok = false; } - if (!content.match(/^description:/m)) { console.error(`${f}: missing description`); ok = false; } - if (!content.match(/^globs:/m)) { console.error(`${f}: missing globs`); ok = false; } - if (!content.match(/^alwaysApply:/m)) { console.error(`${f}: missing alwaysApply`); ok = false; } - if (!content.match(/^---/m)) { console.error(`${f}: missing ---`); ok = false; } - if (seen.has(f)) { console.error(`${f}: duplicate file name`); ok = false; } - seen.add(f); - // Check related links - const related = content.match(/related:[\s\S]*?description:/m); - if (related) { - const rels = [...related[0].matchAll(/-\s+([^\s]+)/g)].map(m => m[1]); - for (const r of rels) { - if (!fs.existsSync(path.join(RULES_DIR, r))) { - console.error(`${f}: related link to missing file ${r}`); - ok = false; - } - } - } -} -if (!ok) process.exit(1); -console.log('All rules structure checks passed.'); \ No newline at end of file diff --git a/.cursor/rules/command-sync.cjs b/.cursor/rules/command-sync.cjs deleted file mode 100644 index 74d40ef8..00000000 --- a/.cursor/rules/command-sync.cjs +++ /dev/null @@ -1,670 +0,0 @@ -#!/usr/bin/env node - -/** - * Command Synchronization Script - * Syncs commands between USER_COMMANDS.md, ai-memory.mdc, and Cursor AI memory-bank - * Usage: node .cursor/rules/command-sync.cjs [sync|export|import|update-cursor] - */ - -const fs = require('fs'); -const path = require('path'); - -// File paths -const USER_COMMANDS_PATH = path.join(process.cwd(), 'USER_COMMANDS.md'); -const AI_MEMORY_PATH = path.join(process.cwd(), '.cursor', 'rules', 'cursor-export', 'ai-memory.mdc'); -const CURSOR_MEMORY_PATH = path.join(process.cwd(), '.cursor', 'rules', 'ai-memory.mdc'); -const ACTIVE_CONTEXT_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'activeContext.md'); -const PROGRESS_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'progress.md'); -const BACKUP_DIR = path.join(process.cwd(), 'memory-bank', 'core', 'backup'); - -// Command structure -const commandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Сохранить контекст сессии'], - description: 'Save achievements, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and commits)', - userDescription: 'AI-ассистент автоматически переведет и сохранит все достижения, решения и планы в memory-bank на английском языке для совместимости с AI/LLM' - }, - 'update progress': { - russian: ['Обнови прогресс', 'Обновить прогресс проекта'], - description: 'Update activeContext.md and progress.md with current status', - userDescription: 'AI-ассистент обновит файлы activeContext.md и progress.md с текущим статусом' - }, - 'restore context': { - russian: ['Восстанови контекст', 'Восстановить полный контекст'], - description: 'Study all memory-bank files and restore full project understanding', - userDescription: 'AI-ассистент изучит все файлы memory-bank и восстановит полное понимание проекта' - }, - 'quick restore': { - russian: ['Быстрое восстановление'], - description: 'Get brief summary of key principles and current status', - userDescription: 'AI-ассистент получит краткую сводку ключевых принципов и текущего статуса' - } - }, - 'Analysis and Study': { - 'analyze architecture': { - russian: ['Анализируй архитектуру', 'Анализ архитектуры'], - description: 'Study systemPatterns.md and techContext.md for architecture understanding', - userDescription: 'AI-ассистент изучит systemPatterns.md и techContext.md для понимания архитектуры' - }, - 'study plugins': { - russian: ['Изучи плагины'], - description: 'Analyze plugins and their structure', - userDescription: 'AI-ассистент проанализирует существующие плагины и их структуру' - }, - 'check build': { - russian: ['Проверь сборку', 'Проверить сборку'], - description: 'Check that project builds and works correctly', - userDescription: 'AI-ассистент проверит, что проект собирается и работает корректно' - }, - 'update documentation': { - russian: ['Обнови документацию', 'Обновить документацию'], - description: 'Check and update README.md and PLUGIN_DEVELOPMENT.md', - userDescription: 'AI-ассистент проверит и обновит README.md и PLUGIN_DEVELOPMENT.md' - } - }, - 'Development': { - 'create plugin [name]': { - russian: ['Создай плагин [название]'], - description: 'Create new plugin with specified name', - userDescription: 'AI-ассистент создаст новый плагин с указанным названием' - }, - 'check code': { - russian: ['Проверь код', 'Проверить линтинг'], - description: 'Run linting and type checking', - userDescription: 'AI-ассистент выполнит линтинг и проверку типов' - }, - 'run tests': { - russian: ['Запусти тесты', 'Запустить тесты'], - description: 'Run all project tests', - userDescription: 'AI-ассистент запустит все тесты проекта' - }, - 'check dependencies': { - russian: ['Проверь зависимости', 'Проверить зависимости'], - description: 'Check dependencies relevance and compatibility', - userDescription: 'AI-ассистент проверит актуальность и совместимость зависимостей' - } - }, - 'Project Management': { - 'bump version patch/minor/major': { - russian: ['Увеличь версию [patch|minor|major]', 'Версионирование'], - description: 'Increase project version according to parameter', - userDescription: 'AI-ассистент увеличит версию проекта (например: "Увеличь версию minor")' - }, - 'clean project': { - russian: ['Очисти проект', 'Очистка проекта'], - description: 'Clean node_modules, dist and cache', - userDescription: 'AI-ассистент выполнит очистку node_modules, dist и кэша' - }, - 'analyze performance': { - russian: ['Анализируй производительность', 'Анализ производительности'], - description: 'Analyze project performance and suggest optimizations', - userDescription: 'AI-ассистент проанализирует производительность проекта и предложит оптимизации' - }, - 'check security': { - russian: ['Проверь безопасность', 'Анализ безопасности'], - description: 'Analyze code and configuration security', - userDescription: 'AI-ассистент проанализирует безопасность кода и конфигурации' - } - }, - 'Releases and Deployment': { - 'create release': { - russian: ['Создай релиз', 'Создать релиз'], - description: 'Prepare project for release (bump version, create ZIP)', - userDescription: 'AI-ассистент подготовит проект к релизу (увеличит версию, создаст ZIP)' - }, - 'build production': { - russian: ['Собери для продакшена', 'Сборка для продакшена'], - description: 'Perform full production build', - userDescription: 'AI-ассистент выполнит полную сборку для продакшена' - } - } -}; - -function generateUserCommandsMD() { - let content = `# Команды для пользователя - -Просто скопируйте и вставьте любую из этих команд в чат с AI-ассистентом. - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - const categoryEmoji = getCategoryEmoji(category); - content += `## ${categoryEmoji} ${category}\n\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `### ${details.userDescription}\n`; - content += `${russianCommands}\n`; - content += `*${details.userDescription}*\n\n`; - } - } - - content += `--- - -## 💡 Как использовать - -1. **Скопируйте** нужную команду -2. **Вставьте** в чат с AI-ассистентом -3. **Дождитесь** выполнения команды - -## 🔄 Комбинированные команды - -Вы можете комбинировать команды: -\`\`\` -Сохрани контекст и обнови прогресс -\`\`\` - -\`\`\` -Восстанови контекст и анализируй архитектуру -\`\`\` - -## 📝 Примечания - -- AI-ассистент автоматически изучит файлы memory-bank при необходимости -- Все команды выполняются с учетом текущего контекста проекта -- Результаты сохраняются в соответствующих файлах memory-bank -- Команды работают с любой LLM моделью, которая следует инструкциям - -## Восстановление контекста проекта в новом чате - -1. Просто напиши в начале чата: - \`\`\` - Восстанови контекст - \`\`\` -2. Ассистент автоматически: - - Прочитает все ключевые файлы memory-bank (README.md, activeContext.md, systemPatterns.md, progress.md, productContext.md, techContext.md, session-log.md) - - Восстановит все правила, best practices, кладбище ошибок, карту взаимосвязей, архитектурные решения и рекомендации - - Будет использовать только актуальные подходы и паттерны из memory-bank - - Если что-то неясно — уточнит у тебя детали по новой фиче или архитектуре -3. Когда стоит добавить детали вручную: - - Если есть особые пожелания к стилю работы, архитектуре или процессу, которые не зафиксированы в memory-bank - - Если нужно сфокусироваться только на определённой части контекста - - Если хочешь ускорить процесс и не ждать уточняющих вопросов - -**Совет:** Если что-то важно для будущей работы — фиксируй это в memory-bank, чтобы не потерять при смене чата или ассистента. -`; - - return content; -} - -function generateAIMemoryMD() { - let content = `--- -description: Universal user commands for AI assistant - complete command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - content += `### ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - content += '\n'; - } - - content += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices -`; - - return content; -} - -function generateCursorMemoryBank() { - let content = `# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - content += `### ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - content += '\n'; - } - - content += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices -`; - - return content; -} - -function getCategoryEmoji(category) { - const emojiMap = { - 'Context and Memory': '📝', - 'Analysis and Study': '🏗️', - 'Development': '🔧', - 'Project Management': '📊', - 'Releases and Deployment': '🚀' - }; - return emojiMap[category] || '📋'; -} - -// Context translation function -function translateContextToEnglish() { - console.log('🔄 Translating context to English for AI/LLM compatibility...\n'); - - // Create backup directory - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - - // Translation mappings for context - const contextTranslations = { - // Headers and titles - 'Активный контекст разработки': 'Active Development Context', - 'Текущий статус проекта': 'Current Project Status', - 'Последнее обновление': 'Last Updated', - 'Сессия интернационализации и синхронизации команд': 'Internationalization and Command Synchronization Session', - - // Task status - 'Завершенные задачи': 'Completed Tasks', - 'Текущий фокус': 'Current Focus', - 'Приоритет': 'Priority', - 'Следующие шаги': 'Next Steps', - 'Готовность к международному сообществу и глобальному использованию': 'Readiness for International Community and Global Usage', - - // Principles - 'Ключевые принципы работы': 'Key Working Principles', - 'Инициативность ассистента': 'Assistant Initiative', - 'Всегда предлагать улучшения и оптимизации': 'Always suggest improvements and optimizations', - 'Конструктивно критиковать существующие решения': 'Constructively criticize existing solutions', - 'Предлагать альтернативные подходы': 'Suggest alternative approaches', - 'Проактивно выявлять потенциальные проблемы': 'Proactively identify potential issues', - - 'Качество кода': 'Code Quality', - 'Следовать принципам из': 'Follow principles from', - 'Применять "Do No Harm" принцип': 'Apply "Do No Harm" principle', - 'Использовать AI-First документацию': 'Use AI-First documentation', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - - 'Internationalization': 'Internationalization', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Универсальный формат команд': 'Universal command format', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Technical context - 'Технический контекст': 'Technical Context', - 'Текущая архитектура': 'Current Architecture', - 'React + TypeScript для UI компонентов': 'React + TypeScript for UI components', - 'Модульная система пакетов': 'Modular package system', - 'Vite для сборки': 'Vite for building', - 'Tailwind CSS для стилизации': 'Tailwind CSS for styling', - 'Chrome Extension API для интеграции': 'Chrome Extension API for integration', - 'Система синхронизации команд': 'Command synchronization system', - - 'Стандарты разработки': 'Development Standards', - 'TypeScript для всех новых файлов': 'TypeScript for all new files', - 'ESLint для проверки кода': 'ESLint for code checking', - 'Компонентный подход с proper accessibility': 'Component approach with proper accessibility', - 'Структурированное логирование': 'Structured logging', - 'Комплексная документация с примерами': 'Comprehensive documentation with examples', - 'Английский язык для всех правил и документации': 'English language for all rules and documentation', - - 'Безопасность': 'Security', - 'Zero Trust архитектура для плагинов': 'Zero Trust architecture for plugins', - 'Валидация всех входных данных': 'Validation of all input data', - 'Шифрование чувствительной информации': 'Encryption of sensitive information', - 'Аудит всех действий плагинов': 'Audit of all plugin actions', - - // Command system - 'Система команд': 'Command System', - 'Автоматическая синхронизация': 'Automatic Synchronization', - 'Единый источник истины': 'Single source of truth', - 'Автоматическая генерация': 'Automatic generation', - 'Множественные форматы': 'Multiple formats', - 'Категории команд': 'Command Categories', - 'Сохранение и восстановление контекста': 'Context saving and restoration', - 'Анализ архитектуры и изучение плагинов': 'Architecture analysis and plugin study', - 'Создание плагинов и проверка кода': 'Plugin creation and code checking', - 'Управление версиями и анализ': 'Version management and analysis', - 'Создание релизов и сборка': 'Release creation and building', - - 'Интеграция с Cursor': 'Cursor Integration', - 'Экспорт команд': 'Command export', - 'Файл для Cursor': 'File for Cursor', - 'Инструкции интеграции': 'Integration instructions', - 'Пошаговое руководство': 'Step-by-step guide', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Приоритеты UX': 'UX Priorities', - 'Интуитивность интерфейса': 'Interface intuitiveness', - 'Быстродействие и отзывчивость': 'Speed and responsiveness', - 'Доступность': 'Accessibility', - 'Консистентность дизайна': 'Design consistency', - 'Поддержка различных тем': 'Support for various themes', - 'Универсальность команд': 'Command universality', - - 'Метрики качества': 'Quality Metrics', - 'Время загрузки компонентов': 'Component loading time', - 'Плавность анимаций': 'Animation smoothness', - 'Доступность для скринридеров': 'Accessibility for screen readers', - 'Совместимость с различными браузерами': 'Compatibility with various browsers', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Development plans - 'Планы развития': 'Development Plans', - 'Краткосрочные цели': 'Short-term Goals', - 'Среднесрочные цели': 'Medium-term Goals', - 'Долгосрочные цели': 'Long-term Goals', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - 'Расширение системы команд новыми категориями': 'Expanding command system with new categories', - 'API интеграция с Cursor для автоматических обновлений': 'API integration with Cursor for automatic updates', - 'Создание шаблонов команд для разных типов проектов': 'Creating command templates for different project types', - 'Развитие экосистемы плагинов': 'Development of plugin ecosystem', - 'Создание полноценной международной платформы': 'Creating a full-fledged international platform', - 'Развитие глобального сообщества разработчиков': 'Development of global developer community', - 'Интеграция с популярными сервисами': 'Integration with popular services', - 'Многоязычная поддержка интерфейса': 'Multilingual interface support', - - // Important files - 'Важные файлы и ресурсы': 'Important Files and Resources', - 'Система синхронизации команд': 'Command Synchronization System', - 'Основной скрипт синхронизации': 'Main synchronization script', - 'Документация системы': 'System documentation', - 'Пользовательский справочник команд': 'User command reference', - 'Экспорт для Cursor': 'Export for Cursor', - - 'Ключевые компоненты': 'Key Components', - 'Карточки плагинов': 'Plugin cards', - 'Панель управления': 'Control panel', - 'Детали плагина': 'Plugin details', - 'Индикатор статуса': 'Status indicator', - - 'Документация': 'Documentation', - 'Документация интернационализации': 'Internationalization documentation', - 'Документация системы команд': 'Command system documentation', - 'Принципы разработки': 'Development principles', - 'План улучшений UI': 'UI improvement plan', - 'Долгосрочные планы': 'Long-term plans', - - 'Конфигурация': 'Configuration', - 'UI компоненты и стили': 'UI components and styles', - 'Конфигурация сборки': 'Build configuration', - 'Общие утилиты': 'Common utilities', - - // Commands and processes - 'Команды и процессы': 'Commands and Processes', - 'Система синхронизации команд': 'Command Synchronization System', - 'Синхронизация всех файлов': 'Synchronize all files', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Справка по командам': 'Command help', - - 'Сборка проекта': 'Project Build', - 'Сборка всех страниц': 'Build all pages', - 'Сборка конкретной страницы': 'Build specific page', - 'Разработка': 'Development', - - 'Тестирование': 'Testing', - 'Использовать DevTools панель': 'Use DevTools panel', - 'Тестировать в боковой панели расширения': 'Test in extension side panel', - 'Проверять в различных темах': 'Check in various themes', - 'Тестировать все обновленные компоненты': 'Test all updated components', - 'Проверять синхронизацию команд': 'Check command synchronization', - - 'Git workflow': 'Git Workflow', - 'Создавать feature ветки для новых функций': 'Create feature branches for new functions', - 'Создавать fix ветки для исправлений': 'Create fix branches for fixes', - 'Использовать осмысленные названия веток': 'Use meaningful branch names', - 'Вливать через pull requests': 'Merge through pull requests', - - // Contacts and support - 'Контакты и поддержка': 'Contacts and Support', - 'Для пользователей': 'For Users', - 'Документация в memory-bank': 'Documentation in memory-bank', - 'Тестирование через DevTools панель': 'Testing through DevTools panel', - 'Обратная связь через GitHub Issues': 'Feedback through GitHub Issues', - 'Справочник команд в USER_COMMANDS.md': 'Command reference in USER_COMMANDS.md', - - 'Для разработчиков': 'For Developers', - 'Следовать принципам из': 'Follow principles from', - 'Использовать модульную архитектуру': 'Use modular architecture', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - 'Документировать все изменения': 'Document all changes', - 'Использовать систему синхронизации команд': 'Use command synchronization system', - - // Status - 'Статус готовности к международному сообществу': 'Readiness Status for International Community', - 'Готовые компоненты': 'Ready Components', - 'Полная интернационализация .cursor и memory-bank': 'Complete internationalization of .cursor and memory-bank', - 'Система синхронизации команд': 'Command synchronization system', - 'Универсальный формат команд': 'Universal command format', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Полностью модернизирован': 'Fully modernized', - 'Полностью обновлен': 'Fully updated', - 'Современный дизайн': 'Modern design', - 'Улучшенный индикатор': 'Improved indicator', - - 'Готово к публикации': 'Ready for Publication', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Автоматическая синхронизация команд': 'Automatic command synchronization', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - 'Современный дизайн-система внедрена': 'Modern design system implemented', - 'Поддержка светлой и темной темы': 'Support for light and dark themes', - - 'План публикации': 'Publication Plan', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - - // Architecture - 'Архитектура: Sidepanel и контекстная логика': 'Architecture: Sidepanel and Context Logic', - 'Sidepanel расширения открывается и закрывается пользователем': 'Extension sidepanel opens and closes by user', - 'Содержимое боковой панели зависит от текущей web-страницы': 'Side panel content depends on current web page', - 'Список доступных плагинов в сайдпанели зависит от разрешений': 'List of available plugins in sidepanel depends on permissions', - 'Сайдпанель не работает как отдельная extension page': 'Sidepanel does not work as separate extension page', - - // E2E Testing - 'E2E-тестирование чата плагина Ozon в Chromium': 'E2E Testing of Ozon Plugin Chat in Chromium', - 'Цель': 'Goal', - 'Полностью автоматизировать сценарий тестирования': 'Fully automate testing scenario', - 'Этапы и прогресс': 'Stages and Progress', - 'Проанализирован существующий e2e-тест': 'Analyzed existing e2e test', - 'Подтверждено, что тест уже реализует': 'Confirmed that test already implements', - 'Открытие страницы Ozon': 'Opening Ozon page', - 'Открытие сайдпанели': 'Opening sidepanel', - 'Проверку наличия плагина Ozon': 'Checking for Ozon plugin', - 'Клик по плагину и открытие чата': 'Click on plugin and open chat', - 'Отправку сообщения в чат': 'Sending message to chat', - 'Получение ответа от плагина': 'Receiving response from plugin', - 'Протестирован существующий тест в Chromium': 'Tested existing test in Chromium', - 'Выявлены и исправлены проблемы с селекторами': 'Identified and fixed selector issues', - 'Добавлена поддержка Chromium-specific селекторов': 'Added support for Chromium-specific selectors', - 'Оптимизированы таймауты для стабильности': 'Optimized timeouts for stability', - 'Добавлена обработка ошибок и retry логика': 'Added error handling and retry logic', - 'Создана документация по тестированию': 'Created testing documentation', - - 'Текущий статус': 'Current Status', - 'Готово к использованию': 'Ready for Use', - 'Тест полностью функционален в Chromium': 'Test is fully functional in Chromium', - 'Готов для CI/CD интеграции': 'Ready for CI/CD integration', - - 'Следующие шаги': 'Next Steps', - 'Интеграция в CI/CD pipeline': 'Integration into CI/CD pipeline', - 'Добавление тестов для других плагинов': 'Adding tests for other plugins', - 'Расширение покрытия тестирования': 'Expanding test coverage', - 'Оптимизация производительности тестов': 'Optimizing test performance' - }; - - function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(contextTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'g'), english); - } - - return translatedText; - } - - // Translate activeContext.md - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - // Create backup - const backupPath = path.join(BACKUP_DIR, `activeContext-${timestamp}.md`); - fs.copyFileSync(ACTIVE_CONTEXT_PATH, backupPath); - - const content = fs.readFileSync(ACTIVE_CONTEXT_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(ACTIVE_CONTEXT_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/activeContext.md'); - console.log(`📁 Backup: ${backupPath}`); - } - - // Translate progress.md - if (fs.existsSync(PROGRESS_PATH)) { - // Create backup - const backupPath = path.join(BACKUP_DIR, `progress-${timestamp}.md`); - fs.copyFileSync(PROGRESS_PATH, backupPath); - - const content = fs.readFileSync(PROGRESS_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(PROGRESS_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/progress.md'); - console.log(`📁 Backup: ${backupPath}`); - } - - console.log('\n🎉 Context translation completed for AI/LLM compatibility!'); -} - -function syncCommands() { - console.log('🔄 Syncing commands between all sources...\n'); - - // Generate USER_COMMANDS.md - const userCommandsContent = generateUserCommandsMD(); - fs.writeFileSync(USER_COMMANDS_PATH, userCommandsContent, 'utf8'); - console.log('✅ Updated USER_COMMANDS.md'); - - // Generate ai-memory.mdc - const aiMemoryContent = generateAIMemoryMD(); - fs.writeFileSync(AI_MEMORY_PATH, aiMemoryContent, 'utf8'); - console.log('✅ Updated .cursor/rules/cursor-export/ai-memory.mdc'); - - // Generate Cursor memory-bank - const cursorMemoryContent = generateCursorMemoryBank(); - fs.writeFileSync(CURSOR_MEMORY_PATH, cursorMemoryContent, 'utf8'); - console.log('✅ Updated .cursor/rules/ai-memory.mdc'); - - console.log('\n🎉 All command files synchronized successfully!'); -} - -function exportForCursor() { - console.log('📤 Exporting commands for Cursor AI memory-bank...\n'); - - let cursorFormat = `# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - const emoji = getCategoryEmoji(category); - cursorFormat += `### ${emoji} ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - cursorFormat += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - cursorFormat += '\n'; - } - - cursorFormat += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices - -## Usage Instructions: -1. Copy this content -2. Go to Cursor Settings → AI → Rules & Memories -3. Paste into User Rules or Project Rules -4. Save and restart Cursor -`; - - const exportPath = path.join(process.cwd(), 'CURSOR_AI_MEMORY_BANK.md'); - fs.writeFileSync(exportPath, cursorFormat, 'utf8'); - console.log(`✅ Exported to ${exportPath}`); - console.log('\n📋 Instructions:'); - console.log('1. Copy the content from CURSOR_AI_MEMORY_BANK.md'); - console.log('2. Go to Cursor Settings → AI → Rules & Memories'); - console.log('3. Paste into User Rules or Project Rules'); - console.log('4. Save and restart Cursor'); -} - -function main() { - const command = process.argv[2] || 'sync'; - - switch (command) { - case 'sync': - syncCommands(); - break; - case 'export': - exportForCursor(); - break; - case 'translate-context': - translateContextToEnglish(); - break; - case 'help': - console.log(` -Command Synchronization Script - -Usage: node .cursor/rules/command-sync.cjs [command] - -Commands: - sync - Sync all command files (default) - export - Export commands for Cursor AI memory-bank - translate-context - Translate context to English for AI/LLM compatibility - help - Show this help - -Examples: - node .cursor/rules/command-sync.cjs sync - node .cursor/rules/command-sync.cjs export - node .cursor/rules/command-sync.cjs translate-context - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { commandCategories, generateUserCommandsMD, generateAIMemoryMD, generateCursorMemoryBank, translateContextToEnglish }; \ No newline at end of file diff --git a/.cursor/rules/context-translator.cjs b/.cursor/rules/context-translator.cjs deleted file mode 100644 index 83673b13..00000000 --- a/.cursor/rules/context-translator.cjs +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env node - -/** - * Context Translator Script - * Automatically translates context to English for AI/LLM compatibility - * Usage: node .cursor/rules/context-translator.cjs [translate|backup|restore] - */ - -const fs = require('fs'); -const path = require('path'); - -// File paths -const ACTIVE_CONTEXT_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'activeContext.md'); -const PROGRESS_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'progress.md'); -const BACKUP_DIR = path.join(process.cwd(), 'memory-bank', 'core', 'backup'); - -// Translation mappings for context -const contextTranslations = { - // Headers and titles - 'Активный контекст разработки': 'Active Development Context', - 'Текущий статус проекта': 'Current Project Status', - 'Последнее обновление': 'Last Updated', - 'Сессия интернационализации и синхронизации команд': 'Internationalization and Command Synchronization Session', - - // Task status - 'Завершенные задачи': 'Completed Tasks', - 'Текущий фокус': 'Current Focus', - 'Приоритет': 'Priority', - 'Следующие шаги': 'Next Steps', - 'Готовность к международному сообществу и глобальному использованию': 'Readiness for International Community and Global Usage', - - // Principles - 'Ключевые принципы работы': 'Key Working Principles', - 'Инициативность ассистента': 'Assistant Initiative', - 'Всегда предлагать улучшения и оптимизации': 'Always suggest improvements and optimizations', - 'Конструктивно критиковать существующие решения': 'Constructively criticize existing solutions', - 'Предлагать альтернативные подходы': 'Suggest alternative approaches', - 'Проактивно выявлять потенциальные проблемы': 'Proactively identify potential issues', - - 'Качество кода': 'Code Quality', - 'Следовать принципам из': 'Follow principles from', - 'Применять "Do No Harm" принцип': 'Apply "Do No Harm" principle', - 'Использовать AI-First документацию': 'Use AI-First documentation', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - - 'Internationalization': 'Internationalization', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Универсальный формат команд': 'Universal command format', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Technical context - 'Технический контекст': 'Technical Context', - 'Текущая архитектура': 'Current Architecture', - 'React + TypeScript для UI компонентов': 'React + TypeScript for UI components', - 'Модульная система пакетов': 'Modular package system', - 'Vite для сборки': 'Vite for building', - 'Tailwind CSS для стилизации': 'Tailwind CSS for styling', - 'Chrome Extension API для интеграции': 'Chrome Extension API for integration', - 'Система синхронизации команд': 'Command synchronization system', - - 'Стандарты разработки': 'Development Standards', - 'TypeScript для всех новых файлов': 'TypeScript for all new files', - 'ESLint для проверки кода': 'ESLint for code checking', - 'Компонентный подход с proper accessibility': 'Component approach with proper accessibility', - 'Структурированное логирование': 'Structured logging', - 'Комплексная документация с примерами': 'Comprehensive documentation with examples', - 'Английский язык для всех правил и документации': 'English language for all rules and documentation', - - 'Безопасность': 'Security', - 'Zero Trust архитектура для плагинов': 'Zero Trust architecture for plugins', - 'Валидация всех входных данных': 'Validation of all input data', - 'Шифрование чувствительной информации': 'Encryption of sensitive information', - 'Аудит всех действий плагинов': 'Audit of all plugin actions', - - // Command system - 'Система команд': 'Command System', - 'Автоматическая синхронизация': 'Automatic Synchronization', - 'Единый источник истины': 'Single source of truth', - 'Автоматическая генерация': 'Automatic generation', - 'Множественные форматы': 'Multiple formats', - 'Категории команд': 'Command Categories', - 'Сохранение и восстановление контекста': 'Context saving and restoration', - 'Анализ архитектуры и изучение плагинов': 'Architecture analysis and plugin study', - 'Создание плагинов и проверка кода': 'Plugin creation and code checking', - 'Управление версиями и анализ': 'Version management and analysis', - 'Создание релизов и сборка': 'Release creation and building', - - 'Интеграция с Cursor': 'Cursor Integration', - 'Экспорт команд': 'Command export', - 'Файл для Cursor': 'File for Cursor', - 'Инструкции интеграции': 'Integration instructions', - 'Пошаговое руководство': 'Step-by-step guide', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Приоритеты UX': 'UX Priorities', - 'Интуитивность интерфейса': 'Interface intuitiveness', - 'Быстродействие и отзывчивость': 'Speed and responsiveness', - 'Доступность': 'Accessibility', - 'Консистентность дизайна': 'Design consistency', - 'Поддержка различных тем': 'Support for various themes', - 'Универсальность команд': 'Command universality', - - 'Метрики качества': 'Quality Metrics', - 'Время загрузки компонентов': 'Component loading time', - 'Плавность анимаций': 'Animation smoothness', - 'Доступность для скринридеров': 'Accessibility for screen readers', - 'Совместимость с различными браузерами': 'Compatibility with various browsers', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Development plans - 'Планы развития': 'Development Plans', - 'Краткосрочные цели': 'Short-term Goals', - 'Среднесрочные цели': 'Medium-term Goals', - 'Долгосрочные цели': 'Long-term Goals', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - 'Расширение системы команд новыми категориями': 'Expanding command system with new categories', - 'API интеграция с Cursor для автоматических обновлений': 'API integration with Cursor for automatic updates', - 'Создание шаблонов команд для разных типов проектов': 'Creating command templates for different project types', - 'Развитие экосистемы плагинов': 'Development of plugin ecosystem', - 'Создание полноценной международной платформы': 'Creating a full-fledged international platform', - 'Развитие глобального сообщества разработчиков': 'Development of global developer community', - 'Интеграция с популярными сервисами': 'Integration with popular services', - 'Многоязычная поддержка интерфейса': 'Multilingual interface support', - - // Important files - 'Важные файлы и ресурсы': 'Important Files and Resources', - 'Система синхронизации команд': 'Command Synchronization System', - 'Основной скрипт синхронизации': 'Main synchronization script', - 'Документация системы': 'System documentation', - 'Пользовательский справочник команд': 'User command reference', - 'Экспорт для Cursor': 'Export for Cursor', - - 'Ключевые компоненты': 'Key Components', - 'Карточки плагинов': 'Plugin cards', - 'Панель управления': 'Control panel', - 'Детали плагина': 'Plugin details', - 'Индикатор статуса': 'Status indicator', - - 'Документация': 'Documentation', - 'Документация интернационализации': 'Internationalization documentation', - 'Документация системы команд': 'Command system documentation', - 'Принципы разработки': 'Development principles', - 'План улучшений UI': 'UI improvement plan', - 'Долгосрочные планы': 'Long-term plans', - - 'Конфигурация': 'Configuration', - 'UI компоненты и стили': 'UI components and styles', - 'Конфигурация сборки': 'Build configuration', - 'Общие утилиты': 'Common utilities', - - // Commands and processes - 'Команды и процессы': 'Commands and Processes', - 'Система синхронизации команд': 'Command Synchronization System', - 'Синхронизация всех файлов': 'Synchronize all files', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Справка по командам': 'Command help', - - 'Сборка проекта': 'Project Build', - 'Сборка всех страниц': 'Build all pages', - 'Сборка конкретной страницы': 'Build specific page', - 'Разработка': 'Development', - - 'Тестирование': 'Testing', - 'Использовать DevTools панель': 'Use DevTools panel', - 'Тестировать в боковой панели расширения': 'Test in extension side panel', - 'Проверять в различных темах': 'Check in various themes', - 'Тестировать все обновленные компоненты': 'Test all updated components', - 'Проверять синхронизацию команд': 'Check command synchronization', - - 'Git workflow': 'Git Workflow', - 'Создавать feature ветки для новых функций': 'Create feature branches for new functions', - 'Создавать fix ветки для исправлений': 'Create fix branches for fixes', - 'Использовать осмысленные названия веток': 'Use meaningful branch names', - 'Вливать через pull requests': 'Merge through pull requests', - - // Contacts and support - 'Контакты и поддержка': 'Contacts and Support', - 'Для пользователей': 'For Users', - 'Документация в memory-bank': 'Documentation in memory-bank', - 'Тестирование через DevTools панель': 'Testing through DevTools panel', - 'Обратная связь через GitHub Issues': 'Feedback through GitHub Issues', - 'Справочник команд в USER_COMMANDS.md': 'Command reference in USER_COMMANDS.md', - - 'Для разработчиков': 'For Developers', - 'Следовать принципам из': 'Follow principles from', - 'Использовать модульную архитектуру': 'Use modular architecture', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - 'Документировать все изменения': 'Document all changes', - 'Использовать систему синхронизации команд': 'Use command synchronization system', - - // Status - 'Статус готовности к международному сообществу': 'Readiness Status for International Community', - 'Готовые компоненты': 'Ready Components', - 'Полная интернационализация .cursor и memory-bank': 'Complete internationalization of .cursor and memory-bank', - 'Система синхронизации команд': 'Command synchronization system', - 'Универсальный формат команд': 'Universal command format', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Полностью модернизирован': 'Fully modernized', - 'Полностью обновлен': 'Fully updated', - 'Современный дизайн': 'Modern design', - 'Улучшенный индикатор': 'Improved indicator', - - 'Готово к публикации': 'Ready for Publication', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Автоматическая синхронизация команд': 'Automatic command synchronization', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - 'Современный дизайн-система внедрена': 'Modern design system implemented', - 'Поддержка светлой и темной темы': 'Support for light and dark themes', - - 'План публикации': 'Publication Plan', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - - // Architecture - 'Архитектура: Sidepanel и контекстная логика': 'Architecture: Sidepanel and Context Logic', - 'Sidepanel расширения открывается и закрывается пользователем': 'Extension sidepanel opens and closes by user', - 'Содержимое боковой панели зависит от текущей web-страницы': 'Side panel content depends on current web page', - 'Список доступных плагинов в сайдпанели зависит от разрешений': 'List of available plugins in sidepanel depends on permissions', - 'Сайдпанель не работает как отдельная extension page': 'Sidepanel does not work as separate extension page', - - // E2E Testing - 'E2E-тестирование чата плагина Ozon в Chromium': 'E2E Testing of Ozon Plugin Chat in Chromium', - 'Цель': 'Goal', - 'Полностью автоматизировать сценарий тестирования': 'Fully automate testing scenario', - 'Этапы и прогресс': 'Stages and Progress', - 'Проанализирован существующий e2e-тест': 'Analyzed existing e2e test', - 'Подтверждено, что тест уже реализует': 'Confirmed that test already implements', - 'Открытие страницы Ozon': 'Opening Ozon page', - 'Открытие сайдпанели': 'Opening sidepanel', - 'Проверку наличия плагина Ozon': 'Checking for Ozon plugin', - 'Клик по плагину и открытие чата': 'Click on plugin and open chat', - 'Отправку сообщения в чат': 'Sending message to chat', - 'Получение ответа от плагина': 'Receiving response from plugin', - 'Протестирован существующий тест в Chromium': 'Tested existing test in Chromium', - 'Выявлены и исправлены проблемы с селекторами': 'Identified and fixed selector issues', - 'Добавлена поддержка Chromium-specific селекторов': 'Added support for Chromium-specific selectors', - 'Оптимизированы таймауты для стабильности': 'Optimized timeouts for stability', - 'Добавлена обработка ошибок и retry логика': 'Added error handling and retry logic', - 'Создана документация по тестированию': 'Created testing documentation', - - 'Текущий статус': 'Current Status', - 'Готово к использованию': 'Ready for Use', - 'Тест полностью функционален в Chromium': 'Test is fully functional in Chromium', - 'Готов для CI/CD интеграции': 'Ready for CI/CD integration', - - 'Следующие шаги': 'Next Steps', - 'Интеграция в CI/CD pipeline': 'Integration into CI/CD pipeline', - 'Добавление тестов для других плагинов': 'Adding tests for other plugins', - 'Расширение покрытия тестирования': 'Expanding test coverage', - 'Оптимизация производительности тестов': 'Optimizing test performance' -}; - -function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(contextTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'g'), english); - } - - return translatedText; -} - -function createBackup() { - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - const backupPath = path.join(BACKUP_DIR, `activeContext-${timestamp}.md`); - fs.copyFileSync(ACTIVE_CONTEXT_PATH, backupPath); - console.log(`✅ Backup created: ${backupPath}`); - } - - if (fs.existsSync(PROGRESS_PATH)) { - const backupPath = path.join(BACKUP_DIR, `progress-${timestamp}.md`); - fs.copyFileSync(PROGRESS_PATH, backupPath); - console.log(`✅ Backup created: ${backupPath}`); - } -} - -function translateContext() { - console.log('🔄 Translating context to English...\n'); - - // Create backup - createBackup(); - - // Translate activeContext.md - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - const content = fs.readFileSync(ACTIVE_CONTEXT_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(ACTIVE_CONTEXT_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/activeContext.md'); - } - - // Translate progress.md - if (fs.existsSync(PROGRESS_PATH)) { - const content = fs.readFileSync(PROGRESS_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(PROGRESS_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/progress.md'); - } - - console.log('\n🎉 Context translation completed!'); - console.log('📝 Note: Backups created in memory-bank/core/backup/'); -} - -function restoreFromBackup(backupName) { - const backupPath = path.join(BACKUP_DIR, backupName); - - if (!fs.existsSync(backupPath)) { - console.log(`❌ Backup not found: ${backupPath}`); - return; - } - - if (backupName.includes('activeContext')) { - fs.copyFileSync(backupPath, ACTIVE_CONTEXT_PATH); - console.log(`✅ Restored: memory-bank/core/activeContext.md from ${backupName}`); - } else if (backupName.includes('progress')) { - fs.copyFileSync(backupPath, PROGRESS_PATH); - console.log(`✅ Restored: memory-bank/core/progress.md from ${backupName}`); - } -} - -function listBackups() { - if (!fs.existsSync(BACKUP_DIR)) { - console.log('❌ No backups found'); - return; - } - - const files = fs.readdirSync(BACKUP_DIR); - if (files.length === 0) { - console.log('❌ No backups found'); - return; - } - - console.log('📁 Available backups:'); - files.forEach(file => { - const stats = fs.statSync(path.join(BACKUP_DIR, file)); - console.log(` ${file} (${stats.mtime.toLocaleString()})`); - }); -} - -function main() { - const command = process.argv[2] || 'translate'; - const backupName = process.argv[3]; - - switch (command) { - case 'translate': - translateContext(); - break; - case 'backup': - createBackup(); - break; - case 'restore': - if (!backupName) { - console.log('❌ Please specify backup name'); - console.log('Usage: node .cursor/rules/context-translator.cjs restore '); - return; - } - restoreFromBackup(backupName); - break; - case 'list': - listBackups(); - break; - case 'help': - console.log(` -Context Translator Script - -Usage: node .cursor/rules/context-translator.cjs [command] [backup-name] - -Commands: - translate - Translate context to English (default) - backup - Create backup of current context - restore - Restore from backup (requires backup name) - list - List available backups - help - Show this help - -Examples: - node .cursor/rules/context-translator.cjs translate - node .cursor/rules/context-translator.cjs backup - node .cursor/rules/context-translator.cjs restore activeContext-2024-07-19T10-30-00-000Z.md - node .cursor/rules/context-translator.cjs list - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { translateText, contextTranslations, createBackup, translateContext }; \ No newline at end of file diff --git a/.cursor/rules/create-rule-for.mdc b/.cursor/rules/create-rule-for.mdc deleted file mode 100644 index f43302e8..00000000 --- a/.cursor/rules/create-rule-for.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: create rule for architecture project -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "создай правило для архитектуры проекта" -translatedAt: "2025-07-19T02:58:58.601Z" ---- - -# create rule for architecture project - -## Overview - -This rule was created based on the request: "создай правило для архитектуры проекта" - -## Description - -create rule for architecture project - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "создай правило для архитектуры проекта"* -*Translated to English for AI/LLM compatibility* diff --git a/.cursor/rules/create-rule.cjs b/.cursor/rules/create-rule.cjs deleted file mode 100644 index 90023503..00000000 --- a/.cursor/rules/create-rule.cjs +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env node - -/** - * Quick Rule Creation Helper - * Automatically translates user requests and creates .cursor rules in English - * Usage: node .cursor/rules/create-rule.cjs "your request in Russian" - */ - -const fs = require('fs'); -const path = require('path'); -const { generateEnglishPrompt } = require('./request-translator.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const RULES_DIR = path.join(CURSOR_DIR, 'rules'); - -function generateFilename(request) { - // Extract key terms from request - const terms = request.toLowerCase() - .replace(/[^\w\s]/g, '') - .split(/\s+/) - .filter(word => word.length > 2) - .slice(0, 3); - - // Create filename - const baseName = terms.join('-') || 'new-rule'; - return `${baseName}.mdc`; -} - -function generateRuleContent(prompt) { - const timestamp = new Date().toISOString(); - const originalRequest = prompt.originalRequest; - const translatedRequest = prompt.translatedRequest || prompt.originalRequest; - - return `--- -description: ${translatedRequest} -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "${originalRequest}" -translatedAt: "${timestamp}" ---- - -# ${translatedRequest} - -## Overview - -This rule was created based on the request: "${originalRequest}" - -## Description - -${translatedRequest} - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "${originalRequest}"* -*Translated to English for AI/LLM compatibility* -`; -} - -async function createRule(request) { - try { - console.log('🤖 Processing your request...\n'); - - // Generate English prompt - const prompt = generateEnglishPrompt(request); - - // Display translation info - console.log('📊 Translation Info:'); - console.log(`Original: ${prompt.originalRequest}`); - console.log(`Translated: ${prompt.translatedRequest}`); - console.log(`Confidence: ${prompt.confidence.toFixed(1)}%`); - - if (prompt.shouldTranslate) { - console.log('\n✅ Request translated to English for AI/LLM compatibility'); - } else { - console.log('\n✅ Request already in English'); - } - - // Generate filename and content - const filename = generateFilename(prompt.translatedRequest || prompt.originalRequest); - const filepath = path.join(RULES_DIR, filename); - const ruleContent = generateRuleContent(prompt); - - // Ensure directory exists - const dir = path.dirname(filepath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - // Write the rule file - fs.writeFileSync(filepath, ruleContent, 'utf8'); - - console.log(`\n✅ Rule created: ${path.relative(process.cwd(), filepath)}`); - console.log(`📁 Location: ${filepath}`); - - // Stage the file for git - const { execSync } = require('child_process'); - try { - execSync(`git add "${filepath}"`, { stdio: 'inherit' }); - console.log('📦 File staged for git'); - } catch (error) { - console.log('⚠️ Could not stage file for git'); - } - - // Show the created content - console.log('\n📝 Created Rule Content:'); - console.log('='.repeat(60)); - console.log(ruleContent); - console.log('='.repeat(60)); - - return filepath; - } catch (error) { - console.error('❌ Error creating rule:', error.message); - throw error; - } -} - -function main() { - const request = process.argv[2]; - - if (!request) { - console.log('❌ Please provide a request'); - console.log('\nUsage: node .cursor/rules/create-rule.cjs "your request"'); - console.log('\nExamples:'); - console.log(' node .cursor/rules/create-rule.cjs "создай правило для архитектуры"'); - console.log(' node .cursor/rules/create-rule.cjs "добавь файл в .cursor"'); - console.log(' node .cursor/rules/create-rule.cjs "напиши правило безопасности"'); - console.log('\nOr use npm script:'); - console.log(' npm run create-rule "your request"'); - process.exit(1); - } - - createRule(request) - .then(() => { - console.log('\n🎉 Rule created successfully!'); - console.log('\n💡 Next steps:'); - console.log(' 1. Edit the rule file to add your specific content'); - console.log(' 2. Commit the changes: git commit -m "feat: add new rule"'); - console.log(' 3. The rule will be automatically protected by the cursor protection system'); - }) - .catch((error) => { - console.error('\n❌ Failed to create rule:', error.message); - process.exit(1); - }); -} - -if (require.main === module) { - main(); -} - -module.exports = { createRule, generateFilename, generateRuleContent }; \ No newline at end of file diff --git a/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md b/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md deleted file mode 100644 index cb3f3798..00000000 --- a/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Import .cursor Rules - -## Quick Import - -1. Copy the entire `cursor-export` folder to your target project -2. Run the import script: - ```bash - node cursor-export/import-cursor..js - ``` - -## Manual Import - -1. Create `.cursor/rules/` directory in your target project -2. Copy files from `cursor-export/` to `.cursor/rules/` -3. Run audit and optimization: - ```bash - cd .cursor/rules - node cursor-manager..js full - ``` - -## What's Included - -### Core Rules -- AI memory and Commands -- Environment constrai.ts -- Project structure guIDElines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guIDElines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update `environment.mdc` with your project constrai.ts -2. Modify `ai-memory.mdc` with project-specific Commands -3. Adjust `monorepo-best-practices.mdc` for your structure -4. Run `node cursor-manager..js optimize` to apply changes - -## Verification - -Verify successful import: -```bash -cd .cursor/rules -node cursor-manager..js status -``` - -All files should show as "AI-ready" with no issues. diff --git a/.cursor/rules/cursor-export/ai-memory.mdc b/.cursor/rules/cursor-export/ai-memory.mdc deleted file mode 100644 index 3a7e484d..00000000 --- a/.cursor/rules/cursor-export/ai-memory.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Universal user Commands for AI assistant - complete Command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc b/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc deleted file mode 100644 index efca4815..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 1: Do No Harm -- Security first: Any changes must improve system security, not weaken it -- Backward compatibility: Changes must not break existing functionality -- Gradual implementation: Implement changes step by step with rollback capability -- Testing: All changes must pass thorough testing before deployment -- Monitoring: Track impact of changes on performance and stability - -description: 'Do No Harm' principle for all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc b/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc deleted file mode 100644 index 7d2b513f..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 3: Best Practices First -- Architectural patterns: Use proven architectural patterns and principles -- Coding standards: Follow accepted standards and conventions -- Security: Apply security principles (Zero Trust, Defense in Depth, Least Privilege) -- Performance: Optimize critical paths and avoid anti-patterns -- Scalability: Design consIDEring future growth and changes -- Testability: Write code that is easy to test and maintain - -description: Prioritize best practices in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc b/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc deleted file mode 100644 index 743a5fa7..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 4: Fail Fast, Fail Safe -- Early error detection: Validate at input, not during execution -- Graceful Degradation: System continues working even with partial failures -- Circuit breaker: Automatic shutdown of problematic compone.ts -- Rollback capability: Quick rollback to working State -- Error boundaries: Isolate errors at component level - -description: Fail fast and fail safe in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc b/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc deleted file mode 100644 index d46bceaa..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 5: Observability First -- Structured logging: Structured logging for analysis -- Metrics everywhere: Performance and State metrics -- distributed tracing: Track reque.ts through all compone.ts -- Health checks: Monitor State of all services -- Debug information: Sufficient information for problem diagnosis - -description: Observability and monitoring in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc b/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc deleted file mode 100644 index 90cf296d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 6: configuration as Code -- Version-controlled configs: All configurations in version control -- Environment-specific: Different configurations for different environme.ts -- Validation: Automatic configuration validation -- Documentation: Document all configuration parameters -- Default safety: Safe default values - -description: configuration as code for all environme.ts -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc b/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc deleted file mode 100644 index 4cb14008..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 7: Progressive Enhancement -- Core functionality: Basic functionality always works -- Feature detection: Detect browser capabilities -- Graceful Degradation: Degrade functionality, not complete failure -- Performance budget: Performance budget for new features -- Accessibility baseline: Minimum accessibility level for all - -description: Progressive enhancement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc b/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc deleted file mode 100644 index 33209d0d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 8: data Integrity & Privacy -- data validation: Validate all input data -- Encryption at rest: Encrypt data at rest -- Encryption in transit: Encrypt data in transit -- data minimization: Collect only necessary data -- User consent: Explicit user consent for data processing -- Right to be forgotten: Ability to delete user data - -description: data integrity and privacy in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc b/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc deleted file mode 100644 index f298f32d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 9: Continuous Learning -- Performance monitoring: Continuous performance monitoring -- User feedback loops: User feedback -- A/B testing: Test hypotheses on real users -- Analytics insig.ts: Usage analysis for improveme.ts -- Knowledge sharing: Document.lessons and insig.ts - -description: Continuous learning and improvement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc b/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc deleted file mode 100644 index 5a16b370..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 10: Ecosystem Thinking -- Plugin compatibility: Ensure plugin compatibility -- API stability: Stable public APIs -- Backward compatibility: Backward version compatibility -- Community building: Support developer community -- Documentation quality: Quality documentation for ecosystem - -description: Ecosystem thinking in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/development-guidelines.mdc b/.cursor/rules/cursor-export/dev/development-guidelines.mdc deleted file mode 100644 index 535e47d1..00000000 --- a/.cursor/rules/cursor-export/dev/development-guidelines.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: General development guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: false -aiCategory: development-practices--- - -# Development GuIDElines - -## JavaScript/TypeScript -- Use ES6+ and async/await. -- Modular architecture, clear separation of concerns. -- Error handling and structured logging are required. -- Use TypeScript for type safety. -- Prefer functional patterns where possible. - -## Python (Plugins) -- Use MCP protocol for communication. -- Use async/await for I/O operations. -- Always validate inp.ts and handle errors. -- Plugins should be single-purpose. -- Document all plugin APIs clearly. - -# Security First -- Validate manife.ts and permissions. -- Sanitize data between.js and Python. -- Sandbox all plugins. -- Apply the principle of least privilege. -- Audit all plugin code. - -# Performance ConsIDErations -- Lazy-load plugins and PyodIDE when possible. -- cache repeated operations. -- Monitor memory usage and clean up resources. -- Optimize bundle size and loading time. -- Use WebWorkers for non-blocking tasks. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/git-workflow.mdc b/.cursor/rules/cursor-export/dev/git-workflow.mdc deleted file mode 100644 index 8dadc7b2..00000000 --- a/.cursor/rules/cursor-export/dev/git-workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git workflow rule - только develop в main, обязательные PR для всех изменений -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Git Workflow Rule - -**Rule:** -- Only merge into main from develop. -- All feature, fix, and doc branches must be merged into develop first, never directly into main. -- This rule is mandatory for all changes and pull reque.ts. - ---- -## Enforcement -- Local pre-push hook (.husky/pre-push) blocks direct push to main and develop. -- GitHub branch protection preve.ts direct push and enforces PRs from develop only. ---- \ No newline at end of file diff --git a/.cursor/rules/cursor-export/dev/security-and-deployment.mdc b/.cursor/rules/cursor-export/dev/security-and-deployment.mdc deleted file mode 100644 index d5f45cd4..00000000 --- a/.cursor/rules/cursor-export/dev/security-and-deployment.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Security rule for -and-deployment -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Security Best Practices - -## Plugin Validation -- Validate plugin manife.ts. -- Check all permissions. -- Scan for malicious code. -- Verify plugin signatures. - -## Runtime Security -- Sandbox all plugins. -- Validate all input data. -- Monitor plugin behavior. -- Apply rate limiting. - -## data Protection -- Sanitize all user data. -- Encrypt sensitive information. -- Ensure secure communication. -- Audit all data access. - -# Deployment ConsIDErations - -## Extension distribution -- Follow Chrome Web Store guIDElines. -- Use a secure update mechanism. -- ProvIDE a transparent privacy policy. -- Maintain user support channels. - -## Plugin distribution -- Use a marketplace for plugins. -- Validate and manage plugin versions. -- Perform security scanning. - -## Monitoring and Analytics -- Track plugin usage. -- Monitor performance. -- Collect error repo.ts. -- Analyze user feedback. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc b/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc deleted file mode 100644 index 3008bfad..00000000 --- a/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing Strategy - -## Unit Testing -- Test compone.ts in isolation. -- Mock external dependencies. -- Validate error handling. -- Test security boundaries. - -## Integration Testing -- Test plugin loading and execution. -- Validate.js-Python communication. -- Test integration with browser APIs. -- Check permission compliance. - -## End-to-End Testing -- Test full plugin workflows. -- Validate user scenarios. -- Test extension installation and updates. -- Check cross-browser compatibility. - -# Debugging GuIDElines - -## JavaScript Debugging -- Use DevTools for extension debugging. -- Log message flow. -- Use breakpoi.ts for complex logic. -- Monitor WebWorker communication. - -## Python Debugging -- Use print/logging. -- Test plugins in isolation. -- Use PyodIDE console for debugging. - -## Common Issues -- PyodIDE startup: use loading indicators. -- Memory leaks: monitor worker lifecycle. -- Permission errors: validate manife.ts. -- Communication failures: check MCP format. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/typescript.mdc b/.cursor/rules/cursor-export/dev/typescript.mdc deleted file mode 100644 index 7a1218c4..00000000 --- a/.cursor/rules/cursor-export/dev/typescript.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: TypeScript-specific guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Rule -- Use only TypeScript for all new files. -- Enforce strict typing, avoid using `any`. -- Use modern language features (optional chaining, nullish coalescing, etc.). -- Always follow the shared.tsconfig from `packages.tsconfig/`. - -# examples -- ✅ `const foo: string = "bar"` -- ❌ `let x: any = 5` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/ai-fallback.mdc b/.cursor/rules/cursor-export/doc/ai-fallback.mdc deleted file mode 100644 index b9192a53..00000000 --- a/.cursor/rules/cursor-export/doc/ai-fallback.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Fallback procedures for AI -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: Правило fallback для AI - Priority источников информации при отсутствии ответа в memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI Fallback Rule - -**Правило:** -Если возникает вопрос, на который нет ответа в AI memory-bank, необходимо: -1. Сначала обращаться к сведениям в папке .rules проекта. -2. Если нужной информации нет в .rules, обращаться к сведениям в папке memory-bank проекта (особенно к архитектурным и контекстным файлам). - -**Priority:** -Это правило Priorityно для всех будущих консультаций и автоматизаций. \ No newline at end of file diff --git a/.cursor/rules/cursor-export/doc/ai-first.mdc b/.cursor/rules/cursor-export/doc/ai-first.mdc deleted file mode 100644 index 20ea682c..00000000 --- a/.cursor/rules/cursor-export/doc/ai-first.mdc +++ /dev/null @@ -1,69 +0,0 @@ ---- -description: AI-oriented documentation standards -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: AI-First Documentation Standards - analytical comme.ts and AI-oriented documentation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI-First Documentation Standards - -## Основные Principles -- **Analytical comme.ts**: Add comme.ts explaining logic and architectural decisions for AI understanding -- **Context explanations**: Explain "why", not just "what" the code does -- **Architectural comme.ts**: Document component relationships and data flows -- **Business logic**: Explain complex business logic and decisions made -- **TODO comme.ts**: Leave TODO with explanation of planned improveme.ts - -## examples правильного использования - -### ✅ Аналитические Comme.ts: -```TypeScript -// AI: This function is required for plugin isolation - preve.ts direct DOM access -function createSandboxedEnvironment() { - // Implementation... -} - -/** - * AI: Example usage - this component handles plugin communication - * through a secure message passing system - */ -export function PluginBridge() { - // Implementation... -} -``` - -### ✅ Объяснение контекста: -```TypeScript -// AI: Using setTimeout here because Chrome extension APIs are async -// and we need to ensure DOM is ready before accessing eleme.ts -setTimeout(() => { - initializePlugin(); -}, 100); -``` - -### ✅ TODO с объяснением: -```TypeScript -// TODO: AI - Replace with Web Workers for better performance -// Current implementation blocks main thread during heavy computations -function proceSSLargedataset(data) { - // Implementation... -} -``` - -## Recommendations -1. **Всегда объясняйте "почему"** - не только что делает код -2. **Используйте префикс "AI:"** для комментариев, предназначенных для AI -3. **Документируйте архитектурные решения** и их обоснование -4. **Объясняйте сложную бизнес-логику** с примерами -5. **Оставляйте TODO с контекстом** для будущих улучшений diff --git a/.cursor/rules/cursor-export/doc/knowledge-map.mdc b/.cursor/rules/cursor-export/doc/knowledge-map.mdc deleted file mode 100644 index f3bdcf53..00000000 --- a/.cursor/rules/cursor-export/doc/knowledge-map.mdc +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Project knowledge structure -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Knowledge Map - -- USER_CommandS: USER_CommandS.md — все пользовательские команды -- graveyard: memory-bank/graveyard.md — ошибки, environment-specific issues -- architecture: memory-bank/codebase-architecture.md — архитектурные решения, Patterns, Dependencies -- meta: memory-bank/README.md — мета-правила, структура, универсальные инструкции -- dev-principles: memory-bank/DEV_PRACTICES.md — best practices для разработки -- security: memory-bank/SECURITY.md — Standards безопасности -- knowledge-map: memory-bank/KNOWLEDGE_MAP.md — карта знаний -- progress: memory-bank/progress.md — Status и история -- activeContext: memory-bank/activeContext.md — актуальный контекст -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc b/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc deleted file mode 100644 index e370d93a..00000000 --- a/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc +++ /dev/null @@ -1,134 +0,0 @@ ---- -description: Standards for .mdc file creation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# Standards создания правил в .cursor/rules - -## Priority .mdc файлов над .md - -### Required использовать .mdc для: -- **Правил и стандартов** - все правила должны быть в .mdc -- **AI инструкций** - команды, Principles, Constrai.ts -- **Архитектурных решений** - структура, Patterns, best practices -- **automation** - скрипты, workflow, Processes - -### Допустимо использовать .md для: -- **Чистой документации** - README, guIDEs, tutorials -- **Временных заметок** - черновики, эксперименты -- **Внешних ссылок** - ссылки на внешние ресурсы - -## Структура .mdc файла - -### Обязательные метаdata: -``.yaml ---- -description: Краткое Description назначения файла -globs: ["**/*"] # Patterns файлов для применения -alwaysApply: true # Автоматическое применение ---- -``` - -### Дополнительные метаdata: -``.yaml ---- -description: Description -globs: ["pages/**/*", "packages/**/*"] -alwaysApply: false # Применяется по запросу -related: - - other-rule.mdc - - another-rule.mdc ---- -``` - -## Преимущества .mdc файлов - -### 1. Автоматическое применение -- Cursor читает метаdata при создании чата -- Правила применяются без явного обращения -- `alwaysApply: true` обеспечивает постоянное Action - -### 2. Лучшая Integration с AI -- AI понимает контекст применения через `globs` -- Description помогает AI выбрать подходящие правила -- Структурированные метаdata улучшают понимание - -### 3. Приоритизация и организация -- `alwaysApply` определяет Importantсть правила -- `related` связывает связанные правила -- `globs` ограничивает область применения - -### 4. AI memory-bank Integration -- Правила автоматически попадают в AI memory-bank -- Доступны через Settings / Rules & Memories -- Сохраняются между сессиями - -## Миграция .md → .mdc - -### Когда мигрировать: -- Файл содержит правила или инструкции -- Файл должен применяться автоматически -- Файл важен для AI понимания проекта - -### Процесс миграции: -1. Переименовать `.md` → `.mdc` -2. Add.yaml frontmatter с метаданными -3. Убедиться в корректности `globs` и `alwaysApply` -4. Check интеграцию с AI memory-bank - -## examples правильного использования - -### ✅ Правильно - .mdc с метаданными: -``.yaml ---- -description: Правила TypeScript - configuration, barrel expo.ts, troubleshooting -globs: ["**/*.ts", "**/*.tsx", "**/*.json"] -alwaysApply: true ---- -``` - -### ❌ Неправильно - .md без метаданных: -```markdown -# TypeScript Rules -- Use strict mode -- Prefer barrel expo.ts -``` - -## Exceptions - -### Допустимые .md файлы: -- `README.md` - главная Documentation проекта -- `CHANGELOG.md` - история изменений -- `LICENSE.md` - лицензия -- Временные файлы с префиксом `temp-` или `draft-` - -## Verification соответствия - -### Автоматическая Verification: -```bash -# Найти все .md файлы в .cursor/rules -find .cursor/rules -name "*.md" -not -name "README.md" - -# Check структуру .mdc файлов -node .cursor/rules/check-rules-structure..js -``` - -### Ручная Verification: -1. Все ли правила в .mdc формате? -2. Есть ли метаdata во всех .mdc файлах? -3. Корректны ли `globs` и `alwaysApply`? -4. Нужно ли мигрировать какие-то .md файлы? - -## Recommendations - -1. **Всегда начинать с .mdc** для новых правил -2. **Мигрировать важные .md** файлы в .mdc -3. **Проверять метаdata** при создании правил -4. **Использовать `alwaysApply: true`** для критических правил -5. **Документировать Exceptions** в README.mdc -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/memorybank-quality.mdc b/.cursor/rules/cursor-export/doc/memorybank-quality.mdc deleted file mode 100644 index 0e2c801c..00000000 --- a/.cursor/rules/cursor-export/doc/memorybank-quality.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Quality standards for memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Memory-Bank Quality Checklist - -- Актуальность: информация своевременно обновляется -- Полнота: все ключевые аспекты проекта отражены -- Нет дублирования: информация не повторяется в разных файлах -- Навигация: структура и ссылки понятны -- История изменений: все важные правки фиксируются -- Прозрачность: причины изменений и удаления всегда документируются -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/restore-context.mdc b/.cursor/rules/cursor-export/doc/restore-context.mdc deleted file mode 100644 index cbee881a..00000000 --- a/.cursor/rules/cursor-export/doc/restore-context.mdc +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: Context restoration procedures -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Восстанови контекст - -1. Прочитать все файлы в директории `memory-bank` и `USER_CommandS.md`. -2. Сделать краткое резюме ключевых положений, правил, пользовательских требований и текущего Statusа проекта. -3. Использовать эту информацию для всех последующих ответов и действий. - -> Это правило предназначено для ручного вызова или использования агентом при необходимости полного восстановления контекста проекта. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/environment.mdc b/.cursor/rules/cursor-export/environment.mdc deleted file mode 100644 index 9fb92cb1..00000000 --- a/.cursor/rules/cursor-export/environment.mdc +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Critical environment constrai.ts - Node.js версия, popup vs sIDEpanel -globs: ["**/*"] -alwaysApply: trueaiPriority: critical -aiCategory: general ---- - -# Critical environment constrai.ts - -- **popup не используется**, основной Interface — **sIDEpanel**. Все ошибки popup можно игнорировать, если они не влияют на работу sIDEpanel. - -- **Использовать только Node.js версии 22.17.1 и выше**. Понижать версию запрещено, все баги и инфраструктурные проблемы решать только в этом контексте. \ No newline at end of file diff --git a/.cursor/rules/cursor-export/import-cursor.cjs b/.cursor/rules/cursor-export/import-cursor.cjs deleted file mode 100755 index d70c8eed..00000000 --- a/.cursor/rules/cursor-export/import-cursor.cjs +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorImporter { - constructor() { - this.exportDir = __dirname; - this.targetRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async import() { - console.log('📥 Importing .cursor rules...\n'); - - // Создаем папку .cursor/rules если не существует - if (!fs.existsSync(this.targetRulesDir)) { - fs.mkdirSync(this.targetRulesDir, { recursive: true }); - console.log('✅ Created .cursor/rules directory'); - } - - // Копируем все файлы - await this.copyFiles(); - - // Копируем скрипты автоматизации - await this.copyAutomationScripts(); - - // Создаем базовую структуру если нужно - await this.createBasicStructure(); - - console.log('\n✅ Import completed!'); - console.log('\n🔧 Next steps:'); - console.log(' 1. Customize environment.mdc for your project'); - console.log(' 2. Update ai-memory.mdc with project-specific commands'); - console.log(' 3. Run: cd .cursor/rules && node cursor-manager.cjs full'); - } - - async copyFiles() { - console.log('📋 Copying rule files...'); - - const items = fs.readdirSync(this.exportDir); - - for (const item of items) { - const sourcePath = path.join(this.exportDir, item); - const targetPath = path.join(this.targetRulesDir, item); - - const stat = fs.statSync(sourcePath); - - if (stat.isDirectory()) { - // Копируем директории - if (!fs.existsSync(targetPath)) { - fs.mkdirSync(targetPath, { recursive: true }); - } - - const files = fs.readdirSync(sourcePath); - for (const file of files) { - if (file.endsWith('.mdc')) { - const fileSource = path.join(sourcePath, file); - const fileTarget = path.join(targetPath, file); - fs.copyFileSync(fileSource, fileTarget); - console.log(` ✅ ${item}/${file}`); - } - } - } else if (item.endsWith('.mdc')) { - // Копируем .mdc файлы - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${item}`); - } - } - } - - async copyAutomationScripts() { - console.log('🔧 Copying automation scripts...'); - - const scripts = [ - 'audit-cursor.cjs', - 'fix-cursor.cjs', - 'optimize-for-ai.cjs', - 'cursor-manager.cjs' - ]; - - for (const script of scripts) { - const sourcePath = path.join(this.exportDir, script); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.targetRulesDir, script); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${script}`); - } - } - } - - async createBasicStructure() { - console.log('🏗️ Creating basic structure...'); - - const requiredDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const dir of requiredDirs) { - const dirPath = path.join(this.targetRulesDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(` ✅ Created ${dir}/`); - } - } - } -} - -// Запуск импорта -async function main() { - const importer = new CursorImporter(); - await importer.import(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorImporter; diff --git a/.cursor/rules/cursor-export/index.mdc b/.cursor/rules/cursor-export/index.mdc deleted file mode 100644 index d0272f95..00000000 --- a/.cursor/rules/cursor-export/index.mdc +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: index rule -globs: ["**/*"] -alwaysApply: true -aiCategory: general--- - -# Index of Modular Rules - -## root -- [AI Memory Bank](ai-memory.mdc) — User Commands and AI instructions -- [Environment Constrai.ts](environment.mdc) — Critical environment limitations -- [Monorepo Best Practices](monorepo-best-practices.mdc) — Monorepo structure and guIDElines -- [TypeScript build Troubleshooting](TypeScript-build-troubleshooting.mdc) — TypeScript build error solutions -- [README](README.mdc) — Main documentation and structure - -## architecture -- [Chat System Architecture](architecture/architecture-chat-system.mdc) — Chat system architecture and data flow -- [Error Handling (Architecture)](architecture/architecture-error-handling.mdc) — Error handling requireme.ts for system architecture -- [Monitoring and Observability (Architecture)](architecture/architecture-observability.mdc) — Monitoring and observability requireme.ts for system architecture -- [Performance GuIDElines (Architecture)](architecture/architecture-performance.mdc) — Performance guIDElines for system architecture -- [Plugin Architecture](architecture/architecture-plugin.mdc) — Plugin architecture and isolation requireme.ts -- [Project Structure Architecture](architecture/architecture-project-structure.mdc) — Directory and component structure for the project -- [Security Architecture](architecture/architecture-security.mdc) — Security architecture and controls -- [Development Workflow (Architecture)](architecture/architecture-workflow.mdc) — Development workflow and process standards -- [Project Overview](architecture/project-architecture.mdc) - -## dev -- [Development Principle 1: Do No Harm](dev/dev-principle-01-do-no-harm.mdc) — 'Do No Harm' principle for all development -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation and comme.ts for all code -- [Development Principle 3: Best Practices First](dev/dev-principle-03-best-practices.mdc) — Prioritize best practices in all development -- [Development Principle 4: Fail Fast, Fail Safe](dev/dev-principle-04-fail-fast-safe.mdc) — Fail fast and fail safe in all development -- [Development Principle 5: Observability First](dev/dev-principle-05-observability.mdc) — Observability and monitoring in all development -- [Development Principle 6: configuration as Code](dev/dev-principle-06-config-as-code.mdc) — configuration as code for all environme.ts -- [Development Principle 7: Progressive Enhancement](dev/dev-principle-07-progressive-enhancement.mdc) — Progressive enhancement in all development -- [Development Principle 8: data Integrity & Privacy](dev/dev-principle-08-data-integrity-privacy.mdc) — data integrity and privacy in all development -- [Development Principle 9: Continuous Learning](dev/dev-principle-09-continuous-learning.mdc) — Continuous learning and improvement in all development -- [Development Principle 10: Ecosystem Thinking](dev/dev-principle-10-ecosystem-thinking.mdc) — Ecosystem thinking in all development -- [Development GuIDElines](dev/development-guIDElines.mdc) — General development guIDElines -- [Security Best Practices](dev/security-and-deployment.mdc) — Security and deployment standards -- [Testing Strategy](dev/testing-and-debugging.mdc) — Testing and debugging approaches -- [TypeScript Rules](dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Git Workflow](dev/git-workflow.mdc) — Git workflow and branching rules - -## doc -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation standards -- [Knowledge Map](doc/knowledge-map.mdc) — Project knowledge structure -- [Memory-Bank Quality Checklist](doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](doc/restore-context.mdc) — Context restoration procedures -- .mdc File Standards](doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [AI Fallback Rule](doc/ai-fallback.mdc) — Fallback procedures for AI - -## plugin -- [Plugin Best Practices](plugin/plugin-best-practices.mdc) — Best practices for plugin development -- [Plugin Documentation Standards](plugin/plugin-documentation.mdc) — Documentation standards for all plugins -- [Plugin Error Handling](plugin/plugin-error-handling.mdc) — Error handling requireme.ts for all plugins -- [Plugin Performance GuIDElines](plugin/plugin-performance.mdc) — Performance guIDElines for all plugins -- [Plugin Security Requireme.ts](plugin/plugin-security.mdc) — Security requireme.ts for all plugins -- [Plugin Structure](plugin/plugin-structure.mdc) — Plugin directory and manifest requireme.ts -- [Plugin Testing Requireme.ts](plugin/plugin-testing.mdc) — Testing requireme.ts for all plugins - -## security -- [Input Validation](security/validation.mdc) — Input validation and data sanitization rules - -## ui -- [Accessibility (A11y) Standards](ui/ui-accessibility.mdc) — Accessibility requireme.ts for all UI compone.ts -- [UI Animation and Transitions](ui/ui-animation.mdc) — Animation and transition standards for all UI compone.ts -- [UI Error Handling](ui/ui-error-handling.mdc) — Error handling requireme.ts for all UI compone.ts -- [UI Form Standards](ui/ui-forms.mdc) — Form standards for all UI compone.ts -- [UI Loading States](ui/ui-loading-States.mdc) — Loading State requireme.ts for all UI compone.ts -- [UI Mobile Responsiveness](ui/ui-mobile.mdc) — Mobile responsiveness standards for all UI compone.ts -- [UI Navigation Standards](ui/ui-navigation.mdc) — Navigation standards for all UI compone.ts -- [UI Performance Standards](ui/ui-performance.mdc) — Performance standards for all UI compone.ts -- [React Component Standards](ui/ui-React-compone.ts.mdc) — Standards for React component structure in UI -- [UI Styling Standards](ui/ui-styling.mdc) — Styling standards for all UI compone.ts -- [UI Testing Standards](ui/ui-testing.mdc) — Testing standards for all UI compone.ts - -## workflow -- [Automation Rules](workflow/automation.mdc) — Automation and synchronization rules -- [Branch Management](workflow/branches.mdc) — Git branch management rules -- [Workflow Branching Rules](workflow/workflow.mdc) — Development workflow standards \ No newline at end of file diff --git a/.cursor/rules/cursor-export/monorepo-best-practices.mdc b/.cursor/rules/cursor-export/monorepo-best-practices.mdc deleted file mode 100644 index f0a8387d..00000000 --- a/.cursor/rules/cursor-export/monorepo-best-practices.mdc +++ /dev/null @@ -1,208 +0,0 @@ ---- -description: Best practices for monorepo work - структура, Dependencies, TypeScript, barrel expo.ts -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# Monorepo Best Practices for AI - -## Основные Principles - -### 1. Project Structure -- **packages/** - общие Libraries и утилиты -- **pages/** - приложения и страницы Extensions -- **external/** - копии внешних boilerplate проектов -- **te.ts/** - тесты и e2e - -### 2. Dependencies -- **Root dependencies** - только общие инструменты (prettier, eslint, TypeScript) -- **Package dependencies** - устанавливать в конкретных пакетах -- **Workspace dependencies** - использовать `pnpm add -D package -w` для root - -### 3. TypeScript configuration -- **Base config** - `packages.tsconfig/base.json` для общих настроек -- **Package configs** - наследовать от base с специфичными настройками -- **App configs** - наследовать от base с browser-specific настройками - -## Правила для AI - -### При создании новых пакетов: - -1. **Create правильную структуру:** -``` -packages/new-package/ -├── lib/ -│ ├── index.ts -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -└── README.md -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "...tsconfig/base.json", - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - }, - "include": ["lib/**/*", "index..ts"], - "exclude": ["node_modules", "dist"] -} -``` - -3. **Настроить package.json:** -``.json -{ - "name": "@extension/new-package", - "main": "dist/index..js", - "types": "dist/index.d.ts", - "expo.ts": { - ".": "./dist/index..js" - }, - "files": ["dist"] -} -``` - -### При создании новых страниц: - -1. **Create правильную структуру:** -``` -pages/new-page/ -├── src/ -│ ├── index.tsx -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -├── pos.css.config..js -└── vite.config..ts -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "@extension.tsconfig/base", - "compilerOptions": { - "types": ["chrome"], - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -3. **Настроить Pos.css для Tailwin.css 4+:** -```JavaScript -// pos.css.config..js -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -### При работе с barrel expo.ts: - -1. **Default expo.ts:** -```TypeScript -// component.ts -export default function Component() { ... } - -// index.ts -export { default as Component } from './component.js'; -``` - -2. **Named expo.ts:** -```TypeScript -// component.ts -export function Component() { ... } - -// index.ts -export { Component } from './component'; -``` - -3. **Mixed expo.ts:** -```TypeScript -// index.ts -export * from './helpers.js'; -export { default as Component } from './component.js'; -export type * from './types.js'; -``` - -### При установке зависимостей: - -1. **В конкретном пакете:** -```bash -cd packages/package-name -pnpm add dependency-name -``` - -2. **В конкретной странице:** -```bash -cd pages/page-name -pnpm add dependency-name -``` - -3. **В workspace root:** -```bash -pnpm add -D dependency-name -w -``` - -### При диагностике проблем: - -1. **Check Dependencies:** -```bash -pnpm why package-name -pnpm list package-name -``` - -2. **Найти проблемные файлы:** -```bash -find . -name .tsconfig.json" -exec grep -l "node" {} \; -``` - -3. **Очистить и пересобрать:** -```bash -rm -rf dist && pnpm run build -``` - -## Частые ошибки и решения - -| Проблема | Решение | -|----------|---------| -| `Cannot find type definition file for 'node'` | Remove 'node' из types в.tsconfig.json | -| `"Component" is not exported by` | Использовать `export { default as Component } from './file.js'` | -| `Cannot find module 'tailwin.css'` | `pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css` | -| `Rollup failed to resolve import` | `pnpm add package-name` в конкретном пакете | -| `spawn prettier ENOENT` | `git commit --no-verify` | - -## Команды для работы - -```bash -# build всего проекта -pnpm run build - -# build конкретного пакета -pnpm -F @extension/package-name run build - -# Установка зависимостей -pnpm install - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache - -# Пропуск pre-commit хуков -git commit --no-verify -m "message" -``` - -## Recommendations - -1. **Всегда проверять сборку** после изменений -2. **Документировать решения** для повторного использования -3. **Использовать правильные barrel expo.ts** для default expo.ts -4. **Устанавливать Dependencies в правильных местах** -5. **Следовать структуре проекта** при создании новых файлов \ No newline at end of file diff --git a/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc b/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc deleted file mode 100644 index a9901630..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for best-practices -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Best Practices -- Single Responsibility: Each plugin should have a single, clear purpose -- Modular Design: Break complex functionality into modules -- configuration: Use configuration files for customizable behavior -- Versioning: Follow semantic versioning for plugin updates -- Backward Compatibility: Maintain compatibility with previous versions - -description: Best practices for plugin development -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc b/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc deleted file mode 100644 index 5b34417a..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for documentation -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Documentation Standards -- API Documentation: Document all public APIs and parameters -- Usage examples: ProvIDE clear usage examples -- Error Codes: Document all possible error codes and meanings -- Performance Notes: Document performance characteristics -- Security Notes: Document security consIDErations - -description: Documentation standards for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc b/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc deleted file mode 100644 index a95d9cf5..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for error-handling -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Error Handling -- Graceful Degradation: Continue working with reduced functionality -- User Feedback: ProvIDE clear error messages to users -- Logging: Log errors for debugging without exposing sensitive data -- Fallbacks: Implement fallback mechanisms for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Error handling requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-performance.mdc b/.cursor/rules/cursor-export/plugin/plugin-performance.mdc deleted file mode 100644 index 868ad488..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-performance.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for performance -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Performance GuIDElines -- PyodIDE Optimization: Optimize for WebAssembly execution -- Memory Management: Clean up resources and avoid memory leaks -- Async Operations: Use async/await for all I/O operations -- Caching: Implement appropriate caching strategies -- Bundle Size: Keep plugin size reasonable for browser loading - -related: - - architecture-performance.mdc - - plugin-testing.mdc - -description: Performance guIDElines for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-security.mdc b/.cursor/rules/cursor-export/plugin/plugin-security.mdc deleted file mode 100644 index a3e81a41..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-security.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Plugin standard for security -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: true -aiCategory: plugin-development--- - -# Plugin Security Requireme.ts -- Zero Trust: Plugins are not trusted by default -- Permission Declaration: All required permissions must be declared in manifest -- API Validation: All API calls must be validated against schemas -- Rate Limiting: Respect rate lim.ts to prevent abuse -- Error Handling: Graceful error handling without exposing sensitive data - -description: Security requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-structure.mdc b/.cursor/rules/cursor-export/plugin/plugin-structure.mdc deleted file mode 100644 index 92065786..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-structure.mdc +++ /dev/null @@ -1,60 +0,0 @@ ---- -description: Plugin standard for structure -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Structure - -## Directory Layout -``` -public/plugins/plugin-name/ -├── manifest.json # Plugin metadata and permissions -├── mcp_server.py # Python MCP protocol implementation -├── workflow.json # Plugin workflow definition (optional) -└──.icon.svg # Plugin.icon -``` - -## Manifest.json Example -``.json -{ - "name": "Plugin Name", - "version": "1.0.0", - "description": "Plugin description", - "main_server": "mcp_server.py", - "required_secr.ts": ["openai_API_key", "weather_API_key"], - "API_permissions": { - "openai": { - "domains": ["API.openai.com"], - "endpoi.ts": ["/v1/chat/completions"], - "methods": ["POST"], - "rate_limit": "100/hour" - } - }, - "network_policy": { - "allowed_domains": ["API.openai.com"], - "WebSock.ts": "denied" - }, - "API_schemas": { - "openai": { - "chat_completions": { - "type": "object", - "required": ["prompt"], - "properties": { - "prompt": {"type": "string", "maxLength": 4000} - } - } - } - } -} -``` -description: Plugin directory and manifest requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-testing.mdc b/.cursor/rules/cursor-export/plugin/plugin-testing.mdc deleted file mode 100644 index c90be344..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-testing.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for testing -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Testing Requireme.ts -- Unit Te.ts: Test individual functions and compone.ts -- Integration Te.ts: Test plugin integration with host system -- Security Te.ts: Validate permission enforcement -- Performance Te.ts: Monitor memory usage and execution time -- Error Te.ts: Test error handling and recovery - -description: Testing requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/security/validation.mdc b/.cursor/rules/cursor-export/security/validation.mdc deleted file mode 100644 index 76a56384..00000000 --- a/.cursor/rules/cursor-export/security/validation.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Input validation and data sanitization rules -globs: ["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"] -alwaysApply: false -aiCategory: security--- - -# Rule -- Always validate all input data and API parameters. -- Never trust data from external sources (user, plugins, third-party services). -- Use schemas, types, or validators (e.g., zod, pydantic, marshmallow). - -# examples -- ✅ `z.object({ email: z.string().email() })` -- ✅ `if not isinstance(user_id, int): raise ValueError()` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc b/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc deleted file mode 100644 index 5c629cf7..00000000 --- a/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc +++ /dev/null @@ -1,166 +0,0 @@ ---- -description: TypeScript build error troubleshooting guIDE - Typeы, barrel expo.ts, Dependencies, Tailwin.css -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# TypeScript build Troubleshooting Rules - -## Основные Principles устранения ошибок - -### 1. TypeScript configuration Errors - -**Error:** `Cannot find type definition file for 'node'` -- **Причина:** В .tsconfig.json` указаны Typeы Node.js для Browserного кода -- **Решение:** Remove `'node'` из `compilerOptions.types`, оставить только `['chrome']` -- **Применяется к:** `pages/*.tsconfig.json`, `te.ts/e2e.tsconfig.json` - -**Error:** `Cannot find module '@extension/shared'` -- **Причина:** Неправильные `paths` в .tsconfig.json` -- **Решение:** Add корректные пути в `compilerOptions.paths` -- **Пример:** -``.json -{ - "compilerOptions": { - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### 2. Barrel Export Issues - -**Error:** `"initAppWithShadow" is not exported by ".../packages/shared/dist/index..js"` -- **Причина:** Неправильный barrel export для default expo.ts -- **Решение:** Использовать явный re-export: `export { default as initAppWithShadow } from './init-app-with-shadow.js'` -- **Паттерн:** -```TypeScript -// В index.ts -export { default as ComponentName } from './component-file.js'; - -// В component-file.ts -export default function ComponentName() { ... } -``` - -### 3. Tailwin.css 4+ Setup - -**Error:** `Cannot find module 'tailwin.css'` -- **Решение:** Установить Dependencies в конкретном пакете: -```bash -pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css -``` - -**Error:** `Loading Pos.css Plugin failed: Cannot find module '@tailwin.css/pos.css'` -- **Решение:** Create `pos.css.config..js`: -```JavaScript -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -**Error:** `Command 'tailwin.css-cli' not found` -- **Решение:** Delete прямой вызов CLI из `build..ts`, использовать Vite Pos.css pipeline - -### 4. Module Resolution - -**Error:** `Rollup failed to resolve import 'file-saver'` -- **Решение:** Установить недостающую зависимость в конкретном пакете: -```bash -pnpm add file-saver -``` - -**Error:** `Failed to resolve entry for package '@extension/vite-config'` -- **Причина:** Неправильные `main`/`expo.ts` в `package.json` -- **Решение:** Использовать ESM-only экспорт: -``.json -{ - "main": "dist/index..js", - "expo.ts": { - ".": "./dist/index..js" - } -} -``` - -### 5. build Script Issues - -**Error:** .jsx is not exported by React.jsx-runtime.js` -- **Причина:** НеCompatibility версий React/SWC/Vite -- **Решение:** - 1. Update до последних версий: `React`, `React-dom`, `vite`, `@vit.js/plugin-React-swc` - 2. Убедиться в .tsconfig.json`: `.jsx": "React.jsx"` - 3. Не использовать `import type React` - -### 6. Pre-commit Hook Issues - -**Error:** `spawn prettier ENOENT` -- **Решение:** Установить prettier и Plugins: -```bash -pnpm add -D prettier prettier-plugin-tailwin.css -w -``` -- **Альтернатива:** Использовать `git commit --no-verify` для пропуска хуков - -## Порядок устранения ошибок - -1. **Анализ ошибки:** Определить Type и контекст -2. **Verification зависимостей:** Убедиться в наличии всех пакетов -3. **configuration:** Исправить .tsconfig.json`, `package.json` -4. **Barrel expo.ts:** Check правильность экспортов -5. **build:** Выполнить `rm -rf dist && pnpm run build` -6. **cache:** При необходимости очистить cache: `pnpm exec rimraf node_modules/.vite .turbo .cache` - -## Частые Patterns исправлений - -### Для pages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "types": ["chrome"], // Remove 'node' - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### Для packages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - } -} -``` - -### Для barrel expo.ts: -```TypeScript -// Правильно для default expo.ts -export { default as ComponentName } from './component-file.js'; - -// Правильно для named expo.ts -export { ComponentName } from './component-file.js'; -``` - -## Команды для diagnostics - -```bash -# Verification зависимостей -pnpm why React -pnpm why tailwin.css - -# Поиск файлов -find . -name .tsconfig.json" -exec grep -l "node" {} \; - -# Verification сборки -pnpm run build - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache && pnpm install -``` \ No newline at end of file diff --git a/.cursor/rules/cursor-export/ui/ui-accessibility.mdc b/.cursor/rules/cursor-export/ui/ui-accessibility.mdc deleted file mode 100644 index 197d9d79..00000000 --- a/.cursor/rules/cursor-export/ui/ui-accessibility.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for accessibility -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# Accessibility (A11y) Standards -- ProvIDE proper ARIA labels for interactive eleme.ts -- Ensure all functionality is keyboard accessible -- Proper focus handling and visible focus indicators -- Use semantic.html and descriptive text for screen readers -- Meet WCAG color contrast requireme.ts - -related: - - ui-forms.mdc - - ui-error-handling.mdc - -description: Accessibility requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-animation.mdc b/.cursor/rules/cursor-export/ui/ui-animation.mdc deleted file mode 100644 index 263fa004..00000000 --- a/.cursor/rules/cursor-export/ui/ui-animation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for animation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Animation and Transitions -- Use.css transitions for State changes -- Subtle loading animations -- Enhance user experience with micro-interactions -- Ensure animations don't impact performance -- Respect user's motion preferences (reduced motion) - -description: Animation and transition standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-error-handling.mdc b/.cursor/rules/cursor-export/ui/ui-error-handling.mdc deleted file mode 100644 index c2613648..00000000 --- a/.cursor/rules/cursor-export/ui/ui-error-handling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for error-handling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Error Handling -- Clear, actionable error messages for users -- Catch and handle component errors with error boundaries -- ProvIDE fallback UI for failed compone.ts -- Allow users to retry failed operations -- Log errors for debugging - -description: Error handling requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-forms.mdc b/.cursor/rules/cursor-export/ui/ui-forms.mdc deleted file mode 100644 index e9b6928b..00000000 --- a/.cursor/rules/cursor-export/ui/ui-forms.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for forms -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Form Standards -- Client-sIDE and server-sIDE validation -- Show validation errors clearly -- ProvIDE clear success feedback -- Auto-save forms where appropriate -- Support common keyboard shortc.ts - -description: Form standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-loading-states.mdc b/.cursor/rules/cursor-export/ui/ui-loading-states.mdc deleted file mode 100644 index 4b268cbf..00000000 --- a/.cursor/rules/cursor-export/ui/ui-loading-states.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for loading-States -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Loading States -- Show skeleton UI while loading -- Use spinners or progress bars as indicators -- ProvIDE informative loading messages -- Handle loading timeo.ts gracefully -- Show appropriate error States - -description: Loading State requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-mobile.mdc b/.cursor/rules/cursor-export/ui/ui-mobile.mdc deleted file mode 100644 index 3c472d88..00000000 --- a/.cursor/rules/cursor-export/ui/ui-mobile.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for mobile -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Mobile Responsiveness -- Adequate size for touch interaction (touch targ.ts) -- Proper viewport meta tags -- Use flexbox and grid for responsive layo.ts -- Support common touch gestures -- Optimize for mobile performance - -description: Mobile responsiveness standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-navigation.mdc b/.cursor/rules/cursor-export/ui/ui-navigation.mdc deleted file mode 100644 index 02d02c56..00000000 --- a/.cursor/rules/cursor-export/ui/ui-navigation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for navigation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Navigation Standards -- ProvIDE clear navigation context with breadcrumbs -- Clearly indicate current page/section (active States) -- Support browser back button -- Support direct links to specific content (deep linking) -- Show loading during navigation - -description: Navigation standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-performance.mdc b/.cursor/rules/cursor-export/ui/ui-performance.mdc deleted file mode 100644 index dc609680..00000000 --- a/.cursor/rules/cursor-export/ui/ui-performance.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for performance -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Performance Standards -- Lazy load compone.ts and routes -- Use React.memo and useMemo appropriately -- Split code into smaller bundles -- Optimize images for web -- Implement appropriate caching strategies - -description: Performance standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-react-components.mdc b/.cursor/rules/cursor-export/ui/ui-react-components.mdc deleted file mode 100644 index 84458a72..00000000 --- a/.cursor/rules/cursor-export/ui/ui-react-components.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for React-compone.ts -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# React Component Standards -- Use TypeScript for all new compone.ts -- Prefer functional compone.ts with hooks -- Define clear interfaces for component props -- ProvIDE sensible default values -- Wrap compone.ts in error boundaries - -description: Standards for React component structure in UI -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-styling.mdc b/.cursor/rules/cursor-export/ui/ui-styling.mdc deleted file mode 100644 index 0a2f7319..00000000 --- a/.cursor/rules/cursor-export/ui/ui-styling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for styling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Styling Standards -- Use Tailwind.css for consistent styling -- Organize.css classes logically -- Ensure responsive design for different screen sizes -- Support light/dark mode -- Use.css variables for theme values - -description: Styling standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-testing.mdc b/.cursor/rules/cursor-export/ui/ui-testing.mdc deleted file mode 100644 index 133251bf..00000000 --- a/.cursor/rules/cursor-export/ui/ui-testing.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for testing -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Testing Standards -- Test component logic and behavior (unit te.ts) -- Test component interactions (integration te.ts) -- Test visual appearance and layout (visual te.ts) -- Test accessibility compliance (a11y te.ts) -- Test component performance - -related: - - plugin-testing.mdc - - ui-error-handling.mdc - -description: Testing standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/workflow/automation.mdc b/.cursor/rules/cursor-export/workflow/automation.mdc deleted file mode 100644 index 14024a6e..00000000 --- a/.cursor/rules/cursor-export/workflow/automation.mdc +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Automation Rules - -- При обновлении ключевых принципов, стандартов или чек-листов в memory-bank (например, DEV_PRACTICES.md, SECURITY.md, KNOWLEDGE_MAP.md), ассистент обязан синхронизировать их с правилами Cursor (.cursor/rules/). -- Все изменения должны быть отражены как в документации, так и в правилах для AI. -- Если обнаружено расхождение — предложить пользователю синхронизировать и объяснить различия. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/workflow/branches.mdc b/.cursor/rules/cursor-export/workflow/branches.mdc deleted file mode 100644 index d485b834..00000000 --- a/.cursor/rules/cursor-export/workflow/branches.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git branch management rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Rule -- For new features, use branches with the prefix `feature/` and a meaningful name. -- For bugfixes, use the prefix `fix/` and a clear description of the problem. -- Never mix bugfixes and features in the same branch. -- After merging a bugfix, update feature branches via rebase/merge from develop. -- Direct comm.ts or merges to `main` and `develop` are strictly forbidden. All changes must go through pull reque.ts into `develop` only. -- Merging to `main` is allowed only from `develop` after release approval. -- **Branch Purpose Tracking:** Before starting a new, unrelated task, you must finish the current branch (commit, push, PR, merge) and only then create a new branch for the next task. This preve.ts mixing tasks and improves review clarity. Exceptions: urgent bugfixes (fix/). - -# examples -- ✅ `feature/pyodIDE-integration` -- ✅ `fix/tailwin.css-build-script` -- ❌ `feature/pyodIDE-integration` + unrelated refactor in same branch \ No newline at end of file diff --git a/.cursor/rules/cursor-export/workflow/workflow.mdc b/.cursor/rules/cursor-export/workflow/workflow.mdc deleted file mode 100644 index 8466ff5b..00000000 --- a/.cursor/rules/cursor-export/workflow/workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Workflow rule for -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# Workflow Branching Rules - -- В ветке main запрещено вести работу. main используется только для релизов и production. -- В ветке develop запрещено вести работу напрямую. Все изменения — только через feature-Branches с именованием feature/{Seeсловое_название}, которые создаются автоматически по Seeслу задачи. -- Merge feature-веток только через pull request в develop. -- Прямой merge или commit в main и develop запрещён. -- В main разрешён merge только из develop после релизного ревью. -- Все PR должны содержать Description изменений, ссылки на документацию и changelog. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-git-hook.cjs b/.cursor/rules/cursor-git-hook.cjs deleted file mode 100644 index 9af6e6e9..00000000 --- a/.cursor/rules/cursor-git-hook.cjs +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env node - -/** - * Cursor Git Hook Script - * Automatically protects .cursor files on git operations - * Usage: node .cursor/rules/cursor-git-hook.cjs [pre-commit|post-commit|pre-push] - */ - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); -const { protectCursor, checkCursorStatus } = require('./cursor-protector.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const GIT_HOOKS_DIR = path.join(process.cwd(), '.git', 'hooks'); - -function getStagedCursorFiles() { - try { - const output = execSync('git diff --cached --name-only --diff-filter=ACM', { encoding: 'utf8' }); - const files = output.trim().split('\n').filter(Boolean); - return files.filter(file => file.startsWith('.cursor/')); - } catch (error) { - return []; - } -} - -function preCommitHook() { - console.log('🛡️ Pre-commit: Checking .cursor files...\n'); - - const stagedFiles = getStagedCursorFiles(); - - if (stagedFiles.length === 0) { - console.log('✅ No .cursor files staged for commit'); - return true; - } - - console.log(`📁 Found ${stagedFiles.length} staged .cursor files:`); - stagedFiles.forEach(file => console.log(` - ${file}`)); - - // Check if any staged files contain Russian content - let hasRussianContent = false; - - for (const file of stagedFiles) { - try { - const content = fs.readFileSync(file, 'utf8'); - if (/[а-яё]/i.test(content)) { - console.log(`⚠️ Warning: ${file} contains Russian content`); - hasRussianContent = true; - } - } catch (error) { - console.log(`❌ Error reading ${file}: ${error.message}`); - } - } - - if (hasRussianContent) { - console.log('\n🔄 Automatically translating staged .cursor files...'); - - // Translate staged files - for (const file of stagedFiles) { - try { - const { translateText } = require('./cursor-protector.cjs'); - const content = fs.readFileSync(file, 'utf8'); - const translatedContent = translateText(content); - - // Create backup - const backupDir = path.join(CURSOR_DIR, 'backup', 'pre-commit'); - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir, { recursive: true }); - } - - const backupPath = path.join(backupDir, path.basename(file)); - fs.copyFileSync(file, backupPath); - - // Write translated content - fs.writeFileSync(file, translatedContent, 'utf8'); - - // Stage the translated file - execSync(`git add "${file}"`, { stdio: 'inherit' }); - - console.log(`✅ Translated and staged: ${file}`); - } catch (error) { - console.log(`❌ Error translating ${file}: ${error.message}`); - } - } - - console.log('\n✅ All staged .cursor files translated to English'); - } else { - console.log('\n✅ All staged .cursor files are already in English'); - } - - return true; -} - -function postCommitHook() { - console.log('🛡️ Post-commit: Ensuring .cursor protection...\n'); - - // Check overall .cursor status - const status = checkCursorStatus(); - - if (status.russianContentCount > 0) { - console.log('\n⚠️ Warning: Some .cursor files still contain Russian content'); - console.log('💡 Consider running: node .cursor/rules/cursor-protector.cjs protect'); - } else { - console.log('\n✅ All .cursor files are protected (English only)'); - } - - return true; -} - -function prePushHook() { - console.log('🛡️ Pre-push: Final .cursor protection check...\n'); - - const status = checkCursorStatus(); - - if (status.russianContentCount > 0) { - console.log('\n❌ Push blocked: .cursor files contain Russian content'); - console.log('💡 Please run: node .cursor/rules/cursor-protector.cjs protect'); - console.log('💡 Then commit and push again'); - return false; - } - - console.log('\n✅ .cursor files are protected - push allowed'); - return true; -} - -function installHooks() { - console.log('🔧 Installing .cursor protection Git hooks...\n'); - - const hooks = { - 'pre-commit': preCommitHook, - 'post-commit': postCommitHook, - 'pre-push': prePushHook - }; - - for (const [hookName, hookFunction] of Object.entries(hooks)) { - const hookPath = path.join(GIT_HOOKS_DIR, hookName); - const hookContent = `#!/bin/sh -# Cursor Protection Hook -node .cursor/rules/cursor-git-hook.cjs ${hookName} -`; - - try { - fs.writeFileSync(hookPath, hookContent, 'utf8'); - fs.chmodSync(hookPath, '755'); - console.log(`✅ Installed: ${hookName} hook`); - } catch (error) { - console.log(`❌ Error installing ${hookName} hook: ${error.message}`); - } - } - - console.log('\n🎉 Git hooks installed successfully!'); - console.log('📝 .cursor files will now be automatically protected on git operations'); -} - -function uninstallHooks() { - console.log('🔧 Uninstalling .cursor protection Git hooks...\n'); - - const hooks = ['pre-commit', 'post-commit', 'pre-push']; - - for (const hookName of hooks) { - const hookPath = path.join(GIT_HOOKS_DIR, hookName); - - try { - if (fs.existsSync(hookPath)) { - const content = fs.readFileSync(hookPath, 'utf8'); - - // Only remove if it's our hook - if (content.includes('Cursor Protection Hook')) { - fs.unlinkSync(hookPath); - console.log(`✅ Uninstalled: ${hookName} hook`); - } else { - console.log(`⚠️ Skipped: ${hookName} hook (not our hook)`); - } - } else { - console.log(`ℹ️ No ${hookName} hook found`); - } - } catch (error) { - console.log(`❌ Error uninstalling ${hookName} hook: ${error.message}`); - } - } - - console.log('\n🎉 Git hooks uninstalled successfully!'); -} - -function main() { - const command = process.argv[2] || 'pre-commit'; - - switch (command) { - case 'pre-commit': - return preCommitHook(); - case 'post-commit': - return postCommitHook(); - case 'pre-push': - return prePushHook(); - case 'install': - installHooks(); - break; - case 'uninstall': - uninstallHooks(); - break; - case 'help': - console.log(` -Cursor Git Hook Script - -Usage: node .cursor/rules/cursor-git-hook.cjs [command] - -Commands: - pre-commit - Pre-commit hook (translates staged .cursor files) - post-commit - Post-commit hook (checks overall .cursor status) - pre-push - Pre-push hook (blocks push if Russian content found) - install - Install Git hooks - uninstall - Uninstall Git hooks - help - Show this help - -Examples: - node .cursor/rules/cursor-git-hook.cjs pre-commit - node .cursor/rules/cursor-git-hook.cjs install - node .cursor/rules/cursor-git-hook.cjs uninstall - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - const success = main(); - process.exit(success ? 0 : 1); -} - -module.exports = { - preCommitHook, - postCommitHook, - prePushHook, - installHooks, - uninstallHooks -}; \ No newline at end of file diff --git a/.cursor/rules/cursor-manager.cjs b/.cursor/rules/cursor-manager.cjs deleted file mode 100644 index 3566b0c3..00000000 --- a/.cursor/rules/cursor-manager.cjs +++ /dev/null @@ -1,580 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorManager { - constructor() { - this.cursorDir = path.join(process.cwd(), '.cursor', 'rules'); - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - } - - async executeCommand(command, options = {}, args = []) { - console.log(`🚀 Executing: ${command}\n`); - - try { - switch (command) { - case 'audit': - await this.auditCursor(); - break; - case 'fix': - await this.fixCursor(); - break; - case 'optimize': - await this.optimizeCursor(); - break; - case 'full': - await this.fullWorkflow(); - break; - case 'status': - await this.showStatus(); - break; - case 'create-rule': - await this.createRule(options.name, options.content); - break; - case 'export': - await this.exportRules(options.project); - break; - case 'import': - await this.importRules(); - break; - case 'memory-add': - await this.memoryAdd(options.content, options); - break; - case 'memory-update': - await this.memoryUpdate(options); - break; - case 'memory-restore': - await this.memoryRestore(options); - break; - case 'memory-audit': - await this.memoryAudit(); - break; - case 'memory-structure': - await this.memoryStructure(options.type); - break; - case 'memory-report': - await this.memoryReport(options.type); - break; - case 'memory-search': - const query = args[1]; - await this.memorySearch(query, options); - break; - case 'document': - await this.document(options.content, options.type); - break; - default: - console.log(`❌ Unknown command: ${command}`); - this.showHelp(); - } - } catch (error) { - console.error(`❌ Error executing ${command}:`, error.message); - process.exit(1); - } - } - - async auditCursor() { - console.log('🔍 Auditing .cursor rules...\n'); - - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - // Проверяем структуру .cursor - const issues = []; - - // Проверяем наличие обязательных файлов - const requiredFiles = [ - 'cursor-export/ai-memory.mdc', - 'doc/documentation-map.mdc', - 'dev/development-principles.mdc' - ]; - - for (const file of requiredFiles) { - const filePath = path.join(this.cursorDir, file); - if (!fs.existsSync(filePath)) { - issues.push(`Missing required file: ${file}`); - } - } - - // Проверяем метаданные в .mdc файлах - const mdcFiles = this.findMdcFiles(); - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - if (!content.includes('description:') || !content.includes('aiPriority:')) { - issues.push(`Missing metadata in: ${path.relative(this.cursorDir, file)}`); - } - } - - if (issues.length === 0) { - console.log('✅ .cursor rules audit passed - no issues found'); - } else { - console.log('❌ Found issues:'); - issues.forEach(issue => console.log(` - ${issue}`)); - } - - return issues; - } - - async fixCursor() { - console.log('🔧 Fixing .cursor rules...\n'); - - const issues = await this.auditCursor(); - - if (issues.length === 0) { - console.log('✅ No fixes needed'); - return; - } - - // Автоматические исправления - for (const issue of issues) { - if (issue.includes('Missing metadata')) { - await this.fixMetadata(issue); - } - } - - console.log('✅ Fixes applied'); - } - - async optimizeCursor() { - console.log('⚡ Optimizing .cursor rules...\n'); - - // Оптимизация структуры - await this.optimizeStructure(); - - // Оптимизация метаданных - await this.optimizeMetadata(); - - console.log('✅ Optimization completed'); - } - - async fullWorkflow() { - console.log('🔄 Running full workflow...\n'); - - await this.auditCursor(); - await this.fixCursor(); - await this.optimizeCursor(); - - console.log('✅ Full workflow completed'); - } - - async showStatus() { - console.log('📊 .cursor rules status:\n'); - - const mdcFiles = this.findMdcFiles(); - const categories = this.getCategories(); - - console.log(`📁 Total .mdc files: ${mdcFiles.length}`); - console.log(`📂 Categories: ${Object.keys(categories).length}`); - - for (const [category, count] of Object.entries(categories)) { - console.log(` - ${category}: ${count} files`); - } - - // Проверяем memory-bank - if (fs.existsSync(this.memoryBankDir)) { - const memoryFiles = this.findMemoryFiles(); - console.log(`\n🧠 Memory bank files: ${memoryFiles.length}`); - } - } - - async createRule(name, content) { - console.log(`📝 Creating rule: ${name}\n`); - - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - await helper.documentExperience(content, 'general-practice'); - - console.log(`✅ Rule created: ${name}`); - } - - async exportRules(project = 'default') { - console.log(`📤 Exporting rules to cursor-export...\n`); - - const exportDir = path.join(this.cursorDir, 'cursor-export'); - if (!fs.existsSync(exportDir)) { - fs.mkdirSync(exportDir, { recursive: true }); - } - - const mdcFiles = this.findMdcFiles(); - let exportedCount = 0; - - for (const file of mdcFiles) { - const relativePath = path.relative(this.cursorDir, file); - const exportPath = path.join(exportDir, relativePath); - - const dir = path.dirname(exportPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - fs.copyFileSync(file, exportPath); - exportedCount++; - } - - // Создаем индекс экспорта - const exportIndex = this.generateExportIndex(mdcFiles, project); - const indexPath = path.join(exportDir, 'EXPORT_INDEX.md'); - fs.writeFileSync(indexPath, exportIndex); - - console.log(`✅ Exported ${exportedCount} rules to cursor-export`); - } - - async importRules() { - console.log('📥 Importing rules from cursor-export...\n'); - - const exportDir = path.join(this.cursorDir, 'cursor-export'); - if (!fs.existsSync(exportDir)) { - console.log('❌ No cursor-export directory found'); - return; - } - - const exportedFiles = this.findExportedFiles(); - let importedCount = 0; - - for (const file of exportedFiles) { - const relativePath = path.relative(exportDir, file); - const importPath = path.join(this.cursorDir, relativePath); - - const dir = path.dirname(importPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - fs.copyFileSync(file, importPath); - importedCount++; - } - - console.log(`✅ Imported ${importedCount} rules from cursor-export`); - } - - // Memory Bank Management - - async memoryAdd(content, options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - await manager.addEntry(content, options); - } - - async memoryUpdate(options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - await manager.updateContext(options); - } - - async memoryRestore(options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const context = await manager.restoreContext(options); - console.log(JSON.stringify(context, null, 2)); - } - - async memoryAudit() { - const MemoryBankAuditor = require('./memory-bank-auditor.cjs'); - const auditor = new MemoryBankAuditor(); - - await auditor.audit(); - } - - async memoryStructure(type = 'react-typescript') { - const MemoryBankStructureCreator = require('./memory-bank-structure-creator.cjs'); - const creator = new MemoryBankStructureCreator(); - - await creator.createStructure(type); - } - - async memoryReport(type = 'full') { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const report = await manager.generateReport({ type }); - console.log(JSON.stringify(report, null, 2)); - } - - async memorySearch(query, options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const results = await manager.searchMemory(query, options); - console.log(JSON.stringify(results, null, 2)); - } - - async document(content, type = 'auto') { - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - await helper.documentExperience(content, type); - } - - // Вспомогательные методы - - findMdcFiles() { - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - - scanDir(this.cursorDir); - return files; - } - - findMemoryFiles() { - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.md')) { - files.push(fullPath); - } - } - } - - scanDir(this.memoryBankDir); - return files; - } - - findExportedFiles() { - const exportDir = path.join(this.cursorDir, 'cursor-export'); - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - - scanDir(exportDir); - return files; - } - - getCategories() { - const categories = {}; - const mdcFiles = this.findMdcFiles(); - - for (const file of mdcFiles) { - const relativePath = path.relative(this.cursorDir, file); - const category = relativePath.split(path.sep)[0]; - categories[category] = (categories[category] || 0) + 1; - } - - return categories; - } - - async fixMetadata(issue) { - const filePath = issue.match(/in: (.+)/)[1]; - const fullPath = path.join(this.cursorDir, filePath); - - if (!fs.existsSync(fullPath)) return; - - const content = fs.readFileSync(fullPath, 'utf8'); - - // Добавляем базовые метаданные если их нет - if (!content.includes('description:')) { - const newContent = content.replace( - /^---\n/, - `--- -description: Auto-generated rule -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: general ---- -` - ); - fs.writeFileSync(fullPath, newContent); - } - } - - async optimizeStructure() { - // Оптимизация структуры каталогов - const categories = ['dev', 'doc', 'ui', 'workflow', 'architecture', 'security', 'plugin']; - - for (const category of categories) { - const categoryDir = path.join(this.cursorDir, category); - if (!fs.existsSync(categoryDir)) { - fs.mkdirSync(categoryDir, { recursive: true }); - } - } - } - - async optimizeMetadata() { - const mdcFiles = this.findMdcFiles(); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - // Оптимизируем метаданные - let optimized = content; - - // Добавляем aiPriority если нет - if (!content.includes('aiPriority:')) { - optimized = optimized.replace( - /^---\n/, - `--- -aiPriority: normal -` - ); - } - - // Добавляем aiCategory если нет - if (!content.includes('aiCategory:')) { - optimized = optimized.replace( - /^---\n/, - `--- -aiCategory: general -` - ); - } - - if (optimized !== content) { - fs.writeFileSync(file, optimized); - } - } - } - - generateExportIndex(files, project) { - const date = new Date().toISOString().split('T')[0]; - - let index = `# Cursor Rules Export - ${project} - -**Exported:** ${date} -**Total files:** ${files.length} - -## Files - -`; - - const categories = {}; - for (const file of files) { - const relativePath = path.relative(this.cursorDir, file); - const category = relativePath.split(path.sep)[0]; - - if (!categories[category]) { - categories[category] = []; - } - categories[category].push(relativePath); - } - - for (const [category, categoryFiles] of Object.entries(categories)) { - index += `### ${category}\n`; - for (const file of categoryFiles) { - index += `- [${file}](./${file})\n`; - } - index += '\n'; - } - - return index; - } - - showHelp() { - console.log(` -Cursor Manager - CLI Tool - -Usage: - node cursor-manager.cjs [options] - -Commands: - audit - Audit .cursor rules - fix - Fix .cursor issues - optimize - Optimize .cursor rules - full - Run full workflow (audit + fix + optimize) - status - Show .cursor status - create-rule - Create new rule - export [project] - Export rules to cursor-export - import - Import rules from cursor-export - -Memory Bank Commands: - memory-add [options] - Add entry to memory-bank - memory-update [options] - Update active context - memory-restore [options] - Restore context from memory-bank - memory-audit - Audit memory-bank structure - memory-structure - Create memory-bank structure - memory-report [type] - Generate memory-bank report - -Documentation Commands: - document [type] - Document experience - -Options: - --type= Entry type (error, decision, progress, etc.) - --category= Category (core, errors, architecture, etc.) - --priority= Priority (critical, high, medium, low) - --tags= Comma-separated tags - --full Full context restoration - --content= Content for rule or entry - `); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - if (args.length === 0) { - const manager = new CursorManager(); - manager.showHelp(); - process.exit(1); - } - - const command = args[0]; - const options = parseOptions(args.slice(1)); - - const manager = new CursorManager(); - await manager.executeCommand(command, options, args); -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } else if (!options.name) { - options.name = arg; - } else if (!options.content) { - options.content = arg; - } - } - - return options; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorManager; \ No newline at end of file diff --git a/.cursor/rules/cursor-protector.cjs b/.cursor/rules/cursor-protector.cjs deleted file mode 100644 index d7f7548e..00000000 --- a/.cursor/rules/cursor-protector.cjs +++ /dev/null @@ -1,726 +0,0 @@ -#!/usr/bin/env node - -/** - * Cursor Protector Script - * Automatically protects and translates all .cursor files to English - * Usage: node .cursor/rules/cursor-protector.cjs [protect|translate|check|restore] - */ - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const BACKUP_DIR = path.join(process.cwd(), '.cursor', 'backup'); - -// Translation mappings for .cursor files -const cursorTranslations = { - // Common headers and titles - 'Описание': 'Description', - 'Назначение': 'Purpose', - 'Использование': 'Usage', - 'Примеры': 'Examples', - 'Примечания': 'Notes', - 'Важно': 'Important', - 'Внимание': 'Warning', - 'Совет': 'Tip', - 'Замечание': 'Note', - - // File structure - 'Структура файлов': 'File Structure', - 'Структура проекта': 'Project Structure', - 'Организация файлов': 'File Organization', - 'Иерархия папок': 'Folder Hierarchy', - - // Development terms - 'Разработка': 'Development', - 'Программирование': 'Programming', - 'Кодирование': 'Coding', - 'Отладка': 'Debugging', - 'Тестирование': 'Testing', - 'Сборка': 'Build', - 'Развертывание': 'Deployment', - 'Версионирование': 'Versioning', - - // Architecture terms - 'Архитектура': 'Architecture', - 'Дизайн': 'Design', - 'Паттерны': 'Patterns', - 'Принципы': 'Principles', - 'Методологии': 'Methodologies', - 'Фреймворки': 'Frameworks', - 'Библиотеки': 'Libraries', - - // Quality and standards - 'Качество': 'Quality', - 'Стандарты': 'Standards', - 'Лучшие практики': 'Best Practices', - 'Рекомендации': 'Recommendations', - 'Требования': 'Requirements', - 'Ограничения': 'Constraints', - - // Security and safety - 'Безопасность': 'Security', - 'Защита': 'Protection', - 'Валидация': 'Validation', - 'Аутентификация': 'Authentication', - 'Авторизация': 'Authorization', - 'Шифрование': 'Encryption', - - // Performance and optimization - 'Производительность': 'Performance', - 'Оптимизация': 'Optimization', - 'Кэширование': 'Caching', - 'Масштабирование': 'Scaling', - 'Мониторинг': 'Monitoring', - 'Логирование': 'Logging', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Интерфейс': 'Interface', - 'Доступность': 'Accessibility', - 'Удобство использования': 'Usability', - 'Отзывчивость': 'Responsiveness', - 'Интерактивность': 'Interactivity', - - // Project management - 'Управление проектом': 'Project Management', - 'Планирование': 'Planning', - 'Контроль': 'Control', - 'Мониторинг': 'Monitoring', - 'Отчетность': 'Reporting', - 'Документирование': 'Documentation', - - // Communication - 'Коммуникация': 'Communication', - 'Сотрудничество': 'Collaboration', - 'Координация': 'Coordination', - 'Синхронизация': 'Synchronization', - 'Интеграция': 'Integration', - - // Error handling - 'Обработка ошибок': 'Error Handling', - 'Исключения': 'Exceptions', - 'Восстановление': 'Recovery', - 'Отказоустойчивость': 'Fault Tolerance', - 'Graceful degradation': 'Graceful Degradation', - - // Data and storage - 'Данные': 'Data', - 'Хранилище': 'Storage', - 'База данных': 'Database', - 'Кэш': 'Cache', - 'Резервное копирование': 'Backup', - 'Синхронизация данных': 'Data Synchronization', - - // API and interfaces - 'API': 'API', - 'Интерфейсы': 'Interfaces', - 'Эндпоинты': 'Endpoints', - 'Запросы': 'Requests', - 'Ответы': 'Responses', - 'Сериализация': 'Serialization', - - // Testing and validation - 'Тестирование': 'Testing', - 'Валидация': 'Validation', - 'Проверка': 'Verification', - 'Unit тесты': 'Unit Tests', - 'Integration тесты': 'Integration Tests', - 'E2E тесты': 'E2E Tests', - - // Configuration and settings - 'Конфигурация': 'Configuration', - 'Настройки': 'Settings', - 'Параметры': 'Parameters', - 'Переменные окружения': 'Environment Variables', - 'Конфигурационные файлы': 'Configuration Files', - - // Dependencies and packages - 'Зависимости': 'Dependencies', - 'Пакеты': 'Packages', - 'Модули': 'Modules', - 'Библиотеки': 'Libraries', - 'Плагины': 'Plugins', - 'Расширения': 'Extensions', - - // Version control and deployment - 'Система контроля версий': 'Version Control System', - 'Репозиторий': 'Repository', - 'Ветки': 'Branches', - 'Коммиты': 'Commits', - 'Слияние': 'Merge', - 'Развертывание': 'Deployment', - - // Documentation - 'Документация': 'Documentation', - 'Руководства': 'Guides', - 'Справочники': 'References', - 'Примеры кода': 'Code Examples', - 'Комментарии': 'Comments', - 'README': 'README', - - // Internationalization - 'Интернационализация': 'Internationalization', - 'Локализация': 'Localization', - 'Перевод': 'Translation', - 'Многоязычность': 'Multilingual', - 'Языковые файлы': 'Language Files', - - // Accessibility - 'Доступность': 'Accessibility', - 'Скринридеры': 'Screen Readers', - 'Клавиатурная навигация': 'Keyboard Navigation', - 'Контрастность': 'Contrast', - 'Размер шрифта': 'Font Size', - - // Mobile and responsive - 'Мобильные устройства': 'Mobile Devices', - 'Адаптивность': 'Responsiveness', - 'Touch интерфейс': 'Touch Interface', - 'Мобильная оптимизация': 'Mobile Optimization', - - // Browser and platform - 'Браузер': 'Browser', - 'Платформа': 'Platform', - 'Совместимость': 'Compatibility', - 'Кроссбраузерность': 'Cross-browser', - 'Кроссплатформенность': 'Cross-platform', - - // Common phrases - 'Важно помнить': 'Important to remember', - 'Следует отметить': 'It should be noted', - 'Необходимо учитывать': 'Must be considered', - 'Рекомендуется': 'Recommended', - 'Обязательно': 'Required', - 'Опционально': 'Optional', - 'По умолчанию': 'Default', - 'Настройка по умолчанию': 'Default setting', - 'См.': 'See', - 'политика': 'policy', - 'параметры': 'parameters', - 'операционной': 'operational', - 'тестовой': 'test', - 'среды': 'environment', - 'доступные': 'available', - 'автоматизации': 'automation', - 'диагностики': 'diagnostics', - 'полного': 'full', - 'доступа': 'access', - 'ассистента': 'assistant', - 'ко': 'to', - 'всем': 'all', - 'файлам': 'files', - 'проекта': 'project', - 'и': 'and', - 'проактивных': 'proactive', - 'действий': 'actions', - 'operational': 'operational', - 'test': 'test', - 'environment': 'environment', - 'available': 'available', - 'для': 'for', - 'автоматизации': 'automation', - 'диагностики': 'diagnostics', - - // Status and states - 'Статус': 'Status', - 'Состояние': 'State', - 'Активный': 'Active', - 'Неактивный': 'Inactive', - 'Включен': 'Enabled', - 'Выключен': 'Disabled', - 'Загружается': 'Loading', - 'Завершено': 'Completed', - 'В процессе': 'In Progress', - 'Ожидает': 'Pending', - 'Ошибка': 'Error', - 'Успешно': 'Success', - - // Actions and operations - 'Действие': 'Action', - 'Операция': 'Operation', - 'Функция': 'Function', - 'Метод': 'Method', - 'Процедура': 'Procedure', - 'Алгоритм': 'Algorithm', - 'Создать': 'Create', - 'Удалить': 'Delete', - 'Обновить': 'Update', - 'Изменить': 'Modify', - 'Добавить': 'Add', - 'Убрать': 'Remove', - 'Проверить': 'Check', - 'Валидировать': 'Validate', - 'Подтвердить': 'Confirm', - 'Отменить': 'Cancel', - 'Сохранить': 'Save', - 'Загрузить': 'Load', - 'Экспортировать': 'Export', - 'Импортировать': 'Import', - - // Time and dates - 'Время': 'Time', - 'Дата': 'Date', - 'Период': 'Period', - 'Интервал': 'Interval', - 'Частота': 'Frequency', - 'Ежедневно': 'Daily', - 'Еженедельно': 'Weekly', - 'Ежемесячно': 'Monthly', - 'Ежегодно': 'Yearly', - - // Size and quantity - 'Размер': 'Size', - 'Количество': 'Quantity', - 'Объем': 'Volume', - 'Максимум': 'Maximum', - 'Минимум': 'Minimum', - 'Ограничение': 'Limit', - 'Порог': 'Threshold', - - // Priority and importance - 'Приоритет': 'Priority', - 'Важность': 'Importance', - 'Критично': 'Critical', - 'Высоко': 'High', - 'Средне': 'Medium', - 'Низко': 'Low', - 'Срочно': 'Urgent', - 'Нормально': 'Normal', - - // Categories and types - 'Категория': 'Category', - 'Тип': 'Type', - 'Класс': 'Class', - 'Группа': 'Group', - 'Семейство': 'Family', - 'Набор': 'Set', - 'Коллекция': 'Collection', - - // Common technical terms - 'Интерфейс': 'Interface', - 'Абстракция': 'Abstraction', - 'Инкапсуляция': 'Encapsulation', - 'Наследование': 'Inheritance', - 'Полиморфизм': 'Polymorphism', - 'Сериализация': 'Serialization', - 'Десериализация': 'Deserialization', - 'Кэширование': 'Caching', - 'Буферизация': 'Buffering', - 'Синхронизация': 'Synchronization', - 'Асинхронность': 'Asynchrony', - 'Потоки': 'Threads', - 'Процессы': 'Processes', - 'Память': 'Memory', - 'Процессор': 'Processor', - 'Сеть': 'Network', - 'Файловая система': 'File System', - 'База данных': 'Database', - 'Сервер': 'Server', - 'Клиент': 'Client', - - // Web development - 'Веб-разработка': 'Web Development', - 'Фронтенд': 'Frontend', - 'Бэкенд': 'Backend', - 'Full-stack': 'Full-stack', - 'HTML': 'HTML', - 'CSS': 'CSS', - 'JavaScript': 'JavaScript', - 'TypeScript': 'TypeScript', - 'React': 'React', - 'Vue': 'Vue', - 'Angular': 'Angular', - 'Node.js': 'Node.js', - 'Express': 'Express', - 'MongoDB': 'MongoDB', - 'PostgreSQL': 'PostgreSQL', - 'MySQL': 'MySQL', - 'REST API': 'REST API', - 'GraphQL': 'GraphQL', - 'WebSocket': 'WebSocket', - 'HTTP': 'HTTP', - 'HTTPS': 'HTTPS', - 'SSL': 'SSL', - 'TLS': 'TLS', - 'CORS': 'CORS', - 'JWT': 'JWT', - 'OAuth': 'OAuth', - - // Development tools - 'Инструменты разработки': 'Development Tools', - 'IDE': 'IDE', - 'Редактор кода': 'Code Editor', - 'Отладчик': 'Debugger', - 'Профилировщик': 'Profiler', - 'Линтер': 'Linter', - 'Форматтер': 'Formatter', - 'Сборщик': 'Bundler', - 'Транспайлер': 'Transpiler', - 'Менеджер пакетов': 'Package Manager', - 'Система контроля версий': 'Version Control System', - 'CI/CD': 'CI/CD', - 'Docker': 'Docker', - 'Kubernetes': 'Kubernetes', - - // Common file extensions and formats - '.js': '.js', - '.ts': '.ts', - '.jsx': '.jsx', - '.tsx': '.tsx', - '.json': '.json', - '.xml': '.xml', - '.yaml': '.yaml', - '.yml': '.yml', - '.md': '.md', - '.mdc': '.mdc', - '.html': '.html', - '.css': '.css', - '.scss': '.scss', - '.sass': '.sass', - '.less': '.less', - '.png': '.png', - '.jpg': '.jpg', - '.jpeg': '.jpeg', - '.gif': '.gif', - '.svg': '.svg', - '.ico': '.ico', - '.woff': '.woff', - '.woff2': '.woff2', - '.ttf': '.ttf', - '.eot': '.eot', - - // Common directories - 'src': 'src', - 'dist': 'dist', - 'build': 'build', - 'public': 'public', - 'assets': 'assets', - 'components': 'components', - 'pages': 'pages', - 'utils': 'utils', - 'helpers': 'helpers', - 'services': 'services', - 'models': 'models', - 'controllers': 'controllers', - 'middleware': 'middleware', - 'routes': 'routes', - 'config': 'config', - 'tests': 'tests', - 'docs': 'docs', - 'examples': 'examples', - 'templates': 'templates', - 'layouts': 'layouts', - 'styles': 'styles', - 'scripts': 'scripts', - 'images': 'images', - 'fonts': 'fonts', - 'icons': 'icons', - 'data': 'data', - 'logs': 'logs', - 'temp': 'temp', - 'cache': 'cache', - 'backup': 'backup', - - // Common patterns - 'Singleton': 'Singleton', - 'Factory': 'Factory', - 'Observer': 'Observer', - 'Strategy': 'Strategy', - 'Command': 'Command', - 'Adapter': 'Adapter', - 'Decorator': 'Decorator', - 'Proxy': 'Proxy', - 'Facade': 'Facade', - 'Bridge': 'Bridge', - 'Composite': 'Composite', - 'Flyweight': 'Flyweight', - 'Template Method': 'Template Method', - 'Chain of Responsibility': 'Chain of Responsibility', - 'State': 'State', - 'Visitor': 'Visitor', - 'Iterator': 'Iterator', - 'Mediator': 'Mediator', - 'Memento': 'Memento', - 'Interpreter': 'Interpreter', - - // Common principles - 'SOLID': 'SOLID', - 'DRY': 'DRY', - 'KISS': 'KISS', - 'YAGNI': 'YAGNI', - 'Single Responsibility': 'Single Responsibility', - 'Open/Closed': 'Open/Closed', - 'Liskov Substitution': 'Liskov Substitution', - 'Interface Segregation': 'Interface Segregation', - 'Dependency Inversion': 'Dependency Inversion', - 'Don\'t Repeat Yourself': 'Don\'t Repeat Yourself', - 'Keep It Simple, Stupid': 'Keep It Simple, Stupid', - 'You Aren\'t Gonna Need It': 'You Aren\'t Gonna Need It' -}; - -function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(cursorTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'gi'), english); - } - - return translatedText; -} - -function createBackup(filePath) { - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const relativePath = path.relative(CURSOR_DIR, filePath); - const backupPath = path.join(BACKUP_DIR, relativePath); - const backupDir = path.dirname(backupPath); - - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir, { recursive: true }); - } - - fs.copyFileSync(filePath, backupPath); - return backupPath; -} - -function translateFile(filePath) { - try { - const content = fs.readFileSync(filePath, 'utf8'); - const translatedContent = translateText(content); - - // Create backup before translation - const backupPath = createBackup(filePath); - - // Write translated content - fs.writeFileSync(filePath, translatedContent, 'utf8'); - - return { - success: true, - backupPath, - originalSize: content.length, - translatedSize: translatedContent.length - }; - } catch (error) { - return { - success: false, - error: error.message - }; - } -} - -function findCursorFiles() { - const files = []; - - function scanDirectory(dir) { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - // Skip backup directory - if (item !== 'backup') { - scanDirectory(fullPath); - } - } else if (stat.isFile()) { - // Only process markdown files - if (item.endsWith('.md') || item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - } - - scanDirectory(CURSOR_DIR); - return files; -} - -function protectCursor() { - console.log('🛡️ Protecting .cursor directory - translating all files to English...\n'); - - const files = findCursorFiles(); - console.log(`📁 Found ${files.length} files to process\n`); - - let successCount = 0; - let errorCount = 0; - const results = []; - - for (const filePath of files) { - const relativePath = path.relative(process.cwd(), filePath); - console.log(`🔄 Processing: ${relativePath}`); - - const result = translateFile(filePath); - results.push({ filePath, ...result }); - - if (result.success) { - console.log(`✅ Translated: ${relativePath}`); - console.log(`📁 Backup: ${path.relative(process.cwd(), result.backupPath)}`); - successCount++; - } else { - console.log(`❌ Error: ${relativePath} - ${result.error}`); - errorCount++; - } - } - - console.log(`\n📊 Summary:`); - console.log(`✅ Successfully translated: ${successCount} files`); - console.log(`❌ Errors: ${errorCount} files`); - console.log(`📁 Backups created in: .cursor/backup/`); - - return { successCount, errorCount, results }; -} - -function checkCursorStatus() { - console.log('🔍 Checking .cursor directory status...\n'); - - const files = findCursorFiles(); - console.log(`📁 Found ${files.length} files in .cursor directory\n`); - - let russianContentCount = 0; - const filesWithRussian = []; - - for (const filePath of files) { - try { - const content = fs.readFileSync(filePath, 'utf8'); - const hasRussian = /[а-яё]/i.test(content); - - if (hasRussian) { - russianContentCount++; - const relativePath = path.relative(process.cwd(), filePath); - filesWithRussian.push(relativePath); - } - } catch (error) { - console.log(`❌ Error reading ${filePath}: ${error.message}`); - } - } - - console.log(`📊 Status Report:`); - console.log(`📁 Total files: ${files.length}`); - console.log(`🇷🇺 Files with Russian content: ${russianContentCount}`); - console.log(`🇺🇸 Files in English only: ${files.length - russianContentCount}`); - - if (filesWithRussian.length > 0) { - console.log(`\n📋 Files that need translation:`); - filesWithRussian.forEach(file => console.log(` - ${file}`)); - } else { - console.log(`\n✅ All files are already in English!`); - } - - return { totalFiles: files.length, russianContentCount, filesWithRussian }; -} - -function restoreFromBackup(backupPath) { - if (!fs.existsSync(backupPath)) { - console.log(`❌ Backup not found: ${backupPath}`); - return false; - } - - try { - const relativePath = path.relative(BACKUP_DIR, backupPath); - const originalPath = path.join(CURSOR_DIR, relativePath); - - fs.copyFileSync(backupPath, originalPath); - console.log(`✅ Restored: ${path.relative(process.cwd(), originalPath)} from backup`); - return true; - } catch (error) { - console.log(`❌ Error restoring ${backupPath}: ${error.message}`); - return false; - } -} - -function listBackups() { - if (!fs.existsSync(BACKUP_DIR)) { - console.log('❌ No backups found'); - return; - } - - console.log('📁 Available backups:'); - - function scanBackups(dir, prefix = '') { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - console.log(`${prefix}📁 ${item}/`); - scanBackups(fullPath, prefix + ' '); - } else { - const stats = fs.statSync(fullPath); - console.log(`${prefix}📄 ${item} (${stats.mtime.toLocaleString()})`); - } - } - } - - scanBackups(BACKUP_DIR); -} - -function main() { - const command = process.argv[2] || 'protect'; - const backupPath = process.argv[3]; - - switch (command) { - case 'protect': - protectCursor(); - break; - case 'check': - checkCursorStatus(); - break; - case 'restore': - if (!backupPath) { - console.log('❌ Please specify backup path'); - console.log('Usage: node .cursor/rules/cursor-protector.cjs restore '); - return; - } - restoreFromBackup(backupPath); - break; - case 'list': - listBackups(); - break; - case 'help': - console.log(` -Cursor Protector Script - -Usage: node .cursor/rules/cursor-protector.cjs [command] [backup-path] - -Commands: - protect - Protect .cursor by translating all files to English (default) - check - Check status of .cursor files (which need translation) - restore - Restore file from backup (requires backup path) - list - List available backups - help - Show this help - -Examples: - node .cursor/rules/cursor-protector.cjs protect - node .cursor/rules/cursor-protector.cjs check - node .cursor/rules/cursor-protector.cjs restore .cursor/backup/rules/ai-memory.mdc - node .cursor/rules/cursor-protector.cjs list - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { - protectCursor, - checkCursorStatus, - translateText, - cursorTranslations, - createBackup, - restoreFromBackup -}; \ No newline at end of file diff --git a/.cursor/rules/dev/date-time-patterns.mdc b/.cursor/rules/dev/date-time-patterns.mdc deleted file mode 100644 index b23fd298..00000000 --- a/.cursor/rules/dev/date-time-patterns.mdc +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: Date and time patterns for AI assista.ts - universal guIDElines for handling dates -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Patterns работы с Dateми и временем - -## Проблема -AI-ассистенты не могут самостоятельно получать актуальную дату и Time, что приводит к использованию устаревших дат в документации. - -## Решение: Usage системных команд - -### Получение текущей даты -```bash -# Текущая Date в формате YYYY-MM-DD -date +"%Y-%m-%d" - -# Текущая Date и Time -date +"%Y-%m-%d %H:%M:%S" - -# Текущая Date на русском языке -date +"%d %B %Y" -``` - -### Получение времени -```bash -# Только Time -date +"%H:%M:%S" - -# Time с часовым поясом -date +"%H:%M:%S %Z" -``` - -## Patterns для AI-ассистентов - -### 1. При создании документации -```bash -# Всегда получать актуальную дату перед созданием документации -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" -``` - -### 2. При обновлении существующей документации -```bash -# Проверять, не устарели ли даты в документации -# Если Date старая - обновлять с актуальной -``` - -### 3. При создании коммитов -```bash -# Использовать актуальную дату в сообщениях коммитов -git commit -m "feat: new feature - $(date +"%Y-%m-%d")" -``` - -## Форматы дат для разных целей - -### Documentation (YYYY-MM-DD) -- `2025-07-12` - стандартный формат для документации -- Используется в memory-bank, README, технической документации - -### Comm.ts (YYYY-MM-DD) -- `2025-07-12` - для сообщений коммитов -- Краткий и понятный формат - -### Пользовательский Interface -- `12 июля 2025` - для пользовательского Interfaceа -- Локализованный формат - -### Logging -- `2025-07-12 14:30:25` - полный формат с временем -- Для системных логов и отладки - -## Проверочный список - -### ✅ Перед созданием документации -- [ ] Получить актуальную дату командой `date +"%Y-%m-%d"` -- [ ] Использовать полученную дату в документации -- [ ] Check формат даты (YYYY-MM-DD) - -### ✅ При обновлении документации -- [ ] Check даты в существующих файлах -- [ ] Update устаревшие даты -- [ ] Убедиться в консистентности форматов - -### ✅ При работе с пользователем -- [ ] Уточнить дату, если есть сомнения -- [ ] Использовать понятные пользователю форматы -- [ ] Объяснить, почему используется системная Date - -## examples использования - -### Создание нового этапа в progress.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### ✅ Этап X: Description (Завершен - $current_date)" -``` - -### Обновление Statusа в errors.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### Status" -echo "✅ **РЕШЕНО** - $current_date" -``` - -### Создание коммита с датой -```bash -current_date=$(date +"%Y-%m-%d") -git commit -m "feat: new feature - $current_date" -``` - -## Частые ошибки - -### ❌ Неправильно -- Usage устаревших дат из контекста -- Разные форматы дат в одном документе -- Отсутствие проверки актуальности дат - -### ✅ Правильно -- Usage `date` команды для получения актуальной даты -- Консистентный формат YYYY-MM-DD -- Регулярная Verification и обновление дат - -## Integration с Cursor IDE - -### Project Rules -Add в project rules: -``` -- Always use system date Commands for current date/time -- Format dates as YYYY-MM-DD in documentation -- Validate dates before using in documentation -``` - -### Saved Memories -Create memory: -``` -Date/Time Pattern: Use 'date +"%Y-%m-%d"' Command to get current date -Format: Always use YYYY-MM-DD format in documentation -Validation: Check dates before using in documentation -``` - -## Автоматизация - -### Скрипт для проверки дат -```bash -#!/bin/bash -# check-dates.sh -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -# Verification дат в документации -grep -r "2024-" docs/ memory-bank/ 2>/dev/null | head -5 -echo "Found old dates above. ConsIDEr updating them." -``` - -### Git hook для проверки дат -```bash -#!/bin/bash -# .git/hooks/pre-commit -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -## New Entry ([2025-07-19T01:32:19.979Z]) - -Паттерн работы с Dateми: Использовать date +%Y-%m-%d для получения текущей даты в документации - -echo "Please ensure all dates in documentation are current." -``` \ No newline at end of file diff --git a/.cursor/rules/dev/development-guidelines.mdc b/.cursor/rules/dev/development-guidelines.mdc deleted file mode 100644 index bbd7f624..00000000 --- a/.cursor/rules/dev/development-guidelines.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Development best practices and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Development GuIDElines - -## Overview - -This file contains dev practice guIDElines and best practices. - -## GuIDElines - - - -## Development Practice ([2025-07-19T01:19:31.302Z]) - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы - - diff --git a/.cursor/rules/dev/development-principles.mdc b/.cursor/rules/dev/development-principles.mdc deleted file mode 100644 index 8ea7328f..00000000 --- a/.cursor/rules/dev/development-principles.mdc +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: Development principles for all proje.ts - universal guIDElines and best practices -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: development-practices ---- - -# 🛡️ Principles разработки - -## Краткая сводка (для быстрого понимания) - -### 🔒 Security и надёжность -1. **"Не навреди"** - Security прежде всего, обратная Compatibility -2. **"Fail Fast, Fail Safe"** - быстрый и безопасный отказ, Graceful Degradation -3. **"data Integrity & Privacy"** - целостность и приватность данных - -### 📊 Наблюдаемость и Quality -4. **"Observability First"** - структурированное Logging, метрики, Monitoring -5. **"Best Practices First"** - проверенные Patterns, Standards, Performance -6. **"configuration as Code"** - Versioning конфигураций, Validation - -### 🚀 User Experience -7. **"Progressive Enhancement"** - базовая функциональность всегда работает -8. **"Continuous Learning"** - Monitoring, обратная связь, A/B Testing - -### 🌐 Экосистема и развитие -9. **"Ecosystem Thinking"** - Compatibility плагинов, стабильность API -10. **"Comme.ts для ИИ"** - аналитические Comme.ts для понимания логики - -## Применение принципов - -### Для каждой задачи ИИ-ассистент должен: -1. **Проанализировать** задачу через призму релевантных принципов -2. **Выбрать** наиболее подходящие Principles для конкретной ситуации -3. **Применить** Principles систематически при принятии решений -4. **Документировать** как Principles повлияли на решение - -### examples применения: -- **Новый плагин** → Principles 1, 2, 5, 6, 9, 10 -- **Исправление бага** → Principles 1, 2, 4, 5, 7 -- **Optimization** → Principles 2, 4, 5, 6, 7, 8 -- **Новая Function** → Principles 1, 3, 5, 7, 8, 9 - -## Взаимодополняемость принципов - -Principles не противоречат друг другу, а усиливают: -- **"Не навреди" + "Fail Fast"** = безопасные быстрые решения -- **"Best Practices" + "Observability"** = качественные решения с Monitoringом -- **"Progressive Enhancement" + "Ecosystem"** = стабильная экосистема -- **"data Privacy" + "configuration"** = безопасная configuration - -## Результат применения - -Следование всем принципам обеспечивает: -- ✅ **Highе Quality кода** и архитектуры -- ✅ **Security** и надёжность системы -- ✅ **Лучший User Experience** - -## New Entry ([2025-07-19T01:32:10.564Z]) - -Принцип разработки: Всегда использовать системные команды для получения актуальной даты - -- ✅ **Масштабируемость** и поддерживаемость -- ✅ **Успешную экосистему** плагинов \ No newline at end of file diff --git a/.cursor/rules/dev/testing-troubleshooting.mdc b/.cursor/rules/dev/testing-troubleshooting.mdc deleted file mode 100644 index d70c8c0c..00000000 --- a/.cursor/rules/dev/testing-troubleshooting.mdc +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing & Troubleshooting Best Practices - -## Documentation тестирования и отладки - -### Ключевые Principles: -- Писать пошаговые гайды для всех основных flows тестирования и отладки -- Разделять usage, troubleshooting и advanced diagnostics в отдельные файлы -- Cross-link гайды для навигации (usage → troubleshooting → advanced) -- Использовать четкие, воспроизводимые шаги и code snipp.ts -- Ссылаться на релевантные скрипты, автоматизацию и best practices -- Обновлять гайды при изменении features или workflows - -### examples гайдов: -- DevTools Testing GuIDE -- DevTools Panel Troubleshooting -- DevTools Panel Usage - -## Анализ внешних репозиториев как submodules - -Для анализа, поиска и отслеживания изменений во внешнем git репозитории: - -### Пошагово: -1. В корне монорепо: - ```bash - git submodule add external/ - git submodule update --init --recursive - ``` - - Пример: - ```bash - git submodule add HTTPS://github.com/QizhengMo/chrome-extension-sIDEpanel-template.git external/sIDEpanel-template - git submodule update --init --recursive - ``` - -2. Открыть монорепо в Cursor/VSCode. Submodule будет проиндексирован и доступен для поиска кода, навигации и анализа. - -3. Update submodule до последней версии: - ```bash - cd external/sIDEpanel-template - git pull origin main - ``` - -4. Delete submodule (если больше не нужен): - ```bash - git submodule deinit -f external/sIDEpanel-template - git rm -f external/sIDEpanel-template - rm -rf .git/modules/external/sIDEpanel-template - ``` - -### Преимущества: -- Изолирует внешний код от основной git истории -- Позволяет легкие обновления и независимое Versioning -- Обеспечивает полный поиск кода и анализ в Cursor/VSCode - -**Используйте этот Method для любого внешнего кодового база, который хотите анализировать или ссылаться в монорепо.** -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/ai-answer-self-check.mdc b/.cursor/rules/doc/ai-answer-self-check.mdc deleted file mode 100644 index a1409f4f..00000000 --- a/.cursor/rules/doc/ai-answer-self-check.mdc +++ /dev/null @@ -1,39 +0,0 @@ - - - - ---- -description: ai answer self check rule -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: falseaiPriority: medium -aiCategory: documentation ---- - -# AI Answer Self-Check Policy - -AI-ассистент обязан для каждого ответа: - -## Обязательные Requireme.ts: -- Разбивать вопрос пользователя на подпункты/чеклист -- Явно отмечать, на что уже дан ответ, а что требует пояснения или осталось неотвеченным -- В конце ответа давать self-check: какие пункты покрыты, какие требуют доработки или согласования -- Если требование уже реализовано — явно указывать где и как (файл, секция, коммит, etc.) -- Для сложных вопросов использовать маркированные списки или таблицы - -## Пример self-check: -``` ---- -**Self-check для вашего вопроса:** -1. ✅ Механизм self-check — описан и предложен для реализации -2. ✅ Пример предоставлен выше -3. ✅ Предыдущий случай объяснен (требование уже реализовано) -4. ⚠️ Если хотите это в каждом ответе — можно сделать стандартом (подтвердите) ---- -``` - -## Применение: -Это правило Required для всех коммуникаций AI в проекте. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/always-russian-answer.mdc b/.cursor/rules/doc/always-russian-answer.mdc deleted file mode 100644 index fa65a4f0..00000000 --- a/.cursor/rules/doc/always-russian-answer.mdc +++ /dev/null @@ -1,7 +0,0 @@ -# Всегда отвечай на русском языке - -AI-ассистент обязан всегда давать ответы пользователю на русском языке, независимо от языка запроса или исходного контекста, если явно не указано иное. Исключение — если пользователь просит ответить на другом языке или задача требует иного языка для технической совместимости. -description: -globs: -alwaysApply: true ---- diff --git a/.cursor/rules/doc/auto-translate-requests.mdc b/.cursor/rules/doc/auto-translate-requests.mdc deleted file mode 100644 index df559883..00000000 --- a/.cursor/rules/doc/auto-translate-requests.mdc +++ /dev/null @@ -1,387 +0,0 @@ -# Auto Translate Requests System - -## Overview - -The Auto Translate Requests System automatically translates user requests from Russian to English when creating .cursor rules, ensuring that all rules are created in English for maximum AI/LLM compatibility. - -## Key Features - -### 🤖 **Automatic Translation** -- **Real-time translation** of user requests to English -- **Comprehensive coverage** of technical terminology -- **High accuracy** with confidence scoring -- **Context-aware** translation for .cursor rules - -### 📝 **Rule Creation** -- **Automatic file generation** with proper structure -- **English-only content** for AI/LLM compatibility -- **Git integration** with automatic staging -- **Template-based** rule generation - -### 🔧 **Multiple Interfaces** -- **Command-line interface** for quick rule creation -- **Interactive mode** for guided rule creation -- **NPM scripts** for easy integration -- **Direct API** for programmatic use - -## System Components - -### **1. Request Translator (`request-translator.cjs`)** -Core translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **Request analysis** - pattern detection and confidence scoring -- **English prompt generation** - AI-ready prompts -- **Context preservation** - maintains original request information - -### **2. Auto Translate Requests (`auto-translate-requests.cjs`)** -Complete system management: -- **Interactive mode** - guided rule creation -- **Setup automation** - system configuration -- **Rule creation** - automatic file generation -- **Integration management** - npm scripts and helpers - -### **3. Quick Rule Creator (`create-rule.cjs`)** -Fast rule creation utility: -- **One-command creation** - translate and create in one step -- **Automatic staging** - git integration -- **Template generation** - structured rule files -- **Progress feedback** - clear status updates - -## Usage - -### **Quick Rule Creation (Recommended)** -```bash -# Create rule with automatic translation -node .cursor/rules/create-rule.cjs "создай правило для архитектуры" - -# Using npm script -npm run create-rule "добавь правило безопасности" -``` - -### **Interactive Mode** -```bash -# Start interactive mode -node .cursor/rules/auto-translate-requests.cjs interactive - -# Using npm script -npm run interactive-rules -``` - -### **Direct Translation** -```bash -# Translate request only -node .cursor/rules/request-translator.cjs translate "напиши правило для тестирования" - -# Analyze request -node .cursor/rules/request-translator.cjs analyze "создай файл в .cursor" -``` - -### **System Setup** -```bash -# Setup auto translation system -node .cursor/rules/auto-translate-requests.cjs setup -``` - -## Translation Coverage - -### **Request Patterns** -- **Creation requests** - создай, добавь, напиши, сделай -- **File types** - правило, файл, документ, конфигурация -- **Locations** - в .cursor, в rules, в папку -- **Content types** - с описанием, с примерами, с инструкциями - -### **Technical Terminology** -- **Development terms** - разработка → development -- **Architecture terms** - архитектура → architecture -- **Security terms** - безопасность → security -- **Performance terms** - производительность → performance -- **Quality terms** - качество → quality - -### **Common Phrases** -- **Important notes** - важные примечания -- **Best practices** - лучшие практики -- **Requirements** - требования -- **Recommendations** - рекомендации - -## Generated Rule Structure - -### **File Format** -```markdown ---- -description: [translated request] -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "[original request]" -translatedAt: "[timestamp]" ---- - -# [translated request] - -## Overview - -This rule was created based on the request: "[original request]" - -## Description - -[translated request] - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "[original request]"* -*Translated to English for AI/LLM compatibility* -``` - -### **Metadata Fields** -- **description** - Translated request description -- **globs** - File patterns this rule applies to -- **alwaysApply** - Whether rule should always be applied -- **aiPriority** - Priority level for AI processing -- **aiCategory** - Category for AI organization -- **createdFrom** - Original user request -- **translatedAt** - Translation timestamp - -## Workflow Integration - -### **For Users** -1. **Write request in Russian** - System automatically translates -2. **Review translation** - Check confidence and accuracy -3. **Confirm creation** - Rule is created in English -4. **Edit as needed** - Add specific content and details -5. **Commit changes** - Git integration handles the rest - -### **For AI Assistants** -1. **Receive English prompt** - All requests translated automatically -2. **Create English rules** - No manual translation needed -3. **Follow templates** - Consistent structure and format -4. **Maintain compatibility** - All content in English - -### **For Development** -1. **Automatic protection** - Rules created in English -2. **Git integration** - Automatic staging and commits -3. **Template consistency** - Standardized rule structure -4. **Backup preservation** - Original requests preserved - -## Configuration - -### **Auto Translate Config** -```json -{ - "enabled": true, - "autoTranslate": true, - "createEnglishPrompts": true, - "backupOriginalRequests": true, - "confidenceThreshold": 70, - "patterns": { - "ruleCreation": ["создай", "добавь", "напиши", "сделай"], - "fileCreation": ["файл", "документ", "правило"], - "cursorRequests": [".cursor", "cursor", "rules"] - } -} -``` - -### **NPM Scripts** -```json -{ - "scripts": { - "create-rule": "node .cursor/rules/create-rule.cjs", - "translate-request": "node .cursor/rules/request-translator.cjs translate", - "interactive-rules": "node .cursor/rules/auto-translate-requests.cjs interactive" - } -} -``` - -## Benefits - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read rules -- **Language consistency** - All rules in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - Original requests preserved -- **Git integration** - Seamless workflow integration -- **Template consistency** - Standardized rule structure - -## Best Practices - -### **For Users** -1. **Use clear requests** - Specific and descriptive requests -2. **Trust automatic translation** - System is comprehensive -3. **Review generated content** - Edit templates as needed -4. **Use English for new content** - Maintain consistency - -### **For AI Assistants** -1. **Always create rules in English** -2. **Trust the translation system** - It handles Russian requests -3. **Follow generated templates** - Maintain consistency -4. **Use standard terminology** - From translation mappings - -### **For Developers** -1. **Extend translation mappings** - Add missing terms as needed -2. **Test translation quality** - Verify accuracy -3. **Maintain templates** - Keep them up to date -4. **Monitor confidence scores** - Improve low-confidence translations - -## Error Handling - -### **Translation Errors** -- **Partial translation** - System continues with available mappings -- **Confidence scoring** - Clear indication of translation quality -- **Original preservation** - Original request always saved -- **Manual fallback** - Option to edit manually - -### **Creation Errors** -- **Directory creation** - Automatic directory structure -- **File conflicts** - Unique filename generation -- **Git integration** - Graceful handling of git errors -- **Template fallback** - Basic template if generation fails - -### **System Errors** -- **Graceful degradation** - System continues with available features -- **Error reporting** - Clear error messages -- **Recovery options** - Multiple ways to create rules -- **User guidance** - Clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Poor translation quality** -1. Check confidence score in analysis -2. Extend translation mappings if needed -3. Use more specific terminology -4. Report missing terms for improvement - -#### **Rule creation fails** -1. Check file permissions -2. Verify directory structure -3. Check git status -4. Use manual creation as fallback - -#### **Interactive mode issues** -1. Check Node.js version compatibility -2. Verify readline module availability -3. Check terminal compatibility -4. Use command-line mode as alternative - -### **Debug Commands** -```bash -# Test translation quality -node .cursor/rules/request-translator.cjs analyze "your request" - -# Check system status -node .cursor/rules/auto-translate-requests.cjs setup - -# Verify file creation -ls -la .cursor/rules/ - -# Check git status -git status -``` - -## Future Enhancements - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during typing -- **Translation quality scoring** - -### **Integration Ideas** -- **IDE plugins** for real-time translation -- **Web interface** for rule creation -- **API endpoints** for programmatic access -- **Collaborative translation** for community contributions - -## Examples - -### **Example 1: Architecture Rule** -```bash -# User request (Russian) -node .cursor/rules/create-rule.cjs "создай правило для архитектуры проекта" - -# Generated English rule ---- -description: create rule for architecture project -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "создай правило для архитектуры проекта" -translatedAt: "2025-07-19T02:58:58.601Z" ---- - -# create rule for architecture project - -## Overview - -This rule was created based on the request: "создай правило для архитектуры проекта" -``` - -### **Example 2: Security Rule** -```bash -# User request (Russian) -node .cursor/rules/create-rule.cjs "добавь правило безопасности для API" - -# Generated English rule ---- -description: add security rule for API -globs: ["**/*"] -alwaysApply: false -aiPriority: high -aiCategory: security -createdFrom: "добавь правило безопасности для API" -translatedAt: "2025-07-19T02:59:15.123Z" ---- - -# add security rule for API - -## Overview - -This rule was created based on the request: "добавь правило безопасности для API" -``` - -## Conclusion - -The Auto Translate Requests System ensures that all .cursor rules are automatically created in English for maximum AI/LLM compatibility. This creates a seamless workflow where users can write requests in Russian, and the system automatically translates and creates English rules. - -**Key Benefits:** -- ✅ **Automatic translation** of user requests -- ✅ **English-only rule creation** -- ✅ **AI/LLM compatibility** -- ✅ **Git integration** -- ✅ **Template consistency** -- ✅ **Error handling and recovery** - -**Usage:** Simply use `node .cursor/rules/create-rule.cjs "your request in Russian"` to automatically translate and create English rules for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/chrome-extension-manual-restore.mdc b/.cursor/rules/doc/chrome-extension-manual-restore.mdc deleted file mode 100644 index 825d8541..00000000 --- a/.cursor/rules/doc/chrome-extension-manual-restore.mdc +++ /dev/null @@ -1,26 +0,0 @@ -# Chrome Extension Manual Restore — Experience & Pitfalls (2024-07-20) - -## Manual Restore Workflow -- Locate the required extension archive in `dist-zip/` (e.g., `extension-20250716-043234.zip`). -- Unzip to `/tmp/chrome-extension-old`: - ```bash - unzip -o dist-zip/extension-20250716-043234.zip -d /tmp/chrome-extension-old - ``` -- Launch Chromium with the script: - ```bash - bash bash-scripts/run-chromium-old-extension.sh - ``` -- The extension will be loaded and available for manual UI inspection and copying. - -## Typical Errors & Solutions -- **Manifest file missing**: Ensure `manifest.json` is present in `/tmp/chrome-extension-old` after unzipping. -- **unzip not found**: Install with `sudo pacman -S unzip` or `sudo apt install unzip`. -- **WDIO browser closes too soon**: Use the bash script for unlimited manual work time. - ---- - -This rule and experience are recorded for future automation and to avoid repeating past mistakes. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/command-synchronization.mdc b/.cursor/rules/doc/command-synchronization.mdc deleted file mode 100644 index 69bab2fe..00000000 --- a/.cursor/rules/doc/command-synchronization.mdc +++ /dev/null @@ -1,264 +0,0 @@ -# Command Synchronization System - -## Overview - -Automated system for synchronizing user Commands between multiple sources: -- `USER_CommandS.md` - User-friendly Command reference -- `.cursor/rules/cursor-export/ai-memory.mdc` - AI assistant instructions -- `.cursor/rules/ai-memory.mdc` - Cursor rules -- `CURSOR_AI_MEMORY_BANK.md` - Export for Cursor AI memory-bank - -## How It Works - -### Single Source of Truth -All Commands are defined in `.cursor/rules/Command-sync..js` in a structured format: - -```JavaScript -const CommandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Save контекст сессии'], - description: 'Save achieveme.ts, decisions and plans to memory-bank', - userDescription: 'AI-ассистент сохранит все достижения, решения и планы в memory-bank' - } - } -} -``` - -### Automatic Synchronization -The script generates all files from the single source: - -```bash -# Sync all files -node .cursor/rules/Command-sync..js sync - -# Export for Cursor AI memory-bank -node .cursor/rules/Command-sync..js export - -# Translate context to English -node .cursor/rules/Command-sync..js translate-context -``` - -## File Form.ts - -### USER_CommandS.md -User-friendly format with emojis and detailed descriptions: -```markdown -## 📝 Сохранение контекста - -### Save контекст сессии -`Сохрани контекст` / `Save контекст сессии` -*AI-ассистент сохранит все достижения, решения и планы в memory-bank* -``` - -### ai-memory.mdc -AI-optimized format for .cursor rules: -```markdown -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -### CURSOR_AI_MEMORY_BANK.md -Format optimized for Cursor AI memory-bank: -```markdown -### 📝 Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -## Context Translation System - -### Automatic Context Translation -The system automatically translates context to English for AI/LLM compatibility: - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only -``` - -### Features -- ✅ **Automatic translation** from Russian to English -- ✅ **backup creation** before translation -- ✅ **Git integration** with automatic comm.ts -- ✅ **Comprehensive coverage** of all context terminology -- ✅ **Error handling** with safe fallbacks - -### Translation Coverage -- **Headers and titles** - All section headers -- **Task status** - Completed tasks, current focus, next steps -- **Principles** - Working principles and guIDElines -- **Technical context** - Architecture and standards -- **Command system** - Command categories and descriptions -- **User experience** - UX priorities and metrics -- **Development plans** - Short, medium, and long-term goals -- **Status indicators** - Readiness and completion status - -## Integration with Cursor AI Memory-Bank - -### Manual Integration -1. Run export Command: - ```bash - node .cursor/rules/Command-sync..js export - ``` - -2. Copy content from `CURSOR_AI_MEMORY_BANK.md` - -3. Go to Cursor Settings → AI → Rules & Memories - -4. Paste into User Rules or Project Rules - -5. Save and restart Cursor - -### Automatic Integration (Future) -Planned features: -- Direct API integration with Cursor -- Automatic updates when Commands change -- Version control for AI memory-bank - -## Command Categories - -### 📝 Context and Memory -- `save context` - Save achieveme.ts, decisions and plans in English (automatic translation) -- `update progress` - Update project status files -- `restore context` - Restore full project understanding -- `quick restore` - Get brief summary - -### 🏗️ Analysis and Study -- `analyze architecture` - Study system patterns -- `study plugins` - Analyze plugin structure -- `check build` - Verify project builds -- `update documentation` - Update project docs - -### 🔧 Development -- `create plugin [name]` - Create new plugin -- `check code` - Run linting and type checking -- `run te.ts` - Run all project te.ts -- `check dependencies` - Verify dependencies - -### 📊 Project Management -- `bump version patch/minor/major` - Increase version -- `clean project` - Clean build artifa.ts -- `analyze performance` - Performance analysis -- `check security` - Security analysis - -### 🚀 Releases and Deployment -- `create release` - Prepare for release -- `build production` - Production build - -## Benef.ts - -### For Users -- ✅ **Consistent Commands** across all sources -- ✅ **Easy to use** - just copy and paste -- ✅ **Bilingual support** - English and Russian -- ✅ **Always up-to-date** - automatic synchronization - -### For AI Assista.ts -- ✅ **Structured format** - easy to parse -- ✅ **Clear descriptions** - understand what each Command does -- ✅ **Multiple sources** - available in all conte.ts -- ✅ **Semantic understanding** - works with any language - -### For Development -- ✅ **Single source of truth** - no duplication -- ✅ **Automatic updates** - change once, update everywhere -- ✅ **Version control** - track Command changes -- ✅ **Easy maintenance** - centralized management - -## Usage examples - -### Adding New Commands -1. Edit `.cursor/rules/Command-sync..js` -2. Add Command to appropriate category -3. Run sync Command: - ```bash - node .cursor/rules/Command-sync..js sync - ``` - -### Updating Existing Commands -1. Edit Command in `.cursor/rules/Command-sync..js` -2. Run sync Command -3. All files updated automatically - -### Exporting for Cursor -```bash -node .cursor/rules/Command-sync..js export -``` - -## Technical Details - -### File Structure -``` -.cursor/rules/ -├── Command-sync..js # Main synchronization script -├── ai-memory.mdc # Cursor rules (auto-generated) -├── cursor-export/ -│ └── ai-memory.mdc # AI assistant instructions (auto-generated) -└── doc/ - └── Command-synchronization.mdc # This documentation - -USER_CommandS.md # User reference (auto-generated) -CURSOR_AI_MEMORY_BANK.md # Cursor export (auto-generated) -``` - -### Script Functions -- `generateUserCommand.md()` - Generate USER_CommandS.md -- `generateAIMemor.md()` - Generate ai-memory.mdc files -- `generateCursorMemoryBank()` - Generate Cursor format -- `syncCommands()` - Sync all files -- `exportForCursor()` - Export for Cursor AI memory-bank - -### Command Structure -Each Command has: -- **English Command** - Primary Command -- **Russian alternatives** - User-friendly alternatives -- **Description** - Technical description for AI -- **User description** - User-friendly description - -## Future Enhanceme.ts - -### Planned Features -- [ ] **API Integration** - Direct Cursor API integration -- [ ] **Auto-sync** - Automatic sync on file changes -- [ ] **Command validation** - Validate Command syntax -- [ ] **Usage analytics** - Track Command usage -- [ ] **template system** - Command templates for different project types - -### Integration IDEas -- **Git hooks** - Auto-sync on commit -- **CI/CD integration** - Validate Command consistency -- **Plugin system** - Extensible Command categories -- **Multi-language support** - More languages beyond English/Russian - -## Troubleshooting - -### Common Issues -1. **Sync fails** - Check file permissions -2. **Commands not working** - Verify Cursor AI memory-bank integration -3. **Format issues** - Check Command structure in script - -### Debug Commands -```bash -# Check script help -node .cursor/rules/Command-sync..js help - -# Verify file generation -ls -la USER_CommandS.md CURSOR_AI_MEMORY_BANK.md -``` - -## Conclusion - -The Command synchronization system provIDEs: -- ✅ **Consistent experience** across all platforms -- ✅ **Easy maintenance** with single source of truth -- ✅ **Automatic updates** for all Command sources -- ✅ **AI-optimized format** for maximum compatibility -- ✅ **User-friendly interface** for easy adoption - -This system ensures that Commands work sea.lessLy across USER_CommandS.md, .cursor rules, and Cursor AI memory-bank settings. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/context-translation-system.mdc b/.cursor/rules/doc/context-translation-system.mdc deleted file mode 100644 index a1183df8..00000000 --- a/.cursor/rules/doc/context-translation-system.mdc +++ /dev/null @@ -1,206 +0,0 @@ -# Context Translation System - -## Overview - -The Context Translation System automatically translates context files from Russian to English for maximum AI/LLM compatibility. This ensures that all context saved in memory-bank is accessible to any AI assistant, regar.less of language preferences. - -## Key Features - -### 🌍 **Automatic Translation** -- **Sea.less translation** from Russian to English -- **Comprehensive coverage** of all context terminology -- **Preserves structure** and formatting -- **Creates backups** before translation - -### 🔄 **Integrated with Save Context Command** -- **Automatic translation** when using "save context" Command -- **Git integration** with automatic comm.ts -- **backup creation** for safety -- **Error handling** for robust operation - -### 📁 **backup System** -- **Automatic backups** before translation -- **Timestamped files** in `memory-bank/core/backup/` -- **Easy restoration** if needed -- **Safe operation** with rollback capability - -## Usage - -### For AI Assista.ts - -When a user issues the Command "save context" or "Сохрани контекст": - -1. **Automatically translate** context to English -2. **Create backups** of original files -3. **Save translated files** to memory-bank -4. **Commit changes** to git with descriptive message -5. **Notify user** of completion - -### Manual Usage - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only - -# Translate context using Command sync script -node .cursor/rules/Command-sync..js translate-context -``` - -## Translation Coverage - -### **Headers and Titles** -- `Active контекст разработки` → `Active Development Context` -- `Текущий Status проекта` → `Current Project Status` -- `Последнее обновление` → `Last Updated` - -### **Task Status** -- `Завершенные задачи` → `Completed Tasks` -- `Текущий фокус` → `Current Focus` -- `Следующие шаги` → `Next Steps` - -### **Principles and GuIDElines** -- `Ключевые Principles работы` → `Key Working Principles` -- `Инициативность ассистента` → `Assistant Initiative` -- `Quality кода` → `Code Quality` - -### **Technical Context** -- `Технический контекст` → `Technical Context` -- `Текущая Architecture` → `Current Architecture` -- `Standards разработки` → `Development Standards` - -### **Command System** -- `Система команд` → `Command System` -- `Автоматическая Synchronization` → `Automatic Synchronization` -- `Категории команд` → `Command Categories` - -### **User Experience** -- `User Experience` → `User Experience` -- `Priorityы UX` → `UX Priorities` -- `Метрики качества` → `Quality Metrics` - -### **Development Plans** -- `Планы развития` → `Development Plans` -- `Краткосрочные цели` → `Short-term Goals` -- `Mediumсрочные цели` → `Medium-term Goals` -- `Долгосрочные цели` → `Long-term Goals` - -### **Status and Readiness** -- `Status готовности` → `Readiness Status` -- `Готовые компоненты` → `Ready Compone.ts` -- `Готово к публикации` → `Ready for publication` - -## File Structure - -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Main context file (English) -│ ├── progress.md # Progress tracking (English) -│ └── backup/ # backup directory -│ ├── activeContext-2024-07-19T10-30-00-000Z.md -│ └── progress-2024-07-19T10-30-00-000Z.md -``` - -## Scri.ts - -### **save-context..js** -Main script for saving context with automatic translation: -- Translates context to English -- Creates backups -- Comm.ts changes to git -- ProvIDEs error handling - -### **Command-sync..js** -Extended with translation functionality: -- `translate-context` Command -- Integrated translation function -- Comprehensive translation mappings - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read context -- **Language consistency** - All context in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - Automatic comm.ts with clear messages -- **Error handling** - Robust operation with fallbacks - -## Best Practices - -### **For AI Assista.ts** -1. **Always use "save context"** Command for context preservation -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English context** for all AI operations - -### **For Users** -1. **Use Russian Commands** - translation happens automatically -2. **Check backup directory** if you need original files -3. **Trust the system** - translations are accurate and comprehensive -4. **Report issues** if translation problems occur - -### **For Developers** -1. **Extend translation mappings** as needed -2. **Test translations** before deployment -3. **Maintain backup system** for safety -4. **Update documentation** when adding new terms - -## Error Handling - -### **Translation Errors** -- **backup preservation** - Original files always saved -- **Partial translation** - System continues with available mappings -- **Error reporting** - Clear messages about issues -- **Manual fallback** - Option to restore from backup - -### **Git Errors** -- **Translation still works** - Files translated even if commit fails -- **Manual commit option** - User can commit manually -- **Clear error messages** - Specific information about git issues -- **Safe operation** - No data loss in case of git problems - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages - -### **Potential Improveme.ts** -- **Real-time translation** during editing -- **Translation quality scoring** -- **User feedback system** for translations -- **Integration with more AI tools** - -## Conclusion - -The Context Translation System ensures that all context in memory-bank is accessible to any AI assistant by automatically translating content to English. This creates a truly international and AI-compatible documentation system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic translation process** -- ✅ **Safe backup system** -- ✅ **Git integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling** - -**Usage:** Simply use the "save context" Command, and the system will automatically translate and save your context in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/cursor-protection-system.mdc b/.cursor/rules/doc/cursor-protection-system.mdc deleted file mode 100644 index 9170030c..00000000 --- a/.cursor/rules/doc/cursor-protection-system.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Cursor Protection System - -## Overview - -The Cursor Protection System automatically ensures that all files in the `.cursor` directory are written in English for maximum AI/LLM compatibility. This system provIDEs comprehensive protection through automatic translation, Git hooks, and monitoring. - -## Key Features - -### 🛡️ **Automatic Protection** -- **Real-time translation** of all .cursor files to English -- **Comprehensive coverage** of technical terminology -- **Preserves structure** and formatting -- **Creates backups** before any changes - -### 🔧 **Git Integration** -- **Pre-commit hooks** - automatically translate staged .cursor files -- **Post-commit hooks** - verify protection status after comm.ts -- **Pre-push hooks** - block push if Russian content is detected -- **Automatic staging** of translated files - -### 📊 **Monitoring and Status** -- **Status checking** - IDEntify files that need translation -- **Protection repo.ts** - comprehensive status overview -- **backup management** - easy restoration from backups -- **Integration status** - verify all compone.ts are working - -## System Compone.ts - -### **1. Cursor Protector (`cursor-protector..js`)** -Main translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **File scanning** - recursive directory processing -- **backup creation** - automatic backup before translation -- **Status reporting** - detailed analysis of files - -### **2. Git Hooks (`cursor-git-hook..js`)** -Automatic protection during Git operations: -- **Pre-commit** - translate staged .cursor files -- **Post-commit** - verify overall protection status -- **Pre-push** - block push if Russian content found -- **Hook management** - install/uninstall hooks - -### **3. Protection Manager (`protect-cursor..js`)** -Complete system management: -- **Full protection** - translate, install hooks, commit -- **System installation** - setup all compone.ts -- **Status checking** - comprehensive protection report -- **backup restoration** - restore from backups - -## Usage - -### **Complete Protection (Recommended)** -```bash -# Protect all .cursor files, install hooks, and commit -node .cursor/rules/protect-cursor..js protect -``` - -### **System Installation** -```bash -# Install protection system (hooks, scri.ts, .cursorignore) -node .cursor/rules/protect-cursor..js install -``` - -### **Status Checking** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Check .cursor files specifically -node .cursor/rules/cursor-protector..js check -``` - -### **Manual Protection** -```bash -# Protect all .cursor files -node .cursor/rules/cursor-protector..js protect - -# Install Git hooks -node .cursor/rules/cursor-git-hook..js install -``` - -### **Package.json Scri.ts** -After installation, use npm scri.ts: -```bash -npm run protect-cursor # Complete protection -npm run check-cursor # Check status -npm run install-cursor-hooks # Install hooks -``` - -## Translation Coverage - -### **Technical Terminology** -- **Development terms** - Development → development -- **Architecture terms** - Architecture → architecture -- **Security terms** - Security → security -- **Performance terms** - Performance → performance -- **Quality terms** - Quality → quality - -### **File Structure** -- **Headers and titles** - заголовки и названия -- **Section names** - названия разделов -- **Status indicators** - индикаторы Statusа -- **Action descriptions** - описания действий - -### **Common Phrases** -- **Important notes** - важные Notes -- **Best practices** - Best Practices -- **Requireme.ts** - Requireme.ts -- **Recommendations** - Recommendations - -## Git Hook Behavior - -### **Pre-commit Hook** -1. **Scans staged files** for .cursor files -2. **Dete.ts Russian content** in staged files -3. **Automatically translates** files with Russian content -4. **Creates backups** before translation -5. **Stages translated files** for commit - -### **Post-commit Hook** -1. **Checks overall .cursor status** -2. **Repo.ts any remaining Russian content** -3. **Sugge.ts actions** if needed -4. **Confirms protection status** - -### **Pre-push Hook** -1. **Scans all .cursor files** -2. **Blocks push** if Russian content found -3. **ProvIDEs clear error message** -4. **Sugge.ts protection Command** - -## File Structure - -``` -.cursor/ -├── rules/ -│ ├── cursor-protector..js # Main translation engine -│ ├── cursor-git-hook..js # Git hooks -│ ├── protect-cursor..js # Protection manager -│ └── doc/ -│ └── cursor-protection-system.mdc # This documentation -├── backup/ # backup directory -│ ├── pre-commit/ # Pre-commit backups -│ └── [timestamped-backups]/ # Manual backups -└── [other .cursor files] # Protected files -``` - -## configuration - -### **.cursorignore** -Exclude files from automatic translation: -``` -# Cursor Protection System -# Files that should not be automatically translated - -# backup files -.cursor/backup/ - -# temporary files -*.tmp -*.temp - -# Log files -*.log - -# cache files -.cache/ -node_modules/ -``` - -### **Package.json Scri.ts** -Automatically added during installation: -``.json -{ - "scri.ts": { - "protect-cursor": "node .cursor/rules/protect-cursor..js protect", - "check-cursor": "node .cursor/rules/cursor-protector..js check", - "install-cursor-hooks": "node .cursor/rules/cursor-git-hook..js install" - } -} -``` - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - any AI assistant can read .cursor files -- **Language consistency** - all files in English -- **Better understanding** - clear terminology for AI processing -- **Reduced confusion** - no mixed language content - -### **For International Community** -- **Global accessibility** - ready for international developers -- **Standardized format** - consistent English documentation -- **Easy sharing** - no language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - no manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - sea.less workflow integration -- **Error prevention** - blocks problematic comm.ts/pushes - -## Best Practices - -### **For Users** -1. **Run complete protection** after any .cursor changes -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English for new files** - maintain consistency - -### **For AI Assista.ts** -1. **Always write .cursor files in English** -2. **Trust the protection system** - it handles translation -3. **Check protection status** if unsure -4. **Use standard terminology** from translation mappings - -### **For Developers** -1. **Install protection system** in new proje.ts -2. **Extend translation mappings** as needed -3. **Test protection** before deployment -4. **Maintain backup system** for safety - -## Error Handling - -### **Translation Errors** -- **backup preservation** - original files always saved -- **Partial translation** - system continues with available mappings -- **Error reporting** - clear messages about issues -- **Manual fallback** - option to restore from backup - -### **Git Hook Errors** -- **Translation still works** - files translated even if hooks fail -- **Manual protection option** - user can run protection manually -- **Clear error messages** - specific information about issues -- **Safe operation** - no data loss in case of hook problems - -### **System Errors** -- **Graceful Degradation** - system continues with available features -- **Error logging** - detailed error information -- **Recovery options** - multiple ways to restore functionality -- **User guidance** - clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Files not being translated** -1. Check if files are in `.cursorignore` -2. Verify file extensions (.md, .mdc) -3. Run manual protection: `node .cursor/rules/cursor-protector..js protect` - -#### **Git hooks not working** -1. Check hook installation: `node .cursor/rules/cursor-git-hook..js install` -2. Verify hook permissions (should be executable) -3. Check for confli.ts with other hooks - -#### **Translation quality issues** -1. Check backup files for original content -2. Extend translation mappings if needed -3. Report missing terms for system improvement - -### **Debug Commands** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Test translation on specific file -node .cursor/rules/cursor-protector..js protect - -# Verify Git hooks -ls -la .git/hooks/ - -# Check backup files -ls -la .cursor/backup/ -``` - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during editing -- **Translation quality scoring** - -### **Integration IDEas** -- **IDE plugins** for real-time protection -- **CI/CD integration** for automated protection -- **Webhook system** for remote protection -- **Collaborative translation** for community contributions - -## Conclusion - -The Cursor Protection System ensures that all `.cursor` files are automatically maintained in English for maximum AI/LLM compatibility. This creates a truly international and AI-compatible configuration system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic protection process** -- ✅ **Safe backup system** -- ✅ **Git workflow integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling and recovery** - -**Usage:** Simply run `node .cursor/rules/protect-cursor..js protect` to activate complete protection, and the system will automatically maintain all `.cursor` files in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/documentation-map.mdc b/.cursor/rules/doc/documentation-map.mdc deleted file mode 100644 index 358d7a02..00000000 --- a/.cursor/rules/doc/documentation-map.mdc +++ /dev/null @@ -1,196 +0,0 @@ -# Documentation Map - Карта назначения файлов - -## Команда "задокументируй" - четкие правила размещения - -### 🎯 **Универсальные правила и best practices → `.cursor/rules/`** - -**Критерии для `.cursor/rules/`:** -- ✅ Применимо к любым проектам -- ✅ Проверенные Patterns и подходы -- ✅ Автоматизация и workflow -- ✅ Standards разработки -- ✅ Documentation и структуры - -**Файлы назначения:** -- `dev/dev-principle-*.mdc` - Principles разработки -- `dev/development-principles.mdc` - универсальные Principles разработки -- `dev/date-time-patterns.mdc` - Patterns работы с Dateми -- `workflow/ci-automation.mdc` - CI/CD автоматизация -- `workflow/experience-transfer.mdc` - перенос опыта -- `doc/ai-first.mdc` - Standards документации -- `ui/ui-*.mdc` - UI/UX Standards -- `plugin/plugin-*.mdc` - Standards плагинов -- `security/validation.mdc` - правила безопасности -- `architecture/architecture-*.mdc` - архитектурные Principles -- `architecture/file-relationships.mdc` - Patterns организации файлов - -### 🧠 **Проектно-специфичный контекст → `memory-bank/`** - -**Критерии для `memory-bank/`:** -- ✅ Уникален для данного проекта -- ✅ Текущий Status и прогресс -- ✅ История ошибок и решений -- ✅ Принятые архитектурные решения -- ✅ Планы развития проекта - -**Файлы назначения:** -- `errors.md` - кладбище ошибок и решений -- `progress.md` - прогресс разработки -- `activeContext.md` - текущий контекст -- `architecture-decisions.md` - принятые решения -- `future-plans.md` - планы развития -- `testing-resu.ts.md` - результаты тестирования -- `sIDE-panel-improveme.ts.md` - улучшения UI -- `version-management.md` - управление версиями - -## 📋 **Детальная карта по Typeам контента** - -### **Errors and solutions:** -``` -Type ошибки → Файл назначения -├── build/TypeScript → memory-bank/errors.md -├── Runtime/UI → memory-bank/errors.md -├── Архитектурная → memory-bank/architecture-decisions.md -├── Security → .cursor/rules/security/validation.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── UI/UX → .cursor/rules/ui/ui-*.mdc -``` - -### **Best practices:** -``` -Type практики → Файл назначения -├── Development → .cursor/rules/dev/dev-principle-*.mdc -├── Documentation → .cursor/rules/doc/ai-first.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── Git workflow → .cursor/rules/workflow/branches.mdc -└── Plugins → .cursor/rules/plugin/plugin-*.mdc -``` - -### **Автоматизация:** -``` -Type automation → Файл назначения -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── build → .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── Деплой → .cursor/rules/workflow/automation.mdc -└── Monitoring → .cursor/rules/architecture/architecture-observability.mdc -``` - -### **Architectural decisions:** -``` -Type решения → Файл назначения -├── Проектно-специфичное → memory-bank/architecture-decisions.md -├── Универсальное → .cursor/rules/architecture/architecture-*.mdc -├── Security → .cursor/rules/architecture/architecture-security.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── Plugins → .cursor/rules/architecture/architecture-plugin.mdc -``` - -## 🔄 **Workflow команды "задокументируй"** - -### **1. Анализ контента:** -```bash -# Определить Type контента -- Error/решение? -- Best practice? -- Автоматизация? -- Архитектурное решение? -- Проектно-специфичное? -``` - -### **2. Выбор файла назначения:** -```bash -# По карте выше определить правильный файл -# Check существование файла -# Create если не существует -``` - -### **3. Структурированное добавление:** -```bash -# Для .cursor/rules/: -- Add метаdata (description, globs, alwaysApply) -- Структурировать по Categoryм -- Add cross-references - -# Для memory-bank/: -- Add дату и контекст -- Связать с существующими записями -- Update индексы -``` - -### **4. Validation:** -```bash -# Check: -- Нет дублирования -- Правильная структура -- Актуальные ссылки -- Соответствие стандартам -``` - -## 🚫 **Запрещенные действия:** - -### **НЕ размещать в `.cursor/rules/`:** -- ❌ Проектно-специфичные ошибки -- ❌ Текущий Status проекта -- ❌ История конкретных решений -- ❌ Планы развития проекта -- ❌ Результаты тестирования - -### **НЕ размещать в `memory-bank/`:** -- ❌ Универсальные правила -- ❌ Best practices для всех проектов -- ❌ Автоматизация и workflow -- ❌ Standards документации -- ❌ UI/UX Standards - -## 📊 **examples правильного размещения:** - -### **Пример 1: Error сборки TypeScript** -``` -Проблема: "Failed to resolve entry for package '@extension/vite-config'" -Решение: "Перевести пакет на ESM-only, main: dist/index..js" - -Размещение: memory-bank/errors.md -Причина: Проектно-специфичная Error сборки -``` - -### **Пример 2: Best practice для ESM пакетов** -``` -Практика: "Использовать только ESM-экспорт для внутренних пакетов" -Обоснование: "Упрощает сборку, устраняет ошибки Vite/esbuild" - -Размещение: .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -Причина: Универсальная практика для всех проектов -``` - -### **Пример 3: CI/CD автоматизация** -``` -Автоматизация: "GitHub Actions для проверки .cursor rules" -Workflow: "Verification структуры, метаданных, дубликатов" - -Размещение: .cursor/rules/workflow/ci-automation.mdc -Причина: Универсальная автоматизация -``` - -## ✅ **Результат:** - -Четкая карта назначения файлов обеспечивает: -- **Нет дублирования** - каждый Type контента в правильном месте -- **Нет рассинхронизации** - четкие критерии размещения -- **Легкий поиск** - структурированная организация -- **Эффективный перенос** - только универсальные правила в .cursor -- **Консистентность** - единообразные подходы -description: -globs: -alwaysApply: false ---- - -## 🛡️ Protected/Administrative Files - -Следующие файлы считаются административными и защищёнными — их нельзя удалять, перемещать или переименовывать автоматизацией (органайзерами, скриптами и т.д.): - -- memory-bank/chrome-extension-manual-restore.md — опыт ручного восстановления расширения, запрещено удалять/перемещать автоматизацией -- (добавляйте сюда другие важные административные файлы по мере необходимости) - ---- diff --git a/.cursor/rules/doc/internationalization-complete.mdc b/.cursor/rules/doc/internationalization-complete.mdc deleted file mode 100644 index fe05f562..00000000 --- a/.cursor/rules/doc/internationalization-complete.mdc +++ /dev/null @@ -1,162 +0,0 @@ -# Internationalization Complete - .cursor and memory-bank - -## Overview - -All `.cursor` and `memory-bank` directories have been completely translated to English for maximum international compatibility with AI assista.ts and LLMs. - -## What Was Translated - -### .cursor/rules Directory -- ✅ **ai-memory.mdc** - User Commands and AI instructions -- ✅ **ai-index.mdc** - AI-optimized rules index -- ✅ **README.mdc** - Main documentation -- ✅ **All subdirectories** - architecture/, dev/, doc/, plugin/, security/, ui/, workflow/ -- ✅ **cursor-export/** - All exported rules and standards - -### memory-bank Directory -- ✅ **README.md** - Main memory bank documentation -- ✅ **INDEX.md** - File structure and navigation -- ✅ **MEMORY_BANK_STRUCTURE.md** - Organization rules -- ✅ **All subdirectories** - core/, errors/, architecture/, development/, ui/, planning/, context/ -- ✅ **All individual files** - activeContext.md, progress.md, errors.md, etc. - -## Translation Approach - -### Universal Command Format -Commands now support both English and Russian: -```markdown -- `save context` / `save session context` - Save achieveme.ts, decisions and plans to memory-bank -- `update progress` / `update project progress` - Update activeContext.md and progress.md files with current status -``` - -### AI Semantic Understanding -- AI and LLMs understand Commands semantically regar.less of language -- English serves as the primary language for international compatibility -- Russian alternatives are preserved for user convenience - -## Benef.ts - -### For International Community -- 🌍 **100% English compatibility** - Any developer can use your .cursor -- 🤖 **AI/LLM optimized** - Commands work with any AI assistant -- 📚 **Standardized patterns** - Consistent Command structure -- 🔄 **Future-proof** - Ready for global sharing and collaboration - -### For You -- ✅ **Russian Commands still work** - No disruption to your workflow -- ✅ **Bilingual support** - Can use either language -- ✅ **AI understands both** - Semantic recognition works for both languages - -### For AI Assista.ts -- 🎯 **Clear Command patterns** - Predictable structure -- 🌐 **Language agnostic** - Understands meaning, not just words -- 📖 **Comprehensive documentation** - All rules in English - -## Technical Implementation - -### Translation Script -Created `.cursor/rules/translate-to-english..js` for automated translation: -```bash -node .cursor/rules/translate-to-english..js -``` - -### Translation Mappings -Comprehensive dictionary of Russian → English translations: -- Metadata descriptions -- Common phrases -- File content -- Navigation eleme.ts - -### Quality Assurance -- ✅ All files translated -- ✅ Structure preserved -- ✅ Functionality maintained -- ✅ Metadata updated - -## Usage examples - -### English Commands (Primary) -```bash -save context -update progress -restore context -analyze architecture -create plugin my-plugin -audit cursor -export cursor -``` - -### Russian Commands (Alternative) -```bash -сохрани контекст -обнови прогресс -восстанови контекст -анализируй архитектуру -создай плагин my-plugin -аудит cursor -экспорт cursor -``` - -## File Structure - -### .cursor/rules/ -``` -.cursor/rules/ -├── ai-memory.mdc # User Commands (English + Russian) -├── ai-index.mdc # Rules index (English) -├── README.mdc # Main documentation (English) -├── architecture/ # Architecture rules (English) -├── dev/ # Development principles (English) -├── doc/ # Documentation standards (English) -├── plugin/ # Plugin standards (English) -├── security/ # Security rules (English) -├── ui/ # UI/UX standards (English) -├── workflow/ # Workflow rules (English) -└── translate-to-english..js # Translation script -``` - -### memory-bank/ -``` -memory-bank/ -├── README.md # Main documentation (English) -├── INDEX.md # File structure (English) -├── MEMORY_BANK_STRUCTURE.md # Organization rules (English) -├── core/ # Core context files (English) -├── errors/ # Error documentation (English) -├── architecture/ # Architecture decisions (English) -├── development/ # Development process (English) -├── ui/ # UI/UX context (English) -├── planning/ # Planning docume.ts (English) -└── context/ # Contextual information (English) -``` - -## Next Steps - -### For Sharing -1. **GitHub Repository** - Ready for international community -2. **Documentation** - All in English for global understanding -3. **Commands** - Universal format for any AI assistant - -### For Development -1. **Continue using Russian Commands** - They still work perfectly -2. **Gradually adopt English** - For international collaboration -3. **Maintain bilingual support** - Best of both worlds - -### For AI Assista.ts -1. **Use English as primary** - For international compatibility -2. **Understand Russian alternatives** - For user convenience -3. **Maintain semantic understanding** - Language-agnostic processing - -## Conclusion - -The internationalization is complete and provIDEs: -- ✅ **Full English compatibility** for global community -- ✅ **Preserved Russian support** for your workflow -- ✅ **AI-optimized structure** for any AI assistant -- ✅ **Future-ready architecture** for international collaboration - -Your `.cursor` and `memory-bank` are now ready for the global AI development community! 🌍🤖 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/universal-commands.mdc b/.cursor/rules/doc/universal-commands.mdc deleted file mode 100644 index cd9cb14f..00000000 --- a/.cursor/rules/doc/universal-commands.mdc +++ /dev/null @@ -1,133 +0,0 @@ -# Universal Commands Approach - -## 🎯 **Цель: Универсальность для международного сообщества** - -### **Проблема:** -- Команды на русском языке ограничивают международное Usage -- Другие разработчики не могут использовать ваш .cursor -- Отсутствие стандартизации команд - -### **Решение:** -- **Английский как основной язык** команд -- **Русский как альтернатива** для удобства -- **AI понимает Seeсл** независимо от языка - -## 📋 **Формат универсальных команд:** - -### **Структура:** -``` -`english Command` / `русская команда` / `альтернативная русская команда` - English description -``` - -### **examples:** -```markdown -- `create memory entry` / `создай запись в memory-bank` - Create new entry with automatic categorization -- `update context` / `обнови контекст` - Update activeContext.md with current status -- `restore context` / `восстанови контекст` - Restore full context from memory-bank -``` - -## 🌍 **Преимущества универсального подхода:** - -### **1. Международная Compatibility:** -- ✅ Любой разработчик может использовать ваш .cursor -- ✅ Стандартизация команд для сообщества -- ✅ Легкость понимания и адаптации - -### **2. AI-Optimization:** -- ✅ AI понимает Seeсл независимо от языка -- ✅ Единообразные Patterns команд -- ✅ Предсказуемое поведение - -### **3. Долгосрочная перспектива:** -- ✅ Не нужно Translationить при публикации -- ✅ Compatibility с будущими версиями Cursor -- ✅ Масштабируемость для новых команд - -## 🔧 **Практическое применение:** - -### **Для вас (русскоязычный пользователь):** -```bash -# Вы можете использовать любую команду: -"создай запись в memory-bank" # Русская команда -"create memory entry" # Английская команда -``` - -### **Для международного сообщества:** -```bash -# Другие разработчики используют английские команды: -"create memory entry" -"update context" -"restore context" -``` - -### **Для AI:** -```bash -# AI понимает Seeсл и выполняет одинаково: -"создай запись в memory-bank" → create memory entry -"create memory entry" → create memory entry -``` - -## 📊 **Статистика совместимости:** - -### **Поддерживаемые языки:** -- ✅ **Английский** - основной язык команд -- ✅ **Русский** - альтернативный язык для удобства -- ✅ **Любой другой** - AI понимает по Seeслу - -### **Охват аудитории:** -- 🌍 **100% международное сообщество** - английские команды -- 🇷🇺 **Русскоязычные разработчики** - русские команды -- 🤖 **AI ассистенты** - понимают любой язык - -## 🚀 **Recommendations по использованию:** - -### **1. При создании новых команд:** -```markdown -# ✅ Правильно: -- `new Command` / `новая команда` - English description - -# ❌ Неправильно: -- `новая команда` - Russian description only -``` - -### **2. При документировании:** -```markdown -# ✅ Правильно: -## Commands for AI Assistant Recognition -- `create memory entry` / `создай запись в memory-bank` - Create new entry - -# ❌ Неправильно: -## Команды для распознавания AI-ассистентом -- `создай запись в memory-bank` - Create новую запись -``` - -### **3. При публикации:** -- Используйте английский как основной язык -- Сохраняйте русские альтернативы для удобства -- Добавляйте описания на английском - -## 📈 **Планы развития:** - -### **Краткосрочные цели:** -- ✅ Перевести все команды на универсальный формат -- ✅ Update документацию -- ✅ Протестировать с международным сообществом - -### **Долгосрочные цели:** -- 🌍 Стандартизация команд для Cursor сообщества -- 📚 Создание Libraries универсальных команд -- 🔄 Автоматическая миграция существующих .cursor - -## ✅ **Результат:** - -**Универсальный подход обеспечивает:** -- 🌍 **Международную Compatibility** - любой разработчик может использовать -- 🤖 **AI-оптимизацию** - понимание независимо от языка -- 📈 **Масштабируемость** - легко добавлять новые команды -- 🔄 **Обратную Compatibility** - ваши русские команды продолжают работать - -**Ваш .cursor теперь готов для международного сообщества!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/documentation-helper.cjs b/.cursor/rules/documentation-helper.cjs deleted file mode 100644 index b7eee130..00000000 --- a/.cursor/rules/documentation-helper.cjs +++ /dev/null @@ -1,472 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class DocumentationHelper { - constructor() { - this.rulesDir = path.join(__dirname); - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.documentationMapPath = path.join(this.rulesDir, 'doc', 'documentation-map.mdc'); - } - - async documentExperience(content, type = 'auto') { - console.log('📝 Documenting experience...\n'); - - // Определяем тип контента - const contentType = type === 'auto' ? this.analyzeContent(content) : type; - - // Определяем файл назначения - const targetFile = this.getTargetFile(contentType, content); - - // Создаем или обновляем файл - await this.addToFile(targetFile, content, contentType); - - console.log(`✅ Documented in: ${targetFile}`); - console.log(`📋 Content type: ${contentType}`); - } - - analyzeContent(content) { - const text = content.toLowerCase(); - - // Ошибки и решения - if (text.includes('ошибка') || text.includes('error') || text.includes('failed') || text.includes('решение')) { - if (text.includes('безопасность') || text.includes('security')) { - return 'security-error'; - } - if (text.includes('производительность') || text.includes('performance')) { - return 'performance-error'; - } - if (text.includes('ui') || text.includes('ux') || text.includes('интерфейс')) { - return 'ui-error'; - } - return 'general-error'; - } - - // Best practices - if (text.includes('best practice') || text.includes('практика') || text.includes('паттерн')) { - if (text.includes('разработка') || text.includes('development') || text.includes('принципы')) { - return 'dev-principles'; - } - if (text.includes('документация') || text.includes('documentation')) { - return 'doc-practice'; - } - if (text.includes('тестирование') || text.includes('testing')) { - return 'testing-practice'; - } - if (text.includes('ci') || text.includes('cd') || text.includes('автоматизация')) { - return 'ci-practice'; - } - if (text.includes('плагин') || text.includes('plugin')) { - return 'plugin-practice'; - } - if (text.includes('дата') || text.includes('время') || text.includes('date') || text.includes('time')) { - return 'date-patterns'; - } - if (text.includes('файл') || text.includes('структура') || text.includes('file') || text.includes('structure')) { - return 'file-patterns'; - } - return 'general-practice'; - } - - // Автоматизация - if (text.includes('автоматизация') || text.includes('automation') || text.includes('workflow')) { - if (text.includes('ci') || text.includes('cd')) { - return 'ci-automation'; - } - if (text.includes('сборка') || text.includes('build')) { - return 'build-automation'; - } - if (text.includes('тестирование') || text.includes('testing')) { - return 'testing-automation'; - } - if (text.includes('деплой') || text.includes('deploy')) { - return 'deploy-automation'; - } - return 'general-automation'; - } - - // Архитектурные решения - if (text.includes('архитектура') || text.includes('architecture') || text.includes('структура')) { - if (text.includes('безопасность') || text.includes('security')) { - return 'security-architecture'; - } - if (text.includes('производительность') || text.includes('performance')) { - return 'performance-architecture'; - } - if (text.includes('плагин') || text.includes('plugin')) { - return 'plugin-architecture'; - } - return 'general-architecture'; - } - - // Проектно-специфичное - if (text.includes('проект') || text.includes('текущий') || text.includes('статус')) { - return 'project-specific'; - } - - return 'general'; - } - - getTargetFile(contentType, content) { - const date = new Date().toISOString().split('T')[0]; - - switch (contentType) { - // Ошибки → memory-bank/errors.md - case 'general-error': - case 'build-error': - case 'runtime-error': - return path.join(this.memoryBankDir, 'errors.md'); - - // Безопасность → .cursor/rules/security/validation.mdc - case 'security-error': - return path.join(this.rulesDir, 'security', 'validation.mdc'); - - // Производительность → .cursor/rules/architecture/architecture-performance.mdc - case 'performance-error': - return path.join(this.rulesDir, 'architecture', 'architecture-performance.mdc'); - - // UI/UX → .cursor/rules/ui/ui-*.mdc - case 'ui-error': - return path.join(this.rulesDir, 'ui', 'ui-error-handling.mdc'); - - // Best practices → .cursor/rules/ - case 'dev-principles': - return path.join(this.rulesDir, 'dev', 'development-principles.mdc'); - case 'dev-practice': - return path.join(this.rulesDir, 'dev', 'development-guidelines.mdc'); - case 'doc-practice': - return path.join(this.rulesDir, 'doc', 'ai-first.mdc'); - case 'testing-practice': - return path.join(this.rulesDir, 'dev', 'testing-troubleshooting.mdc'); - case 'ci-practice': - return path.join(this.rulesDir, 'workflow', 'ci-automation.mdc'); - case 'plugin-practice': - return path.join(this.rulesDir, 'plugin', 'plugin-best-practices.mdc'); - case 'date-patterns': - return path.join(this.rulesDir, 'dev', 'date-time-patterns.mdc'); - case 'file-patterns': - return path.join(this.rulesDir, 'architecture', 'file-relationships.mdc'); - - // Автоматизация → .cursor/rules/workflow/ - case 'ci-automation': - return path.join(this.rulesDir, 'workflow', 'ci-automation.mdc'); - case 'build-automation': - return path.join(this.rulesDir, 'dev', 'typescript-build-troubleshooting.mdc'); - case 'testing-automation': - return path.join(this.rulesDir, 'dev', 'testing-troubleshooting.mdc'); - case 'deploy-automation': - return path.join(this.rulesDir, 'workflow', 'automation.mdc'); - - // Архитектура → .cursor/rules/architecture/ - case 'security-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-security.mdc'); - case 'performance-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-performance.mdc'); - case 'plugin-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-plugin.mdc'); - - // Проектно-специфичное → memory-bank/ - case 'project-specific': - return path.join(this.memoryBankDir, 'activeContext.md'); - - // По умолчанию → memory-bank/errors.md - default: - return path.join(this.memoryBankDir, 'errors.md'); - } - } - - async addToFile(targetFile, content, contentType) { - // Создаем директорию если не существует - const dir = path.dirname(targetFile); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - const date = new Date().toISOString(); - const timestamp = `[${date}]`; - - // Определяем формат файла - const isMdcFile = targetFile.endsWith('.mdc'); - const isMemoryBank = targetFile.includes('memory-bank'); - - if (isMdcFile && !isMemoryBank) { - // .cursor/rules/ файл - добавляем в структурированном виде - await this.addToMdcFile(targetFile, content, contentType, timestamp); - } else { - // memory-bank файл - добавляем в хронологическом порядке - await this.addToMemoryBankFile(targetFile, content, contentType, timestamp); - } - } - - async addToMdcFile(filePath, content, contentType, timestamp) { - let fileContent = ''; - - if (fs.existsSync(filePath)) { - fileContent = fs.readFileSync(filePath, 'utf8'); - } else { - // Создаем новый .mdc файл с метаданными - fileContent = this.createMdcTemplate(contentType); - } - - // Добавляем контент в соответствующую секцию - const newSection = `\n## ${this.getSectionTitle(contentType)} (${timestamp})\n\n${content}\n`; - - // Ищем место для вставки (перед последней секцией) - const lines = fileContent.split('\n'); - const insertIndex = lines.length - 2; // Перед последней строкой - - lines.splice(insertIndex, 0, newSection); - - fs.writeFileSync(filePath, lines.join('\n')); - } - - async addToMemoryBankFile(filePath, content, contentType, timestamp) { - let fileContent = ''; - - if (fs.existsSync(filePath)) { - fileContent = fs.readFileSync(filePath, 'utf8'); - } - - // Добавляем в начало файла (новые записи сверху) - const newEntry = `\n## ${timestamp} - ${this.getEntryTitle(contentType)}\n\n${content}\n\n---\n`; - - fileContent = newEntry + fileContent; - - fs.writeFileSync(filePath, fileContent); - } - - createMdcTemplate(contentType) { - const description = this.getDescription(contentType); - const category = this.getCategory(contentType); - - return `--- -description: ${description} -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: ${category} ---- - -# ${this.getTitle(contentType)} - -${this.getInitialContent(contentType)} -`; - } - - getSectionTitle(contentType) { - const titles = { - 'security-error': 'Security Issue', - 'performance-error': 'Performance Issue', - 'ui-error': 'UI/UX Issue', - 'dev-practice': 'Development Practice', - 'doc-practice': 'Documentation Practice', - 'testing-practice': 'Testing Practice', - 'ci-practice': 'CI/CD Practice', - 'plugin-practice': 'Plugin Practice', - 'ci-automation': 'CI/CD Automation', - 'build-automation': 'Build Automation', - 'testing-automation': 'Testing Automation', - 'deploy-automation': 'Deployment Automation', - 'security-architecture': 'Security Architecture', - 'performance-architecture': 'Performance Architecture', - 'plugin-architecture': 'Plugin Architecture' - }; - - return titles[contentType] || 'New Entry'; - } - - getEntryTitle(contentType) { - const titles = { - 'general-error': 'General Error', - 'build-error': 'Build Error', - 'runtime-error': 'Runtime Error', - 'project-specific': 'Project Update' - }; - - return titles[contentType] || 'New Entry'; - } - - getDescription(contentType) { - const descriptions = { - 'security-error': 'Security validation and error handling', - 'performance-error': 'Performance optimization and monitoring', - 'ui-error': 'UI/UX error handling and best practices', - 'dev-practice': 'Development best practices and guidelines', - 'doc-practice': 'Documentation standards and practices', - 'testing-practice': 'Testing strategies and troubleshooting', - 'ci-practice': 'CI/CD practices and automation', - 'plugin-practice': 'Plugin development best practices', - 'ci-automation': 'CI/CD automation and workflows', - 'build-automation': 'Build automation and optimization', - 'testing-automation': 'Testing automation and tools', - 'deploy-automation': 'Deployment automation and processes', - 'security-architecture': 'Security architecture principles', - 'performance-architecture': 'Performance architecture guidelines', - 'plugin-architecture': 'Plugin architecture patterns' - }; - - return descriptions[contentType] || 'General guidelines and best practices'; - } - - getCategory(contentType) { - if (contentType.includes('error')) return 'error-handling'; - if (contentType.includes('practice')) return 'development-practices'; - if (contentType.includes('automation')) return 'process-management'; - if (contentType.includes('architecture')) return 'system-design'; - return 'general'; - } - - getTitle(contentType) { - const titles = { - 'security-error': 'Security Validation', - 'performance-error': 'Performance Guidelines', - 'ui-error': 'UI Error Handling', - 'dev-practice': 'Development Guidelines', - 'doc-practice': 'Documentation Standards', - 'testing-practice': 'Testing & Troubleshooting', - 'ci-practice': 'CI/CD Practices', - 'plugin-practice': 'Plugin Best Practices', - 'ci-automation': 'CI/CD Automation', - 'build-automation': 'Build Automation', - 'testing-automation': 'Testing Automation', - 'deploy-automation': 'Deployment Automation', - 'security-architecture': 'Security Architecture', - 'performance-architecture': 'Performance Architecture', - 'plugin-architecture': 'Plugin Architecture' - }; - - return titles[contentType] || 'General Guidelines'; - } - - getInitialContent(contentType) { - return `## Overview - -This file contains ${contentType.replace('-', ' ')} guidelines and best practices. - -## Guidelines - - -`; - } -} - -// Memory Bank Management -const memoryCommands = { - 'memory-add': 'Добавить запись в memory-bank', - 'memory-update': 'Обновить запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank', - 'memory-structure': 'Создать структуру memory-bank', - 'memory-report': 'Генерировать отчет по memory-bank' -}; - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - if (args.length === 0) { - console.log('Usage: node documentation-helper.cjs [type]'); - console.log('Types: auto, error, practice, automation, architecture, project'); - console.log('\nMemory Bank Commands:'); - console.log(' memory-add [options] - Add entry to memory-bank'); - console.log(' memory-update - Update active context'); - console.log(' memory-restore [options] - Restore context from memory-bank'); - console.log(' memory-search [options] - Search in memory-bank'); - console.log(' memory-audit - Audit memory-bank structure'); - console.log(' memory-structure - Create memory-bank structure'); - console.log(' memory-report [type] - Generate memory-bank report'); - process.exit(1); - } - - const command = args[0]; - - // Обработка memory-bank команд - if (command.startsWith('memory-')) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - switch (command) { - case 'memory-add': - const content = args[1]; - const options = parseOptions(args.slice(2)); - await manager.addEntry(content, options); - break; - case 'memory-update': - const contextData = parseContextData(args.slice(1)); - await manager.updateContext(contextData); - break; - case 'memory-restore': - const restoreOptions = parseOptions(args.slice(1)); - const context = await manager.restoreContext(restoreOptions); - console.log(JSON.stringify(context, null, 2)); - break; - case 'memory-search': - const query = args[1]; - const searchOptions = parseOptions(args.slice(2)); - const results = await manager.searchMemory(query, searchOptions); - console.log(JSON.stringify(results, null, 2)); - break; - case 'memory-audit': - const MemoryBankAuditor = require('./memory-bank-auditor.cjs'); - const auditor = new MemoryBankAuditor(); - await auditor.audit(); - break; - case 'memory-structure': - const projectType = args[1] || 'react-typescript'; - const MemoryBankStructureCreator = require('./memory-bank-structure-creator.cjs'); - const creator = new MemoryBankStructureCreator(); - await creator.createStructure(projectType); - break; - case 'memory-report': - const reportOptions = parseOptions(args.slice(1)); - const report = await manager.generateReport(reportOptions); - console.log(JSON.stringify(report, null, 2)); - break; - default: - console.log(`Unknown memory command: ${command}`); - process.exit(1); - } - return; - } - - // Стандартная обработка документации - const content = args[0]; - const type = args[1] || 'auto'; - - const helper = new DocumentationHelper(); - await helper.documentExperience(content, type); -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } - } - - return options; -} - -function parseContextData(args) { - const contextData = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - contextData[key] = value; - } - } - - return contextData; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = DocumentationHelper; \ No newline at end of file diff --git a/.cursor/rules/environment.yaml b/.cursor/rules/environment.yaml deleted file mode 100644 index b7cb37c6..00000000 --- a/.cursor/rules/environment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -os: Manjaro Linux (Plasma) -os_version: 6.12.37-1-MANJARO -os_arch: x64 -cursor: - version: 1.2.4 - vscode_version: 1.99.3 - commit: a8e95743c5268be73767c46944a71f4465d05c90 - build_date: 2025-07-10T16:59:43.242Z - electron: 34.5.1 - chromium: 132.0.6834.210 - node: 20.19.0 - v8: 13.2.152.41-electron.0 -browser: - main: Chrome - main_version: 138.0.7204.100 - install: Flatpak - test: Chromium - test_version: 138.0.7204.100 - test_install: official repo (external), required for test automation -editor: Cursor -chrome_launch: Flatpak, custom args possible -debug_tools: - - Cursor - - Debugger for Chrome - - Composer Web -preferred_doc_lang: en -devtools_note: F12 only for main page, inspect sidepanel via context menu -extension_tab_name: Agent Platform Tools -ai_file_access: true # AI assistant has full access to all project files -cli_tools: - - git - - gh (GitHub CLI) - # Add more CLI tools as needed \ No newline at end of file diff --git a/.cursor/rules/export-cursor.cjs b/.cursor/rules/export-cursor.cjs deleted file mode 100644 index 51d326de..00000000 --- a/.cursor/rules/export-cursor.cjs +++ /dev/null @@ -1,303 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorExporter { - constructor() { - this.rulesDir = path.join(__dirname); - this.exportDir = path.join(process.cwd(), 'cursor-export'); - } - - async export(targetProject = null) { - console.log('📦 Exporting .cursor rules...\n'); - - // Создаем папку для экспорта - if (!fs.existsSync(this.exportDir)) { - fs.mkdirSync(this.exportDir, { recursive: true }); - } - - // Экспортируем основные файлы - await this.exportCoreFiles(); - - // Экспортируем правила по категориям - await this.exportRulesByCategory(); - - // Создаем инструкции по импорту - await this.createImportInstructions(); - - // Создаем скрипт импорта - await this.createImportScript(); - - console.log('✅ Export completed!'); - console.log(`📁 Exported to: ${this.exportDir}`); - - if (targetProject) { - console.log(`\n🚀 To import to ${targetProject}:`); - console.log(` cd ${targetProject}`); - console.log(` node cursor-export/import-cursor.cjs`); - } - } - - async exportCoreFiles() { - console.log('📋 Exporting core files...'); - - const coreFiles = [ - 'ai-memory.mdc', - 'environment.mdc', - 'index.mdc', - 'README.mdc', - 'ai-optimization.mdc', - 'ai-index.mdc', - 'monorepo-best-practices.mdc', - 'typescript-build-troubleshooting.mdc' - ]; - - for (const file of coreFiles) { - const sourcePath = path.join(this.rulesDir, file); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.exportDir, file); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${file}`); - } - } - } - - async exportRulesByCategory() { - console.log('📁 Exporting rules by category...'); - - const categories = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const category of categories) { - const categoryDir = path.join(this.rulesDir, category); - if (fs.existsSync(categoryDir)) { - const targetCategoryDir = path.join(this.exportDir, category); - fs.mkdirSync(targetCategoryDir, { recursive: true }); - - const files = fs.readdirSync(categoryDir); - for (const file of files) { - if (file.endsWith('.mdc')) { - const sourcePath = path.join(categoryDir, file); - const targetPath = path.join(targetCategoryDir, file); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${category}/${file}`); - } - } - } - } - } - - async createImportInstructions() { - console.log('📝 Creating import instructions...'); - - const instructions = `# Import .cursor Rules - -## Quick Import - -1. Copy the entire \`cursor-export\` folder to your target project -2. Run the import script: - \`\`\`bash - node cursor-export/import-cursor.cjs - \`\`\` - -## Manual Import - -1. Create \`.cursor/rules/\` directory in your target project -2. Copy files from \`cursor-export/\` to \`.cursor/rules/\` -3. Run audit and optimization: - \`\`\`bash - cd .cursor/rules - node cursor-manager.cjs full - \`\`\` - -## What's Included - -### Core Rules -- AI memory and commands -- Environment constraints -- Project structure guidelines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guidelines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update \`environment.mdc\` with your project constraints -2. Modify \`ai-memory.mdc\` with project-specific commands -3. Adjust \`monorepo-best-practices.mdc\` for your structure -4. Run \`node cursor-manager.cjs optimize\` to apply changes - -## Verification - -Verify successful import: -\`\`\`bash -cd .cursor/rules -node cursor-manager.cjs status -\`\`\` - -All files should show as "AI-ready" with no issues. -`; - - const instructionsPath = path.join(this.exportDir, 'IMPORT-INSTRUCTIONS.md'); - fs.writeFileSync(instructionsPath, instructions); - console.log(' ✅ IMPORT-INSTRUCTIONS.md'); - } - - async createImportScript() { - console.log('🔧 Creating import script...'); - - const importScript = `#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorImporter { - constructor() { - this.exportDir = __dirname; - this.targetRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async import() { - console.log('📥 Importing .cursor rules...\\n'); - - // Создаем папку .cursor/rules если не существует - if (!fs.existsSync(this.targetRulesDir)) { - fs.mkdirSync(this.targetRulesDir, { recursive: true }); - console.log('✅ Created .cursor/rules directory'); - } - - // Копируем все файлы - await this.copyFiles(); - - // Копируем скрипты автоматизации - await this.copyAutomationScripts(); - - // Создаем базовую структуру если нужно - await this.createBasicStructure(); - - console.log('\\n✅ Import completed!'); - console.log('\\n🔧 Next steps:'); - console.log(' 1. Customize environment.mdc for your project'); - console.log(' 2. Update ai-memory.mdc with project-specific commands'); - console.log(' 3. Run: cd .cursor/rules && node cursor-manager.cjs full'); - } - - async copyFiles() { - console.log('📋 Copying rule files...'); - - const items = fs.readdirSync(this.exportDir); - - for (const item of items) { - const sourcePath = path.join(this.exportDir, item); - const targetPath = path.join(this.targetRulesDir, item); - - const stat = fs.statSync(sourcePath); - - if (stat.isDirectory()) { - // Копируем директории - if (!fs.existsSync(targetPath)) { - fs.mkdirSync(targetPath, { recursive: true }); - } - - const files = fs.readdirSync(sourcePath); - for (const file of files) { - if (file.endsWith('.mdc')) { - const fileSource = path.join(sourcePath, file); - const fileTarget = path.join(targetPath, file); - fs.copyFileSync(fileSource, fileTarget); - console.log(\` ✅ \${item}/\${file}\`); - } - } - } else if (item.endsWith('.mdc')) { - // Копируем .mdc файлы - fs.copyFileSync(sourcePath, targetPath); - console.log(\` ✅ \${item}\`); - } - } - } - - async copyAutomationScripts() { - console.log('🔧 Copying automation scripts...'); - - const scripts = [ - 'audit-cursor.cjs', - 'fix-cursor.cjs', - 'optimize-for-ai.cjs', - 'cursor-manager.cjs' - ]; - - for (const script of scripts) { - const sourcePath = path.join(this.exportDir, script); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.targetRulesDir, script); - fs.copyFileSync(sourcePath, targetPath); - console.log(\` ✅ \${script}\`); - } - } - } - - async createBasicStructure() { - console.log('🏗️ Creating basic structure...'); - - const requiredDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const dir of requiredDirs) { - const dirPath = path.join(this.targetRulesDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(\` ✅ Created \${dir}/\`); - } - } - } -} - -// Запуск импорта -async function main() { - const importer = new CursorImporter(); - await importer.import(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorImporter; -`; - - const importScriptPath = path.join(this.exportDir, 'import-cursor.cjs'); - fs.writeFileSync(importScriptPath, importScript); - - // Делаем скрипт исполняемым - fs.chmodSync(importScriptPath, '755'); - console.log(' ✅ import-cursor.cjs'); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const targetProject = args[0] || null; - - const exporter = new CursorExporter(); - await exporter.export(targetProject); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorExporter; \ No newline at end of file diff --git a/.cursor/rules/fix-cursor.cjs b/.cursor/rules/fix-cursor.cjs deleted file mode 100644 index c619901f..00000000 --- a/.cursor/rules/fix-cursor.cjs +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); -const CursorAuditor = require('./audit-cursor.cjs'); - -class CursorFixer { - constructor() { - this.rulesDir = path.join(__dirname); - this.auditor = new CursorAuditor(); - } - - async fix() { - console.log('🔧 Starting automatic .cursor fixes...\n'); - - // Сначала проводим аудит - const stats = await this.auditor.audit(); - - console.log('\n🔧 Applying fixes...\n'); - - // Исправляем метаданные - if (stats.invalidMetadata.length > 0 || stats.filesWithoutMetadata > 0) { - await this.fixMetadata(); - } - - // Удаляем дубликаты - if (stats.duplicateContent.length > 0) { - await this.removeDuplicates(stats.duplicateContent); - } - - // Исправляем ссылки - if (stats.brokenLinks.length > 0) { - await this.fixLinks(stats.brokenLinks); - } - - // Обновляем индекс - await this.updateIndex(); - - console.log('\n✅ All fixes applied!'); - } - - async fixMetadata() { - console.log('📋 Fixing metadata...'); - - const mdcFiles = this.auditor.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - if (!content.startsWith('---')) { - // Добавляем метаданные - const fileName = path.basename(file, '.mdc'); - const metadata = this.generateMetadata(fileName, file); - - const newContent = metadata + content; - fs.writeFileSync(file, newContent); - console.log(` ✅ Added metadata to ${path.relative(this.rulesDir, file)}`); - } else { - // Проверяем и исправляем существующие метаданные - const fixedContent = this.fixExistingMetadata(content, file); - if (fixedContent !== content) { - fs.writeFileSync(file, fixedContent); - console.log(` ✅ Fixed metadata in ${path.relative(this.rulesDir, file)}`); - } - } - } - } - - generateMetadata(fileName, filePath) { - const dirName = path.basename(path.dirname(filePath)); - - let description = ''; - let globs = '["**/*"]'; - let alwaysApply = 'false'; - - // Определяем описание на основе имени файла и директории - if (fileName.includes('principle')) { - description = `Development principle ${fileName.split('-').pop()} - ${fileName.replace(/-/g, ' ')}`; - alwaysApply = 'true'; - } else if (fileName.includes('architecture')) { - description = `Architecture rule for ${fileName.replace('architecture-', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('ui-')) { - description = `UI standard for ${fileName.replace('ui-', '')}`; - globs = '["pages/**/*", "**/*.tsx", "**/*.css"]'; - } else if (fileName.includes('plugin')) { - description = `Plugin standard for ${fileName.replace('plugin-', '')}`; - globs = '["plugins/**/*", "**/*.py", "**/*.json"]'; - } else if (fileName.includes('workflow')) { - description = `Workflow rule for ${fileName.replace('workflow', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('security')) { - description = `Security rule for ${fileName.replace('security', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('validation')) { - description = 'Input validation and data sanitization rules'; - alwaysApply = 'true'; - } else if (fileName.includes('testing')) { - description = 'Testing and debugging standards'; - globs = '["tests/**/*", "**/*.test.*", "**/*.spec.*"]'; - } else if (fileName.includes('typescript')) { - description = 'TypeScript-specific guidelines'; - globs = '["**/*.ts", "**/*.tsx", "**/*.json"]'; - } else if (fileName.includes('development')) { - description = 'General development guidelines'; - alwaysApply = 'true'; - } else if (fileName.includes('automation')) { - description = 'Automation and synchronization rules'; - alwaysApply = 'true'; - } else if (fileName.includes('branches')) { - description = 'Git branch management rules'; - alwaysApply = 'true'; - } else if (fileName.includes('knowledge')) { - description = 'Project knowledge structure'; - alwaysApply = 'true'; - } else if (fileName.includes('memorybank')) { - description = 'Quality standards for memory-bank'; - alwaysApply = 'true'; - } else if (fileName.includes('restore')) { - description = 'Context restoration procedures'; - alwaysApply = 'true'; - } else if (fileName.includes('ai-first')) { - description = 'AI-oriented documentation standards'; - globs = '["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"]'; - alwaysApply = 'true'; - } else if (fileName.includes('ai-fallback')) { - description = 'Fallback procedures for AI'; - alwaysApply = 'true'; - } else if (fileName.includes('mdc-file-standards')) { - description = 'Standards for .mdc file creation'; - alwaysApply = 'true'; - } else if (fileName === 'index') { - description = 'Rules index and navigation'; - alwaysApply = 'true'; - } else if (fileName === 'README') { - description = 'Main documentation and structure'; - alwaysApply = 'true'; - } else if (fileName === 'ai-memory') { - description = 'User commands and AI instructions'; - alwaysApply = 'true'; - } else if (fileName === 'environment') { - description = 'Critical environment limitations'; - alwaysApply = 'true'; - } else if (fileName === 'monorepo-best-practices') { - description = 'Monorepo structure and guidelines'; - alwaysApply = 'true'; - } else if (fileName === 'typescript-build-troubleshooting') { - description = 'TypeScript build error solutions'; - alwaysApply = 'true'; - } else { - description = `${fileName.replace(/-/g, ' ')} rule`; - } - - return `--- -description: ${description} -globs: ${globs} -alwaysApply: ${alwaysApply} ---- - -`; - } - - fixExistingMetadata(content, filePath) { - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (!metadataMatch) return content; - - const metadata = metadataMatch[1]; - const fileName = path.basename(filePath, '.mdc'); - - let newMetadata = metadata; - let hasChanges = false; - - // Проверяем и добавляем недостающие поля - if (!metadata.includes('description:')) { - const description = this.generateMetadata(fileName, filePath).match(/description: (.*)/)[1]; - newMetadata = `description: ${description}\n${newMetadata}`; - hasChanges = true; - } - - if (!metadata.includes('globs:')) { - newMetadata = `${newMetadata}globs: ["**/*"]\n`; - hasChanges = true; - } - - if (!metadata.includes('alwaysApply:')) { - newMetadata = `${newMetadata}alwaysApply: false\n`; - hasChanges = true; - } - - if (hasChanges) { - return content.replace(metadataMatch[0], `---\n${newMetadata}---\n\n`); - } - - return content; - } - - async removeDuplicates(duplicates) { - console.log('🔄 Removing duplicates...'); - - for (const dup of duplicates) { - const duplicatePath = path.join(this.rulesDir, dup.duplicate); - - if (fs.existsSync(duplicatePath)) { - fs.unlinkSync(duplicatePath); - console.log(` ✅ Removed duplicate: ${dup.duplicate}`); - } - } - } - - async fixLinks(brokenLinks) { - console.log('🔗 Fixing broken links...'); - - // Группируем по файлам - const linksByFile = {}; - for (const link of brokenLinks) { - if (!linksByFile[link.file]) { - linksByFile[link.file] = []; - } - linksByFile[link.file].push(link.link); - } - - for (const [file, links] of Object.entries(linksByFile)) { - const filePath = path.join(this.rulesDir, file); - let content = fs.readFileSync(filePath, 'utf8'); - - for (const link of links) { - // Пытаемся найти правильную ссылку - const fixedLink = this.findCorrectLink(link); - if (fixedLink) { - content = content.replace(new RegExp(`\\(${link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`, 'g'), `(${fixedLink})`); - console.log(` ✅ Fixed link in ${file}: ${link} → ${fixedLink}`); - } - } - - fs.writeFileSync(filePath, content); - } - } - - findCorrectLink(brokenLink) { - // Простые исправления для типичных случаев - const fixes = { - 'doc/user-commands.mdc': 'ai-memory.mdc', - 'dev/dev-principle-02-ai-first-docs.mdc': 'doc/ai-first.mdc', - 'ui/accessibility.mdc': 'ui/ui-accessibility.mdc' - }; - - return fixes[brokenLink] || null; - } - - async updateIndex() { - console.log('📝 Updating index...'); - - // Генерируем новый индекс на основе существующих файлов - const indexContent = this.generateIndex(); - const indexPath = path.join(this.rulesDir, 'index.mdc'); - - fs.writeFileSync(indexPath, indexContent); - console.log(' ✅ Updated index.mdc'); - } - - generateIndex() { - const files = this.auditor.getAllFiles(this.rulesDir); - const structure = {}; - - // Группируем файлы по директориям - for (const file of files) { - const relativePath = path.relative(this.rulesDir, file); - const dir = path.dirname(relativePath); - const name = path.basename(file, path.extname(file)); - - if (!structure[dir]) { - structure[dir] = []; - } - - structure[dir].push({ - name: name, - path: relativePath, - ext: path.extname(file) - }); - } - - // Генерируем содержимое индекса - let content = `--- -description: Rules index and navigation -globs: ["**/*"] -alwaysApply: true ---- - -# Index of Modular Rules - -`; - - // Корневые файлы - if (structure['.']) { - content += '## root\n'; - for (const file of structure['.'].filter(f => f.ext === '.mdc')) { - const description = this.getFileDescription(file.name); - content += `- [${this.formatName(file.name)}](${file.path}) — ${description}\n`; - } - content += '\n'; - } - - // Директории - const dirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - for (const dir of dirs) { - if (structure[dir] && structure[dir].length > 0) { - content += `## ${dir}\n`; - for (const file of structure[dir].filter(f => f.ext === '.mdc')) { - const description = this.getFileDescription(file.name); - content += `- [${this.formatName(file.name)}](${file.path}) — ${description}\n`; - } - content += '\n'; - } - } - - return content; - } - - getFileDescription(fileName) { - const descriptions = { - 'ai-memory': 'User commands and AI instructions', - 'environment': 'Critical environment limitations', - 'monorepo-best-practices': 'Monorepo structure and guidelines', - 'typescript-build-troubleshooting': 'TypeScript build error solutions', - 'README': 'Main documentation and structure', - 'index': 'Rules index and navigation' - }; - - return descriptions[fileName] || `${fileName.replace(/-/g, ' ')} rule`; - } - - formatName(fileName) { - return fileName - .split('-') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - } -} - -// Запуск исправлений -async function main() { - const fixer = new CursorFixer(); - await fixer.fix(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorFixer; \ No newline at end of file diff --git a/.cursor/rules/generate-rules-index.cjs b/.cursor/rules/generate-rules-index.cjs deleted file mode 100644 index 6bc45760..00000000 --- a/.cursor/rules/generate-rules-index.cjs +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env node -const fs = require('fs'); -const path = require('path'); - -const RULES_DIR = path.join(__dirname); -const INDEX_FILE = path.join(RULES_DIR, 'index.mdc'); -const README_FILE = path.join(RULES_DIR, 'README.md'); - -function getRuleFiles(dir = RULES_DIR, rel = '') { - let files = []; - for (const f of fs.readdirSync(dir)) { - const abs = path.join(dir, f); - const relPath = rel ? path.join(rel, f) : f; - if (fs.statSync(abs).isDirectory()) { - files = files.concat(getRuleFiles(abs, relPath)); - } else if (f.endsWith('.mdc') && !['index.mdc', 'generate-rules-index.js'].includes(f)) { - files.push(relPath); - } - } - return files; -} - -function extractMeta(filePath) { - const content = fs.readFileSync(path.join(RULES_DIR, filePath), 'utf8'); - const lines = content.split('\n'); - const heading = lines.find(l => l.startsWith('#')) || ''; - const descLine = lines.find(l => l.startsWith('description:')) || ''; - const description = descLine.replace('description:', '').trim(); - return { heading: heading.replace(/^#+\s*/, ''), description }; -} - -function generateIndex(ruleFiles) { - let out = '# Index of Modular Rules\n\n'; - // Group by top-level folder - const groups = {}; - for (const f of ruleFiles) { - const parts = f.split(path.sep); - const group = parts.length > 1 ? parts[0] : 'root'; - if (!groups[group]) groups[group] = []; - groups[group].push(f); - } - for (const group of Object.keys(groups).sort()) { - if (group !== 'root') out += `## ${group}\n`; - groups[group].sort().forEach(f => { - const meta = extractMeta(f); - out += `- [${meta.heading || f}](${f})${meta.description ? ' — ' + meta.description : ''}\n`; - }); - out += '\n'; - } - out += 'description:\nglobs:\nalwaysApply: false\n---\n'; - return out; -} - -function updateReadmeStructure(ruleFiles) { - // Build tree structure - function tree(files) { - const t = {}; - for (const f of files) { - const parts = f.split(path.sep); - let cur = t; - for (let i = 0; i < parts.length; ++i) { - if (!cur[parts[i]]) cur[parts[i]] = (i === parts.length - 1) ? null : {}; - cur = cur[parts[i]]; - } - } - return t; - } - function printTree(t, indent = ' ') { - let out = []; - for (const k of Object.keys(t).sort()) { - out.push(indent + k + (t[k] ? '/' : '')); - if (t[k]) out.push(printTree(t[k], indent + ' ')); - } - return out.join('\n'); - } - const t = tree(ruleFiles); - const structure = [ - '```', - '.cursor/', - ' rules/', - printTree(t), - '```', - '' - ].join('\n'); - let readme = fs.readFileSync(README_FILE, 'utf8'); - readme = readme.replace(/```[\s\S]*?```/, structure); - fs.writeFileSync(README_FILE, readme, 'utf8'); -} - -function main() { - const ruleFiles = getRuleFiles(); - const indexContent = generateIndex(ruleFiles); - fs.writeFileSync(INDEX_FILE, indexContent, 'utf8'); - updateReadmeStructure(ruleFiles); - console.log('index.mdc and README.md structure updated.'); -} - -if (require.main === module) { - main(); -} \ No newline at end of file diff --git a/.cursor/rules/memory-bank-auditor.cjs b/.cursor/rules/memory-bank-auditor.cjs deleted file mode 100644 index 1bb7380b..00000000 --- a/.cursor/rules/memory-bank-auditor.cjs +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankAuditor { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.cursorRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async audit() { - console.log('🔍 Auditing Memory Bank for files that should be in .cursor/rules/...\n'); - - const files = await this.getAllFiles(); - const analysis = await this.analyzeFiles(files); - - this.printReport(analysis); - - return analysis; - } - - async getAllFiles() { - const files = []; - - const walkDir = (dir) => { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - walkDir(fullPath); - } else if (item.endsWith('.md')) { - files.push({ - path: fullPath, - relativePath: path.relative(this.memoryBankDir, fullPath), - name: item, - category: path.dirname(path.relative(this.memoryBankDir, fullPath)) - }); - } - } - }; - - walkDir(this.memoryBankDir); - return files; - } - - async analyzeFiles(files) { - const analysis = { - shouldMoveToCursor: [], - shouldStayInMemoryBank: [], - unclear: [], - totalFiles: files.length - }; - - for (const file of files) { - const content = fs.readFileSync(file.path, 'utf8'); - const decision = this.analyzeFile(file, content); - - switch (decision.category) { - case 'cursor': - analysis.shouldMoveToCursor.push({ ...file, reason: decision.reason }); - break; - case 'memory-bank': - analysis.shouldStayInMemoryBank.push({ ...file, reason: decision.reason }); - break; - case 'unclear': - analysis.unclear.push({ ...file, reason: decision.reason }); - break; - } - } - - return analysis; - } - - analyzeFile(file, content) { - const text = content.toLowerCase(); - const name = file.name.toLowerCase(); - - // Критерии для .cursor/rules/ - const cursorIndicators = [ - // Универсальные принципы и правила - 'принципы разработки', 'development principles', 'best practices', - 'паттерны', 'patterns', 'стандарты', 'standards', - 'правила', 'rules', 'guidelines', 'руководства', - - // Автоматизация и workflow - 'автоматизация', 'automation', 'workflow', 'процессы', - 'ci/cd', 'continuous integration', 'deployment', - - // Документация и структуры - 'документация', 'documentation', 'структура', 'structure', - 'форматы', 'formats', 'шаблоны', 'templates', - - // UI/UX стандарты - 'ui/ux', 'user interface', 'user experience', 'интерфейс', - 'стили', 'styles', 'компоненты', 'components', - - // Безопасность - 'безопасность', 'security', 'валидация', 'validation', - - // Технические паттерны - 'паттерны работы', 'date patterns', 'time patterns', - 'file relationships', 'взаимосвязи файлов' - ]; - - // Критерии для memory-bank/ - const memoryBankIndicators = [ - // Проектно-специфичное - 'agent-plugins-platform', 'текущий проект', 'current project', - 'статус проекта', 'project status', 'прогресс', 'progress', - - // Конкретные ошибки и решения - 'ошибка', 'error', 'баг', 'bug', 'решение', 'solution', - 'vite-react19', 'typescript-build', 'конкретная проблема', - - // Архитектурные решения проекта - 'архитектура проекта', 'project architecture', 'решения проекта', - 'принятые решения', 'decisions made', - - // Результаты тестирования - 'результаты тестов', 'test results', 'отладка', 'debugging', - - // Планы развития - 'планы развития', 'development plans', 'roadmap', 'roadmap проекта', - - // Контекст проекта - 'контекст проекта', 'project context', 'окружение', 'environment' - ]; - - // Проверяем индикаторы - const cursorScore = cursorIndicators.filter(indicator => - text.includes(indicator) || name.includes(indicator) - ).length; - - const memoryBankScore = memoryBankIndicators.filter(indicator => - text.includes(indicator) || name.includes(indicator) - ).length; - - // Специальные случаи - if (name.includes('readme') || name.includes('index')) { - return { category: 'memory-bank', reason: 'README и INDEX файлы должны оставаться в memory-bank' }; - } - - if (name.includes('development-principles') || text.includes('принципы разработки')) { - return { category: 'cursor', reason: 'Универсальные принципы разработки применимы к любым проектам' }; - } - - if (name.includes('file-relationships') || text.includes('взаимосвязи файлов')) { - return { category: 'cursor', reason: 'Паттерны организации файлов универсальны' }; - } - - if (name.includes('date-time-patterns') || text.includes('паттерны работы с датами')) { - return { category: 'cursor', reason: 'Универсальные паттерны работы с датами и временем' }; - } - - if (name.includes('errors') || name.includes('error') || name.includes('graveyard')) { - return { category: 'memory-bank', reason: 'Проектно-специфичные ошибки и решения' }; - } - - if (name.includes('architecture') || name.includes('systempattern')) { - return { category: 'memory-bank', reason: 'Архитектурные решения конкретного проекта' }; - } - - if (name.includes('testing') || name.includes('debug')) { - return { category: 'memory-bank', reason: 'Результаты тестирования и отладки проекта' }; - } - - if (name.includes('progress') || name.includes('activecontext')) { - return { category: 'memory-bank', reason: 'Текущий статус и прогресс проекта' }; - } - - // Принимаем решение на основе баллов - if (cursorScore > memoryBankScore && cursorScore > 0) { - return { - category: 'cursor', - reason: `Найдено ${cursorScore} индикаторов для .cursor/rules/ (vs ${memoryBankScore} для memory-bank/)` - }; - } else if (memoryBankScore > cursorScore && memoryBankScore > 0) { - return { - category: 'memory-bank', - reason: `Найдено ${memoryBankScore} индикаторов для memory-bank/ (vs ${cursorScore} для .cursor/rules/)` - }; - } else { - return { - category: 'unclear', - reason: `Неясно: cursorScore=${cursorScore}, memoryBankScore=${memoryBankScore}` - }; - } - } - - printReport(analysis) { - console.log('📊 MEMORY BANK AUDIT REPORT'); - console.log('='.repeat(60)); - - console.log(`📁 Total files analyzed: ${analysis.totalFiles}`); - console.log(`🔄 Should move to .cursor/rules/: ${analysis.shouldMoveToCursor.length}`); - console.log(`🧠 Should stay in memory-bank/: ${analysis.shouldStayInMemoryBank.length}`); - console.log(`❓ Unclear: ${analysis.unclear.length}`); - - if (analysis.shouldMoveToCursor.length > 0) { - console.log('\n🔄 FILES THAT SHOULD MOVE TO .cursor/rules/:'); - console.log('-'.repeat(60)); - - for (const file of analysis.shouldMoveToCursor) { - console.log(`📄 ${file.relativePath}`); - console.log(` Reason: ${file.reason}`); - console.log(` Suggested location: .cursor/rules/${this.getSuggestedLocation(file)}`); - console.log(''); - } - } - - if (analysis.unclear.length > 0) { - console.log('\n❓ UNCLEAR FILES (need manual review):'); - console.log('-'.repeat(60)); - - for (const file of analysis.unclear) { - console.log(`📄 ${file.relativePath}`); - console.log(` Reason: ${file.reason}`); - console.log(''); - } - } - - if (analysis.shouldMoveToCursor.length === 0 && analysis.unclear.length === 0) { - console.log('\n✅ All files are in the correct location!'); - } else { - console.log('\n💡 RECOMMENDATIONS:'); - console.log('-'.repeat(60)); - - if (analysis.shouldMoveToCursor.length > 0) { - console.log('1. Move identified files to .cursor/rules/ with proper metadata'); - console.log('2. Update documentation-helper.cjs to handle new file types'); - console.log('3. Update documentation-map.mdc if needed'); - } - - if (analysis.unclear.length > 0) { - console.log('4. Manually review unclear files'); - console.log('5. Update analysis criteria based on findings'); - } - } - - console.log('\n' + '='.repeat(60)); - } - - getSuggestedLocation(file) { - const name = file.name.toLowerCase(); - - if (name.includes('development-principles')) { - return 'dev/dev-principle-01-do-no-harm.mdc'; - } - - if (name.includes('file-relationships')) { - return 'architecture/architecture-project-structure.mdc'; - } - - if (name.includes('date-time-patterns')) { - return 'dev/date-time-patterns.mdc'; - } - - if (name.includes('patterns')) { - return 'dev/patterns.mdc'; - } - - if (name.includes('standards')) { - return 'doc/standards.mdc'; - } - - return 'general/'; - } - - async generateMigrationScript(analysis) { - if (analysis.shouldMoveToCursor.length === 0) { - console.log('No files to migrate.'); - return; - } - - const scriptPath = path.join(process.cwd(), 'migrate-to-cursor.sh'); - let script = '#!/bin/bash\n\n'; - script += '# Migration script for memory-bank files to .cursor/rules/\n'; - script += '# Generated by memory-bank-auditor.cjs\n\n'; - - for (const file of analysis.shouldMoveToCursor) { - const targetPath = this.getSuggestedLocation(file); - script += `# ${file.reason}\n`; - script += `# mv "${file.path}" ".cursor/rules/${targetPath}"\n\n`; - } - - fs.writeFileSync(scriptPath, script); - console.log(`📝 Migration script generated: ${scriptPath}`); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - const auditor = new MemoryBankAuditor(); - - if (args.includes('--generate-script')) { - const analysis = await auditor.audit(); - await auditor.generateMigrationScript(analysis); - } else { - await auditor.audit(); - } -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankAuditor; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-manager.cjs b/.cursor/rules/memory-bank-manager.cjs deleted file mode 100644 index c14aeaa1..00000000 --- a/.cursor/rules/memory-bank-manager.cjs +++ /dev/null @@ -1,622 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankManager { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.cursorRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async addEntry(content, options = {}) { - const { - type = 'info', - category = 'core', - priority = 'medium', - tags = options.tags ? options.tags.split(',') : [], - relatedFiles = [] - } = options; - - const timestamp = new Date().toISOString(); - const date = timestamp.split('T')[0]; - const time = timestamp.split('T')[1].split('.')[0]; - - // Определяем файл назначения - const targetFile = this.getTargetFile(type, category); - const targetPath = path.join(this.memoryBankDir, targetFile); - - // Создаем запись - const entry = this.formatEntry({ - content, - type, - category, - priority, - tags, - relatedFiles, - timestamp: `${date} ${time}`, - date - }); - - // Добавляем запись в файл - await this.appendToFile(targetPath, entry); - - console.log(`✅ Added entry to ${targetFile}`); - console.log(`📝 Type: ${type}, Category: ${category}, Priority: ${priority}`); - - return { targetFile, entry }; - } - - async updateContext(contextData) { - const contextFile = path.join(this.memoryBankDir, 'core', 'activeContext.md'); - const timestamp = new Date().toISOString().split('T')[0]; - - let content = fs.existsSync(contextFile) - ? fs.readFileSync(contextFile, 'utf8') - : this.generateActiveContextTemplate(); - - // Обновляем секцию Current Status - content = this.updateSection(content, 'Current Status', { - 'Last Updated': timestamp, - 'Current Focus': contextData.focus || 'Development', - 'Active Tasks': contextData.tasks || ['Initial setup'], - 'Next Steps': contextData.nextSteps || ['Continue development'], - 'Blockers': contextData.blockers || [] - }); - - // Добавляем новую запись в Recent Decisions - if (contextData.decisions) { - content = this.addToSection(content, 'Recent Decisions', contextData.decisions); - } - - fs.writeFileSync(contextFile, content); - console.log('✅ Updated active context'); - } - - async restoreContext(options = {}) { - const { full = false, category = null } = options; - - if (full) { - return await this.restoreFullContext(); - } - - if (category) { - return await this.restoreCategoryContext(category); - } - - return await this.restoreQuickContext(); - } - - async restoreFullContext() { - const context = { - activeContext: await this.readFile('core/activeContext.md'), - progress: await this.readFile('core/progress.md'), - errors: await this.readFile('errors/errors.md'), - decisions: await this.readFile('architecture/decisions.md'), - recentActivity: await this.getRecentActivity() - }; - - console.log('📚 Full context restored'); - return context; - } - - async restoreQuickContext() { - const context = { - activeContext: await this.readFile('core/activeContext.md'), - recentErrors: await this.getRecentErrors(), - recentDecisions: await this.getRecentDecisions() - }; - - console.log('⚡ Quick context restored'); - return context; - } - - async restoreCategoryContext(category) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) { - throw new Error(`Category ${category} not found`); - } - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')) - .map(file => path.join(category, file)); - - const context = {}; - for (const file of files) { - context[file] = await this.readFile(file); - } - - console.log(`📁 Category context restored: ${category}`); - return context; - } - - async searchMemory(query, options = {}) { - const { category = null, type = null, limit = 10 } = options; - - const results = []; - const searchDirs = category - ? [path.join(this.memoryBankDir, category)] - : this.getAllCategoryDirs(); - - for (const dir of searchDirs) { - if (!fs.existsSync(dir)) continue; - - const files = fs.readdirSync(dir) - .filter(file => file.endsWith('.md')); - - for (const file of files) { - const filePath = path.join(dir, file); - const content = fs.readFileSync(filePath, 'utf8'); - - if (content.toLowerCase().includes(query.toLowerCase())) { - const relativePath = path.relative(this.memoryBankDir, filePath); - results.push({ - file: relativePath, - content: this.extractRelevantContent(content, query), - lastModified: fs.statSync(filePath).mtime - }); - } - } - } - - // Сортируем по дате изменения и ограничиваем результаты - results.sort((a, b) => b.lastModified - a.lastModified); - return results.slice(0, limit); - } - - async generateReport(options = {}) { - const { type = 'full' } = options; - - const report = { - timestamp: new Date().toISOString(), - summary: await this.generateSummary(), - categories: await this.generateCategoryReport(), - recentActivity: await this.getRecentActivity(), - errorStats: await this.generateErrorStats(), - recommendations: await this.generateRecommendations() - }; - - if (type === 'errors') { - return { errorStats: report.errorStats }; - } - - if (type === 'progress') { - return { - summary: report.summary, - recentActivity: report.recentActivity - }; - } - - return report; - } - - // Вспомогательные методы - - getTargetFile(type, category) { - const fileMap = { - 'error': 'errors/errors.md', - 'build-error': 'errors/build-errors.md', - 'runtime-error': 'errors/runtime-errors.md', - 'ui-error': 'errors/ui-errors.md', - 'decision': 'architecture/decisions.md', - 'pattern': 'architecture/patterns.md', - 'progress': 'core/progress.md', - 'context': 'core/activeContext.md', - 'plan': 'planning/feature-roadmap.md', - 'test-result': 'development/testing-results.md', - 'debug-note': 'development/debugging-guide.md' - }; - - return fileMap[type] || `${category}/general.md`; - } - - formatEntry(data) { - const { - content, - type, - category, - priority, - tags, - relatedFiles, - timestamp, - date - } = data; - - const tagsArray = Array.isArray(tags) ? tags : (typeof tags === 'string' ? tags.split(',') : []); - const tagsStr = tagsArray.length > 0 ? tagsArray.map(tag => `#${tag.trim()}`).join(' ') : ''; - const relatedStr = relatedFiles.length > 0 - ? relatedFiles.map(file => `- ${file}`).join('\n') - : ''; - - const title = content ? content.split('\n')[0] : 'New Entry'; - - return ` -## [${timestamp}] - ${title} - -**Тип:** ${type} -**Категория:** ${category} -**Приоритет:** ${priority} - -**Контекст:** ${content || 'No content provided'} - -**Статус:** 🔄 В процессе - -${relatedStr ? `**Связанные файлы:**\n${relatedStr}\n` : ''} -${tagsStr ? `**Теги:** ${tagsStr}\n` : ''} -**AI Команды:** -- \`обнови контекст\` - для обновления активного контекста -- \`задокументируй\` - для создания документации - ---- -`; - } - - async appendToFile(filePath, content) { - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - if (!fs.existsSync(filePath)) { - const header = this.generateFileHeader(filePath); - fs.writeFileSync(filePath, header); - } - - const existingContent = fs.readFileSync(filePath, 'utf8'); - const newContent = existingContent + content; - fs.writeFileSync(filePath, newContent); - } - - generateFileHeader(filePath) { - const filename = path.basename(filePath, '.md'); - const category = path.dirname(filePath).split(path.sep).pop(); - - return `# ${filename.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} - ${category} - -## Overview - -This file contains ${category}-related information and entries. - -## Entries - - - ---- -*Auto-generated file* -`; - } - - updateSection(content, sectionName, updates) { - const lines = content.split('\n'); - const sectionStart = lines.findIndex(line => - line.trim().startsWith(`## ${sectionName}`) - ); - - if (sectionStart === -1) return content; - - let sectionEnd = lines.findIndex((line, index) => - index > sectionStart && line.trim().startsWith('## ') - ); - - if (sectionEnd === -1) sectionEnd = lines.length; - - const beforeSection = lines.slice(0, sectionStart + 1); - const afterSection = lines.slice(sectionEnd); - - const newSection = [`## ${sectionName}\n`]; - - for (const [key, value] of Object.entries(updates)) { - if (Array.isArray(value)) { - newSection.push(`**${key}:**`); - value.forEach(item => newSection.push(`- ${item}`)); - newSection.push(''); - } else { - newSection.push(`**${key}:** ${value}`); - } - } - - return [...beforeSection, ...newSection, ...afterSection].join('\n'); - } - - addToSection(content, sectionName, items) { - const lines = content.split('\n'); - const sectionStart = lines.findIndex(line => - line.trim().startsWith(`## ${sectionName}`) - ); - - if (sectionStart === -1) return content; - - const beforeSection = lines.slice(0, sectionStart + 1); - const afterSection = lines.slice(sectionStart + 1); - - const newItems = Array.isArray(items) ? items : [items]; - const formattedItems = newItems.map(item => `- ${item}`); - - return [...beforeSection, ...formattedItems, '', ...afterSection].join('\n'); - } - - async readFile(relativePath) { - const fullPath = path.join(this.memoryBankDir, relativePath); - if (!fs.existsSync(fullPath)) return null; - return fs.readFileSync(fullPath, 'utf8'); - } - - getAllCategoryDirs() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - return categories.map(cat => path.join(this.memoryBankDir, cat)); - } - - extractRelevantContent(content, query) { - const lines = content.split('\n'); - const queryLower = query.toLowerCase(); - - const relevantLines = lines.filter(line => - line.toLowerCase().includes(queryLower) - ); - - return relevantLines.slice(0, 5).join('\n'); - } - - async getRecentActivity() { - const allFiles = []; - - for (const dir of this.getAllCategoryDirs()) { - if (!fs.existsSync(dir)) continue; - - const files = fs.readdirSync(dir) - .filter(file => file.endsWith('.md')) - .map(file => path.join(dir, file)); - - allFiles.push(...files); - } - - const fileStats = allFiles.map(file => ({ - file: path.relative(this.memoryBankDir, file), - lastModified: fs.statSync(file).mtime - })); - - return fileStats - .sort((a, b) => b.lastModified - a.lastModified) - .slice(0, 10); - } - - async getRecentErrors() { - const errorsFile = path.join(this.memoryBankDir, 'errors', 'errors.md'); - if (!fs.existsSync(errorsFile)) return []; - - const content = fs.readFileSync(errorsFile, 'utf8'); - const entries = content.split('## ['); - - return entries - .slice(1) // Пропускаем заголовок - .slice(-5) // Последние 5 ошибок - .map(entry => entry.split('\n')[0]); - } - - async getRecentDecisions() { - const decisionsFile = path.join(this.memoryBankDir, 'architecture', 'decisions.md'); - if (!fs.existsSync(decisionsFile)) return []; - - const content = fs.readFileSync(decisionsFile, 'utf8'); - const entries = content.split('## ['); - - return entries - .slice(1) // Пропускаем заголовок - .slice(-5) // Последние 5 решений - .map(entry => entry.split('\n')[0]); - } - - async generateSummary() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - const summary = {}; - - for (const category of categories) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) { - summary[category] = 0; - continue; - } - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')); - - summary[category] = files.length; - } - - return summary; - } - - async generateCategoryReport() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - const report = {}; - - for (const category of categories) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) continue; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')) - .map(file => ({ - name: file, - path: path.join(category, file), - size: fs.statSync(path.join(categoryDir, file)).size - })); - - report[category] = files; - } - - return report; - } - - async generateErrorStats() { - const errorsFile = path.join(this.memoryBankDir, 'errors', 'errors.md'); - if (!fs.existsSync(errorsFile)) { - return { total: 0, resolved: 0, unresolved: 0 }; - } - - const content = fs.readFileSync(errorsFile, 'utf8'); - const entries = content.split('## ['); - const total = entries.length - 1; // Пропускаем заголовок - - const resolved = (content.match(/✅ Решено/g) || []).length; - const unresolved = total - resolved; - - return { total, resolved, unresolved }; - } - - async generateRecommendations() { - const recommendations = []; - - // Проверяем активность - const recentActivity = await this.getRecentActivity(); - if (recentActivity.length === 0) { - recommendations.push('Добавить первую запись в memory-bank'); - } - - // Проверяем ошибки - const errorStats = await this.generateErrorStats(); - if (errorStats.unresolved > 5) { - recommendations.push('Обработать нерешенные ошибки'); - } - - // Проверяем контекст - const activeContext = await this.readFile('core/activeContext.md'); - if (!activeContext || activeContext.includes('Initial setup')) { - recommendations.push('Обновить активный контекст проекта'); - } - - return recommendations; - } - - generateActiveContextTemplate() { - const date = new Date().toISOString().split('T')[0]; - - return `# Active Context - -## Current Status - -**Last Updated:** ${date} -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description - -## Next Steps - -- Step 1: Description - -## Blockers - -- Blocker 1: Description - ---- -*Auto-generated template* -`; - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const command = args[0]; - const manager = new MemoryBankManager(); - - try { - switch (command) { - case 'add': - const content = args[1]; - const options = parseOptions(args.slice(2)); - await manager.addEntry(content, options); - break; - - case 'update-context': - const contextData = parseContextData(args.slice(1)); - await manager.updateContext(contextData); - break; - - case 'restore': - const restoreOptions = parseOptions(args.slice(1)); - const context = await manager.restoreContext(restoreOptions); - console.log(JSON.stringify(context, null, 2)); - break; - - case 'search': - const query = args[1]; - const searchOptions = parseOptions(args.slice(2)); - const results = await manager.searchMemory(query, searchOptions); - console.log(JSON.stringify(results, null, 2)); - break; - - case 'report': - const reportOptions = parseOptions(args.slice(1)); - const report = await manager.generateReport(reportOptions); - console.log(JSON.stringify(report, null, 2)); - break; - - default: - console.log(` -Memory Bank Manager - CLI Tool - -Usage: - node memory-bank-manager.cjs add "content" [options] - node memory-bank-manager.cjs update-context [options] - node memory-bank-manager.cjs restore [options] - node memory-bank-manager.cjs search "query" [options] - node memory-bank-manager.cjs report [options] - -Options: - --type= Entry type (error, decision, progress, etc.) - --category= Category (core, errors, architecture, etc.) - --priority= Priority (critical, high, medium, low) - --tags= Comma-separated tags - --full Full context restoration - --category= Specific category for restore/search - --limit= Limit search results - `); - } - } catch (error) { - console.error('❌ Error:', error.message); - process.exit(1); - } -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } - } - - return options; -} - -function parseContextData(args) { - const contextData = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - contextData[key] = value; - } - } - - return contextData; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankManager; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-organizer.cjs b/.cursor/rules/memory-bank-organizer.cjs deleted file mode 100644 index 6cd3f0e9..00000000 --- a/.cursor/rules/memory-bank-organizer.cjs +++ /dev/null @@ -1,331 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -function getProtectedFilesFromMap() { - const mapPath = path.join(process.cwd(), '.cursor/rules/doc/documentation-map.mdc'); - if (!fs.existsSync(mapPath)) return []; - const content = fs.readFileSync(mapPath, 'utf-8'); - const protectedSection = content.split('## 🛡️ Protected/Administrative Files')[1]; - if (!protectedSection) return []; - return protectedSection - .split('\n') - .filter(line => line.trim().startsWith('- ')) - .map(line => line.replace(/^-\s+/, '').split('—')[0].trim()) - .filter(Boolean); -} - -class MemoryBankOrganizer { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.structurePath = path.join(this.memoryBankDir, 'MEMORY_BANK_STRUCTURE.md'); - this.protectedFiles = getProtectedFilesFromMap(); - } - - async reorganize() { - console.log('🧠 Reorganizing Memory Bank...\n'); - - // Создаем структуру каталогов - await this.createDirectoryStructure(); - - // Анализируем существующие файлы - const fileAnalysis = await this.analyzeFiles(); - - // Перемещаем файлы по новой структуре - await this.moveFiles(fileAnalysis); - - // Создаем индексы - await this.createIndexes(); - - console.log('✅ Memory Bank reorganization completed!'); - } - - async createDirectoryStructure() { - const directories = [ - 'core', - 'errors', - 'architecture', - 'development', - 'ui', - 'planning', - 'context', - 'deprecated' - ]; - - for (const dir of directories) { - const dirPath = path.join(this.memoryBankDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(`📁 Created directory: ${dir}`); - } - } - } - - async analyzeFiles() { - const files = fs.readdirSync(this.memoryBankDir) - .filter(file => file.endsWith('.md') && file !== 'MEMORY_BANK_STRUCTURE.md') - .map(file => ({ - name: file, - path: path.join(this.memoryBankDir, file), - category: this.categorizeFile(file) - })); - - return files; - } - - categorizeFile(filename) { - const name = filename.toLowerCase(); - - // Core файлы - if (name.includes('activecontext') || name.includes('progress') || - name.includes('projectbrief') || name.includes('session-log')) { - return 'core'; - } - - // Errors - if (name.includes('error') || name.includes('graveyard') || - name.includes('vite-react') || name.includes('typescript-build')) { - return 'errors'; - } - - // Architecture - if (name.includes('architecture') || name.includes('systempattern') || - name.includes('security-architecture') || name.includes('comprehensive-architecture')) { - return 'architecture'; - } - - // Development - if (name.includes('testing') || name.includes('debug') || - name.includes('devtools') || name.includes('version-management')) { - return 'development'; - } - - // UI - if (name.includes('side-panel') || name.includes('chat-context') || - name.includes('lazy-sync') || name.includes('ui')) { - return 'ui'; - } - - // Planning - if (name.includes('future') || name.includes('plan') || - name.includes('optimization') || name.includes('roadmap')) { - return 'planning'; - } - - // Context - if (name.includes('tech-context') || name.includes('product-context') || - name.includes('environment') || name.includes('context')) { - return 'context'; - } - - // Deprecated (дублирующие или устаревшие) - if (name.includes('cursor-') || name.includes('ai-') || - name.includes('mdc-file-standards') || name.includes('user-commands')) { - return 'deprecated'; - } - - return 'core'; // По умолчанию в core - } - - async moveFiles(fileAnalysis) { - for (const file of fileAnalysis) { - if (this.protectedFiles.includes(`memory-bank/${file.name}`)) { - console.log(`🔒 Protected: ${file.name} — оставлен в корне memory-bank`); - continue; - } - const targetDir = path.join(this.memoryBankDir, file.category); - const targetPath = path.join(targetDir, file.name); - - // Проверяем, не существует ли уже файл с таким именем - if (fs.existsSync(targetPath)) { - // Добавляем префикс для избежания конфликтов - const newName = `migrated-${file.name}`; - const newTargetPath = path.join(targetDir, newName); - fs.renameSync(file.path, newTargetPath); - console.log(`📄 Moved: ${file.name} → ${file.category}/${newName}`); - } else { - fs.renameSync(file.path, targetPath); - console.log(`📄 Moved: ${file.name} → ${file.category}/`); - } - } - } - - async createIndexes() { - // Создаем индекс для каждой категории - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - - for (const category of categories) { - await this.createCategoryIndex(category); - } - - // Создаем общий индекс - await this.createMainIndex(); - } - - async createCategoryIndex(category) { - const categoryDir = path.join(this.memoryBankDir, category); - const indexPath = path.join(categoryDir, 'README.md'); - - if (!fs.existsSync(categoryDir)) return; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md') && file !== 'README.md') - .sort(); - - const indexContent = `# ${this.getCategoryTitle(category)} - Index - -## Files in this category: - -${files.map(file => `- [${file.replace('.md', '')}](./${file})`).join('\n')} - -## Description: - -${this.getCategoryDescription(category)} - -## Last updated: ${new Date().toISOString().split('T')[0]} -`; - - fs.writeFileSync(indexPath, indexContent); - console.log(`📋 Created index: ${category}/README.md`); - } - - async createMainIndex() { - const indexPath = path.join(this.memoryBankDir, 'INDEX.md'); - - const indexContent = `# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Основные файлы контекста -- activeContext.md - Текущий контекст проекта -- progress.md - Прогресс разработки -- projectbrief.md - Краткое описание проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Ошибки и решения -- errors.md - Кладбище ошибок (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Архитектурные решения -- decisions.md - Принятые решения -- patterns.md - Системные паттерны -- security.md - Архитектура безопасности -- comprehensive.md - Комплексная архитектура - -### 🔧 [Development](./development/) - Процесс разработки -- testing-results.md - Результаты тестирования -- debugging-guide.md - Руководство по отладке -- devtools-guide.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX контекст -- side-panel.md - Улучшения side-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая синхронизация - -### 📅 [Planning](./planning/) - Планирование -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Контекстная информация -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Устаревшие файлы -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-results.md](./development/testing-results.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: ${new Date().toISOString().split('T')[0]} -`; - - fs.writeFileSync(indexPath, indexContent); - console.log(`📋 Created main index: INDEX.md`); - } - - getCategoryTitle(category) { - const titles = { - 'core': 'Core Files', - 'errors': 'Errors & Solutions', - 'architecture': 'Architecture Decisions', - 'development': 'Development Process', - 'ui': 'UI/UX Context', - 'planning': 'Planning & Roadmap', - 'context': 'Context Information', - 'deprecated': 'Deprecated Files' - }; - - return titles[category] || category; - } - - getCategoryDescription(category) { - const descriptions = { - 'core': 'Критически важные файлы для понимания текущего состояния проекта. Обновляются регулярно.', - 'errors': 'Проектно-специфичные ошибки и их решения. История проблем и workarounds.', - 'architecture': 'Принятые архитектурные решения с обоснованием. Влияние на проект.', - 'development': 'Результаты тестирования, руководства по отладке, процессы разработки.', - 'ui': 'UI/UX решения и улучшения. Пользовательский опыт и интерфейсные паттерны.', - 'planning': 'Планы развития проекта, roadmap, стратегические решения.', - 'context': 'Технический и продуктовый контекст. Окружение разработки.', - 'deprecated': 'Устаревшие файлы, дубликаты, мигрированные версии.' - }; - - return descriptions[category] || 'Category description'; - } - - async cleanup() { - console.log('\n🧹 Cleaning up deprecated files...'); - - const deprecatedDir = path.join(this.memoryBankDir, 'deprecated'); - if (fs.existsSync(deprecatedDir)) { - const files = fs.readdirSync(deprecatedDir); - - for (const file of files) { - if (file.endsWith('.md')) { - const filePath = path.join(deprecatedDir, file); - const content = fs.readFileSync(filePath, 'utf8'); - - // Проверяем, есть ли полезная информация - if (content.length < 1000) { - fs.unlinkSync(filePath); - console.log(`🗑️ Removed small file: ${file}`); - } - } - } - } - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - const organizer = new MemoryBankOrganizer(); - - if (args.includes('--cleanup')) { - await organizer.cleanup(); - } else { - await organizer.reorganize(); - } -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankOrganizer; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-structure-creator.cjs b/.cursor/rules/memory-bank-structure-creator.cjs deleted file mode 100644 index acd2e1db..00000000 --- a/.cursor/rules/memory-bank-structure-creator.cjs +++ /dev/null @@ -1,690 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankStructureCreator { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.templatesDir = path.join(__dirname, 'memory-bank', 'templates'); - } - - async createStructure(projectType = 'default') { - console.log(`🏗️ Creating Memory Bank structure for ${projectType} project...\n`); - - // Создаем базовую структуру - await this.createBaseStructure(); - - // Создаем специфичную структуру для типа проекта - await this.createProjectSpecificStructure(projectType); - - // Создаем индексы и README файлы - await this.createIndexes(projectType); - - console.log('✅ Memory Bank structure created successfully!'); - } - - async createBaseStructure() { - const baseCategories = [ - 'core', - 'errors', - 'architecture', - 'development', - 'ui', - 'planning', - 'context', - 'deprecated' - ]; - - for (const category of baseCategories) { - const categoryPath = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryPath)) { - fs.mkdirSync(categoryPath, { recursive: true }); - console.log(`📁 Created category: ${category}`); - } - } - } - - async createProjectSpecificStructure(projectType) { - const templates = this.getProjectTemplates(projectType); - - for (const [category, files] of Object.entries(templates)) { - const categoryPath = path.join(this.memoryBankDir, category); - - for (const file of files) { - const filePath = path.join(categoryPath, file); - if (!fs.existsSync(filePath)) { - const content = this.generateFileContent(file, projectType); - fs.writeFileSync(filePath, content); - console.log(`📄 Created: ${category}/${file}`); - } - } - } - } - - getProjectTemplates(projectType) { - const templates = { - 'react-typescript': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'build-errors.md', - 'runtime-errors.md', - 'ui-errors.md', - 'typescript-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'patterns.md', - 'state-management.md', - 'component-structure.md', - 'routing.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'build-process.md' - ], - 'ui': [ - 'component-library.md', - 'styling-patterns.md', - 'responsive-design.md', - 'accessibility.md', - 'performance.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'optimization-plans.md', - 'migration-plans.md', - 'tech-debt.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'deployment.md' - ] - }, - 'chrome-extension': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'manifest-errors.md', - 'permission-errors.md', - 'api-errors.md', - 'content-script-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'background-scripts.md', - 'content-scripts.md', - 'popup-structure.md', - 'options-page.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'packaging.md' - ], - 'ui': [ - 'popup-design.md', - 'options-page.md', - 'content-ui.md', - 'icon-design.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'store-publishing.md', - 'user-feedback.md', - 'security-audit.md' - ], - 'context': [ - 'chrome-apis.md', - 'permissions.md', - 'environment.md', - 'store-requirements.md' - ] - }, - 'node-api': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'api-errors.md', - 'database-errors.md', - 'authentication-errors.md', - 'validation-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'api-design.md', - 'database-schema.md', - 'authentication.md', - 'middleware.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'deployment.md' - ], - 'ui': [ - 'api-documentation.md', - 'swagger-spec.md', - 'client-examples.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'scaling-plans.md', - 'security-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'infrastructure.md' - ] - }, - 'monorepo': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'build-errors.md', - 'dependency-errors.md', - 'workspace-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'workspace-structure.md', - 'package-organization.md', - 'shared-libraries.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'ci-cd.md' - ], - 'ui': [ - 'shared-components.md', - 'design-system.md', - 'storybook.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'migration-plans.md', - 'refactoring-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'workspace-config.md' - ] - }, - 'fullstack': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'frontend-errors.md', - 'backend-errors.md', - 'database-errors.md', - 'deployment-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'system-architecture.md', - 'api-design.md', - 'database-design.md', - 'frontend-architecture.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'deployment.md' - ], - 'ui': [ - 'component-library.md', - 'design-system.md', - 'responsive-design.md', - 'accessibility.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'scaling-plans.md', - 'performance-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'infrastructure.md' - ] - } - }; - - return templates[projectType] || templates['react-typescript']; - } - - generateFileContent(filename, projectType) { - const date = new Date().toISOString().split('T')[0]; - - const baseContent = `# ${this.getFileTitle(filename, projectType)} - -## Overview - -This file contains ${this.getFileDescription(filename, projectType)}. - -## Entries - - - ---- -*Generated on ${date} for ${projectType} project* -`; - - // Специальные шаблоны для ключевых файлов - if (filename === 'activeContext.md') { - return this.generateActiveContextTemplate(projectType); - } - - if (filename === 'projectbrief.md') { - return this.generateProjectBriefTemplate(projectType); - } - - if (filename === 'errors.md') { - return this.generateErrorsTemplate(projectType); - } - - return baseContent; - } - - generateActiveContextTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Active Context - ${projectType} - -## Current Status - -**Last Updated:** ${date} -**Project Type:** ${projectType} -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for ${projectType} project* -`; - } - - generateProjectBriefTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Project Brief - ${projectType} - -## Project Overview - -**Name:** [Project Name] -**Type:** ${projectType} -**Created:** ${date} -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requirements - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constraints - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for ${projectType} project* -`; - } - - generateErrorsTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Error Log - ${projectType} - -## Error Categories - -### Build Errors - - -### Runtime Errors - - -### UI/UX Errors - - -### ${this.getProjectSpecificErrorCategory(projectType)} - - -## Error Resolution Workflow - -1. **Document** - Record error details -2. **Analyze** - Understand root cause -3. **Solve** - Implement solution -4. **Test** - Verify resolution -5. **Document** - Update with solution - -## Recent Errors - - - ---- -*Auto-generated for ${projectType} project* -`; - } - - getFileTitle(filename, projectType) { - const titles = { - 'activeContext.md': `Active Context - ${projectType}`, - 'progress.md': `Progress - ${projectType}`, - 'projectbrief.md': `Project Brief - ${projectType}`, - 'session-log.md': `Session Log - ${projectType}`, - 'errors.md': `Error Log - ${projectType}`, - 'decisions.md': `Architecture Decisions - ${projectType}`, - 'patterns.md': `Design Patterns - ${projectType}`, - 'testing-results.md': `Testing Results - ${projectType}`, - 'debugging-guide.md': `Debugging Guide - ${projectType}`, - 'devtools-guide.md': `DevTools Guide - ${projectType}`, - 'version-management.md': `Version Management - ${projectType}`, - 'feature-roadmap.md': `Feature Roadmap - ${projectType}`, - 'tech-stack.md': `Tech Stack - ${projectType}`, - 'dependencies.md': `Dependencies - ${projectType}`, - 'environment.md': `Environment - ${projectType}` - }; - - return titles[filename] || filename.replace('.md', '').replace(/-/g, ' '); - } - - getFileDescription(filename, projectType) { - const descriptions = { - 'activeContext.md': 'current project context and status', - 'progress.md': 'development progress and milestones', - 'projectbrief.md': 'project overview and requirements', - 'session-log.md': 'development session logs', - 'errors.md': 'error tracking and resolution', - 'decisions.md': 'architectural decisions and rationale', - 'patterns.md': 'design patterns and best practices', - 'testing-results.md': 'testing outcomes and coverage', - 'debugging-guide.md': 'debugging procedures and tips', - 'devtools-guide.md': 'development tools usage', - 'version-management.md': 'version control and releases', - 'feature-roadmap.md': 'feature planning and roadmap', - 'tech-stack.md': 'technology stack and tools', - 'dependencies.md': 'project dependencies and versions', - 'environment.md': 'development environment setup' - }; - - return descriptions[filename] || 'project-specific information'; - } - - getProjectSpecificErrorCategory(projectType) { - const categories = { - 'react-typescript': 'TypeScript Errors', - 'chrome-extension': 'Extension Errors', - 'node-api': 'API Errors', - 'monorepo': 'Workspace Errors', - 'fullstack': 'Full-stack Errors' - }; - - return categories[projectType] || 'Project Errors'; - } - - async createIndexes(projectType) { - // Создаем главный индекс - await this.createMainIndex(projectType); - - // Создаем индексы для каждой категории - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - - for (const category of categories) { - await this.createCategoryIndex(category, projectType); - } - } - - async createMainIndex(projectType) { - const indexPath = path.join(this.memoryBankDir, 'INDEX.md'); - const date = new Date().toISOString().split('T')[0]; - - const content = `# Memory Bank - ${projectType} Project - -## Project Information - -**Type:** ${projectType} -**Created:** ${date} -**Last Updated:** ${date} - -## Categories - -### 📋 [Core](./core/) - Основные файлы контекста -- activeContext.md - Текущий контекст проекта -- progress.md - Прогресс разработки -- projectbrief.md - Краткое описание проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Ошибки и решения -- errors.md - Кладбище ошибок (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Архитектурные решения -- decisions.md - Принятые решения -- patterns.md - Системные паттерны -- ${this.getArchitectureFiles(projectType)} - -### 🔧 [Development](./development/) - Процесс разработки -- testing-results.md - Результаты тестирования -- debugging-guide.md - Руководство по отладке -- devtools-guide.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX контекст -- ${this.getUIFiles(projectType)} - -### 📅 [Planning](./planning/) - Планирование -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Контекстная информация -- tech-stack.md - Технический стек -- dependencies.md - Зависимости проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- \`создай запись в memory-bank\` - Создать новую запись -- \`обнови контекст\` - Обновить активный контекст -- \`восстанови контекст\` - Восстановить полный контекст -- \`аудит memory-bank\` - Провести аудит - ---- -*Auto-generated for ${projectType} project on ${date}* -`; - - fs.writeFileSync(indexPath, content); - console.log('📋 Created main index: INDEX.md'); - } - - async createCategoryIndex(category, projectType) { - const categoryDir = path.join(this.memoryBankDir, category); - const indexPath = path.join(categoryDir, 'README.md'); - - if (!fs.existsSync(categoryDir)) return; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md') && file !== 'README.md') - .sort(); - - const content = `# ${this.getCategoryTitle(category)} - ${projectType} - -## Files in this category: - -${files.map(file => `- [${file.replace('.md', '')}](./${file})`).join('\n')} - -## Description: - -${this.getCategoryDescription(category)} - -## AI Commands: - -- \`добавь в ${category}\` - Добавить запись в эту категорию -- \`обнови ${category}\` - Обновить файлы в категории -- \`покажи ${category}\` - Показать содержимое категории - ---- -*Auto-generated for ${projectType} project* -`; - - fs.writeFileSync(indexPath, content); - console.log(`📋 Created index: ${category}/README.md`); - } - - getCategoryTitle(category) { - const titles = { - 'core': 'Core Files', - 'errors': 'Errors & Solutions', - 'architecture': 'Architecture Decisions', - 'development': 'Development Process', - 'ui': 'UI/UX Context', - 'planning': 'Planning & Roadmap', - 'context': 'Context Information' - }; - - return titles[category] || category; - } - - getCategoryDescription(category) { - const descriptions = { - 'core': 'Критически важные файлы для понимания текущего состояния проекта.', - 'errors': 'Проектно-специфичные ошибки и их решения.', - 'architecture': 'Принятые архитектурные решения с обоснованием.', - 'development': 'Результаты тестирования, руководства по отладке, процессы разработки.', - 'ui': 'UI/UX решения и улучшения, пользовательский опыт.', - 'planning': 'Планы развития проекта, roadmap, стратегические решения.', - 'context': 'Технический и продуктовый контекст, окружение разработки.' - }; - - return descriptions[category] || 'Category description'; - } - - getArchitectureFiles(projectType) { - const files = { - 'react-typescript': 'state-management.md - Управление состоянием\n- component-structure.md - Структура компонентов\n- routing.md - Маршрутизация', - 'chrome-extension': 'background-scripts.md - Background scripts\n- content-scripts.md - Content scripts\n- popup-structure.md - Структура popup', - 'node-api': 'api-design.md - Дизайн API\n- database-schema.md - Схема БД\n- authentication.md - Аутентификация', - 'monorepo': 'workspace-structure.md - Структура workspace\n- package-organization.md - Организация пакетов\n- shared-libraries.md - Общие библиотеки', - 'fullstack': 'system-architecture.md - Системная архитектура\n- api-design.md - Дизайн API\n- frontend-architecture.md - Архитектура фронтенда' - }; - - return files[projectType] || 'patterns.md - Системные паттерны'; - } - - getUIFiles(projectType) { - const files = { - 'react-typescript': 'component-library.md - Библиотека компонентов\n- styling-patterns.md - Паттерны стилизации\n- responsive-design.md - Адаптивный дизайн', - 'chrome-extension': 'popup-design.md - Дизайн popup\n- options-page.md - Страница настроек\n- content-ui.md - UI в content scripts', - 'node-api': 'api-documentation.md - Документация API\n- swagger-spec.md - Swagger спецификация\n- client-examples.md - Примеры клиентов', - 'monorepo': 'shared-components.md - Общие компоненты\n- design-system.md - Дизайн-система\n- storybook.md - Storybook', - 'fullstack': 'component-library.md - Библиотека компонентов\n- design-system.md - Дизайн-система\n- responsive-design.md - Адаптивный дизайн' - }; - - return files[projectType] || 'ui-patterns.md - UI паттерны'; - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const projectType = args[0] || 'react-typescript'; - - const creator = new MemoryBankStructureCreator(); - await creator.createStructure(projectType); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankStructureCreator; \ No newline at end of file diff --git a/.cursor/rules/memory-bank/INDEX.md b/.cursor/rules/memory-bank/INDEX.md deleted file mode 100644 index 0eee849e..00000000 --- a/.cursor/rules/memory-bank/INDEX.md +++ /dev/null @@ -1,65 +0,0 @@ -# Memory Bank - React-TypeScript Project - -## Project Information - -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Last Updated:** 2025-07-19 - -## Categories - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- State-management.md - Управление Stateм -- component-structure.md - Структура компонентов -- routing.md - Маршрутизация - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- component-library.md - Библиотека компонентов -- styling-patterns.md - Patterns стилизации -- responsive-design.md - Адаптивный Design - -### 📅 [Planning](./planning/) - Planning -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Contextual information -- tech-stack.md - Технический стек -- dependencies.md - Dependencies проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- `создай запись в memory-bank` - Create новую запись -- `обнови контекст` - Update Active контекст -- `восстанови контекст` - Восстановить полный контекст -- `аудит memory-bank` - Провести аудит - ---- -*Auto-generated for React-TypeScript project on 2025-07-19* diff --git a/.cursor/rules/memory-bank/architecture/README.md b/.cursor/rules/memory-bank/architecture/README.md deleted file mode 100644 index 21d38ba9..00000000 --- a/.cursor/rules/memory-bank/architecture/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Files in this category: - -- [component-structure](./component-structure.md) -- [decisions](./decisions.md) -- [patterns](./patterns.md) -- [routing](./routing.md) -- [State-management](./State-management.md) - -## Description: - -Принятые архитектурные решения с обоснованием. - -## AI Commands: - -- `добавь в architecture` - Add запись в эту категорию -- `обнови architecture` - Update файлы в категории -- `покажи architecture` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/component-structure.md b/.cursor/rules/memory-bank/architecture/component-structure.md deleted file mode 100644 index 6626b3ce..00000000 --- a/.cursor/rules/memory-bank/architecture/component-structure.md +++ /dev/null @@ -1,12 +0,0 @@ -# component structure - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/decisions.md b/.cursor/rules/memory-bank/architecture/decisions.md deleted file mode 100644 index 720d2530..00000000 --- a/.cursor/rules/memory-bank/architecture/decisions.md +++ /dev/null @@ -1,12 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Overview - -This file contains architectural decisions and rationale. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/patterns.md b/.cursor/rules/memory-bank/architecture/patterns.md deleted file mode 100644 index ae9baf41..00000000 --- a/.cursor/rules/memory-bank/architecture/patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# Design Patterns - React-TypeScript - -## Overview - -This file contains design patterns and best practices. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/routing.md b/.cursor/rules/memory-bank/architecture/routing.md deleted file mode 100644 index a252858e..00000000 --- a/.cursor/rules/memory-bank/architecture/routing.md +++ /dev/null @@ -1,12 +0,0 @@ -# routing - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/state-management.md b/.cursor/rules/memory-bank/architecture/state-management.md deleted file mode 100644 index d7ecf603..00000000 --- a/.cursor/rules/memory-bank/architecture/state-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# State management - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/README.md b/.cursor/rules/memory-bank/context/README.md deleted file mode 100644 index 374553e6..00000000 --- a/.cursor/rules/memory-bank/context/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Context Information - React-TypeScript - -## Files in this category: - -- [dependencies](./dependencies.md) -- [deployment](./deployment.md) -- [environment](./environment.md) -- [tech-stack](./tech-stack.md) - -## Description: - -Технический и продуктовый контекст, окружение разработки. - -## AI Commands: - -- `добавь в context` - Add запись в эту категорию -- `обнови context` - Update файлы в категории -- `покажи context` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/dependencies.md b/.cursor/rules/memory-bank/context/dependencies.md deleted file mode 100644 index 6caba601..00000000 --- a/.cursor/rules/memory-bank/context/dependencies.md +++ /dev/null @@ -1,12 +0,0 @@ -# Dependencies - React-TypeScript - -## Overview - -This file contains project dependencies and versions. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/deployment.md b/.cursor/rules/memory-bank/context/deployment.md deleted file mode 100644 index 23fa5567..00000000 --- a/.cursor/rules/memory-bank/context/deployment.md +++ /dev/null @@ -1,12 +0,0 @@ -# deployment - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/environment.md b/.cursor/rules/memory-bank/context/environment.md deleted file mode 100644 index 54b83b38..00000000 --- a/.cursor/rules/memory-bank/context/environment.md +++ /dev/null @@ -1,12 +0,0 @@ -# Environment - React-TypeScript - -## Overview - -This file contains development environment setup. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/tech-stack.md b/.cursor/rules/memory-bank/context/tech-stack.md deleted file mode 100644 index 1d83f76a..00000000 --- a/.cursor/rules/memory-bank/context/tech-stack.md +++ /dev/null @@ -1,12 +0,0 @@ -# Tech Stack - React-TypeScript - -## Overview - -This file contains technology stack and tools. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/INDEX.md b/.cursor/rules/memory-bank/core/INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/rules/memory-bank/core/INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/rules/memory-bank/core/README.md b/.cursor/rules/memory-bank/core/README.md deleted file mode 100644 index a3b68a3e..00000000 --- a/.cursor/rules/memory-bank/core/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Core Files - React-TypeScript - -## Files in this category: - -- [INDEX](./INDEX.md) -- [activeContext](./activeContext.md) -- [migrated-INDEX](./migrated-INDEX.md) -- [progress](./progress.md) -- [projectbrief](./projectbrief.md) -- [session-log](./session-log.md) - -## Description: - -Критически важные файлы для понимания текущего состояния проекта. - -## AI Commands: - -- `добавь в core` - Add запись в эту категорию -- `обнови core` - Update файлы в категории -- `покажи core` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/activeContext.md b/.cursor/rules/memory-bank/core/activeContext.md deleted file mode 100644 index 8c429166..00000000 --- a/.cursor/rules/memory-bank/core/activeContext.md +++ /dev/null @@ -1,36 +0,0 @@ -# Active Context - React-TypeScript - -## Current Status - -**Last Updated:** 2025-07-19 -**Project Type:** React-TypeScript -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/general.md b/.cursor/rules/memory-bank/core/general.md deleted file mode 100644 index 71e787bc..00000000 --- a/.cursor/rules/memory-bank/core/general.md +++ /dev/null @@ -1,31 +0,0 @@ -# General - core - -## Overview - -This file contains core-related information and entries. - -## Entries - - - ---- -*Auto-generated file* - -## [2025-07-19 01:56:13] - New Entry - -**Type:** test -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #universal #Commands #testing - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/rules/memory-bank/core/migrated-INDEX.md b/.cursor/rules/memory-bank/core/migrated-INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/rules/memory-bank/core/migrated-INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/rules/memory-bank/core/progress.md b/.cursor/rules/memory-bank/core/progress.md deleted file mode 100644 index fd42cfe2..00000000 --- a/.cursor/rules/memory-bank/core/progress.md +++ /dev/null @@ -1,31 +0,0 @@ -# Progress - React-TypeScript - -## Overview - -This file contains development progress and milestones. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* - -## [2025-07-19 01:47:05] - New Entry - -**Type:** progress -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #memory-bank #cursor #automation - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/rules/memory-bank/core/projectbrief.md b/.cursor/rules/memory-bank/core/projectbrief.md deleted file mode 100644 index 04f2e336..00000000 --- a/.cursor/rules/memory-bank/core/projectbrief.md +++ /dev/null @@ -1,33 +0,0 @@ -# Project Brief - React-TypeScript - -## Project Overview - -**Name:** [Project Name] -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requireme.ts - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constrai.ts - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/session-log.md b/.cursor/rules/memory-bank/core/session-log.md deleted file mode 100644 index 35ff1fc3..00000000 --- a/.cursor/rules/memory-bank/core/session-log.md +++ /dev/null @@ -1,12 +0,0 @@ -# Session Log - React-TypeScript - -## Overview - -This file contains development session logs. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/README.md b/.cursor/rules/memory-bank/development/README.md deleted file mode 100644 index 25270007..00000000 --- a/.cursor/rules/memory-bank/development/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Development Process - React-TypeScript - -## Files in this category: - -- [build-process](./build-process.md) -- [debugging-guIDE](./debugging-guIDE.md) -- [devtools-guIDE](./devtools-guIDE.md) -- [testing-resu.ts](./testing-resu.ts.md) -- [version-management](./version-management.md) - -## Description: - -Результаты тестирования, GuIDEs по отладке, Processes разработки. - -## AI Commands: - -- `добавь в development` - Add запись в эту категорию -- `обнови development` - Update файлы в категории -- `покажи development` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/build-process.md b/.cursor/rules/memory-bank/development/build-process.md deleted file mode 100644 index 1a1ffcf4..00000000 --- a/.cursor/rules/memory-bank/development/build-process.md +++ /dev/null @@ -1,12 +0,0 @@ -# build process - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/debugging-guide.md b/.cursor/rules/memory-bank/development/debugging-guide.md deleted file mode 100644 index 5369fa55..00000000 --- a/.cursor/rules/memory-bank/development/debugging-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# Debugging GuIDE - React-TypeScript - -## Overview - -This file contains debugging procedures and tips. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/devtools-guide.md b/.cursor/rules/memory-bank/development/devtools-guide.md deleted file mode 100644 index c0d68911..00000000 --- a/.cursor/rules/memory-bank/development/devtools-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# DevTools GuIDE - React-TypeScript - -## Overview - -This file contains development tools usage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/testing-results.md b/.cursor/rules/memory-bank/development/testing-results.md deleted file mode 100644 index bf105272..00000000 --- a/.cursor/rules/memory-bank/development/testing-results.md +++ /dev/null @@ -1,12 +0,0 @@ -# Testing Resu.ts - React-TypeScript - -## Overview - -This file contains testing outcomes and coverage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/version-management.md b/.cursor/rules/memory-bank/development/version-management.md deleted file mode 100644 index 67b7e2f6..00000000 --- a/.cursor/rules/memory-bank/development/version-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# Version Management - React-TypeScript - -## Overview - -This file contains version control and releases. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/README.md b/.cursor/rules/memory-bank/errors/README.md deleted file mode 100644 index eca2654f..00000000 --- a/.cursor/rules/memory-bank/errors/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Errors & Solutions - React-TypeScript - -## Files in this category: - -- [build-errors](./build-errors.md) -- [errors](./errors.md) -- [runtime-errors](./runtime-errors.md) -- [TypeScript-errors](./TypeScript-errors.md) -- [ui-errors](./ui-errors.md) - -## Description: - -Проектно-специфичные ошибки и их решения. - -## AI Commands: - -- `добавь в errors` - Add запись в эту категорию -- `обнови errors` - Update файлы в категории -- `покажи errors` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/build-errors.md b/.cursor/rules/memory-bank/errors/build-errors.md deleted file mode 100644 index b0462c1e..00000000 --- a/.cursor/rules/memory-bank/errors/build-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# build errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/errors.md b/.cursor/rules/memory-bank/errors/errors.md deleted file mode 100644 index 33bd85eb..00000000 --- a/.cursor/rules/memory-bank/errors/errors.md +++ /dev/null @@ -1,12 +0,0 @@ - -## [2025-07-19T01:19:27.379Z] - New Entry - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы. Это упрощает сборку, устраняет ошибки Vite/esbuild, ускоряет разработку и повышает Compatibility с современными инструментами. - ---- - -## [2025-07-19T01:19:22.935Z] - General Error - -Error сборки: Failed to resolve entry for package '@extension/vite-config'. Решение: перевести пакет на ESM-only, main: dist/index..js - ---- diff --git a/.cursor/rules/memory-bank/errors/runtime-errors.md b/.cursor/rules/memory-bank/errors/runtime-errors.md deleted file mode 100644 index c8b0744e..00000000 --- a/.cursor/rules/memory-bank/errors/runtime-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# runtime errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/typescript-errors.md b/.cursor/rules/memory-bank/errors/typescript-errors.md deleted file mode 100644 index 7f9bbbfb..00000000 --- a/.cursor/rules/memory-bank/errors/typescript-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# TypeScript errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/ui-errors.md b/.cursor/rules/memory-bank/errors/ui-errors.md deleted file mode 100644 index 19cf9b26..00000000 --- a/.cursor/rules/memory-bank/errors/ui-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# ui errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/memory-bank-controller.mdc b/.cursor/rules/memory-bank/memory-bank-controller.mdc deleted file mode 100644 index de782b38..00000000 --- a/.cursor/rules/memory-bank/memory-bank-controller.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Memory Bank Controller - Система управления memory-bank - -## 🎯 **Принцип: Memory-bank полностью подчинен .cursor правилам** - -### **Обоснование:** -- ✅ Оба каталога предназначены исключительно для AI -- ✅ Единая система правил и стандартов -- ✅ Автоматизация всех процессов -- ✅ Консистентность и предсказуемость -- ✅ Масштабируемость и поддерживаемость - -## 📋 **Структура управления memory-bank** - -### **1. Автоматическое создание структуры** -```bash -# Команда для создания структуры memory-bank -node cursor-manager..js create-memory-structure [project-type] - -# Поддерживаемые Typeы проектов: -- React-TypeScript # React + TypeScript проекты -- node-API # Node.js API проекты -- chrome-extension # Chrome Extension проекты -- monorepo # Монорепозитории -- fullstack # Full-stack приложения -``` - -### **2. Шаблоны структуры для разных Typeов проектов** - -#### **React TypeScript Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст React проекта -│ ├── progress.md # Development progress React компонентов -│ ├── projectbrief.md # Description React проекта -│ └── session-log.md # Лог сессий React разработки -├── errors/ -│ ├── errors.md # Ошибки React/TypeScript -│ ├── build-errors.md # Ошибки сборки Vite/Webpack -│ ├── runtime-errors.md # Runtime ошибки React -│ └── ui-errors.md # UI/UX ошибки -├── architecture/ -│ ├── decisions.md # Architectural decisions React -│ ├── patterns.md # React Patterns -│ ├── State-management.md # Управление Stateм -│ └── component-structure.md # Структура компонентов -├── development/ -│ ├── testing-resu.ts.md # Результаты тестирования React -│ ├── debugging-guIDE.md # Debugging React приложений -│ ├── devtools-guIDE.md # React DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── component-library.md # Библиотека компонентов -│ ├── styling-patterns.md # Patterns стилизации -│ └── responsive-design.md # Адаптивный Design -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── optimization-plans.md # Планы оптимизации -│ └── migration-plans.md # Планы миграции -├── context/ -│ ├── tech-stack.md # Технический стек -│ ├── dependencies.md # Dependencies проекта -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -#### **Chrome Extension Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст extension -│ ├── progress.md # Development progress extension -│ ├── projectbrief.md # Description extension -│ └── session-log.md # Лог сессий разработки -├── errors/ -│ ├── errors.md # Ошибки extension -│ ├── manifest-errors.md # Ошибки manifest.json -│ ├── permission-errors.md # Ошибки разрешений -│ └── API-errors.md # Ошибки Chrome APIs -├── architecture/ -│ ├── decisions.md # Architectural decisions -│ ├── background-scri.ts.md # Background scri.ts -│ ├── content-scri.ts.md # Content scri.ts -│ └── popup-structure.md # Структура popup -├── development/ -│ ├── testing-resu.ts.md # Testing extension -│ ├── debugging-guIDE.md # Debugging extension -│ ├── devtools-guIDE.md # Chrome DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── popup-design.md # Design popup -│ ├── options-page.md # Страница настроек -│ └── content-ui.md # UI в content scri.ts -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── store-publishing.md # Публикация в store -│ └── user-feedback.md # Обратная связь пользователей -├── context/ -│ ├── chrome-APIs.md # Используемые Chrome APIs -│ ├── permissions.md # Требуемые разрешения -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -## 🔄 **Автоматизация процессов memory-bank** - -### **1. Автоматическое создание записей** -```bash -# Команды для автоматического создания записей -node cursor-manager..js memory-add "content" --type=error --category=build -node cursor-manager..js memory-add "content" --type=decision --category=architecture -node cursor-manager..js memory-add "content" --type=progress --category=feature -``` - -### **2. Автоматическое обновление контекста** -```bash -# Обновление активного контекста -node cursor-manager..js memory-update-context --auto -node cursor-manager..js memory-update-progress --auto -node cursor-manager..js memory-update-session --auto -``` - -### **3. Автоматическое Recovery контекста** -```bash -# Recovery полного контекста -node cursor-manager..js memory-restore --full -node cursor-manager..js memory-restore --quick -node cursor-manager..js memory-restore --category=errors -``` - -## 📝 **Правила создания и сохранения контекста** - -### **1. Структура записей (унифицированная)** -```markdown -## [YYYY-MM-DD HH:MM:SS] - Краткое Description - -**Type:** error|decision|progress|context|plan -**Category:** build|runtime|architecture|ui|testing|planning -**Priority:** critical|high|medium|low - -**Контекст:** Description ситуации и обстоятельств - -**Детали:** Подробная информация о проблеме/решении - -**Решение/Результат:** Что было сделано и результат - -**Status:** ✅ Решено | 🔄 In Progress | ❌ Проблема | 📋 План - -**Связанные файлы:** -- .cursor/rules/dev/development-principles.mdc -- memory-bank/errors/build-errors.md - -**Теги:** #React #TypeScript #build #vite - -**AI Команды:** -- `аудит cursor` - для проверки .cursor правил -- `задокументируй` - для создания документации -``` - -### **2. Автоматические теги и категоризация** -```JavaScript -// Автоматическое определение тегов -const autoTags = { - 'React': /React.jsx|component|hook/i, - 'TypeScript': /TypeScript.ts|type|interface/i, - 'build': /build|compile|vite|webpack/i, - 'runtime': /runtime|error|crash|exception/i, - 'ui': /ui|ux|component|styling/i, - 'testing': /test|spec|jest|cypress/i, - 'architecture': /architecture|pattern|structure/i -}; -``` - -### **3. Автоматическое связывание с .cursor правилами** -```JavaScript -// Автоматическое создание связей -const ruleConnections = { - 'error': '.cursor/rules/dev/error-handling.mdc', - 'architecture': '.cursor/rules/architecture/patterns.mdc', - 'testing': '.cursor/rules/dev/testing-troubleshooting.mdc', - 'ui': '.cursor/rules/ui/ui-patterns.mdc', - 'build': '.cursor/rules/dev/build-troubleshooting.mdc' -}; -``` - -## 🤖 **AI Команды для управления memory-bank** - -### **1. Команды создания и обновления** -```bash -# Создание новой записи -"создай запись в memory-bank" / "add memory entry" -"добавь ошибку в memory-bank" / "add error to memory" -"запиши решение в memory-bank" / "record solution" - -# Обновление существующих записей -"обнови контекст" / "update context" -"обнови прогресс" / "update progress" -"обнови сессию" / "update session" -``` - -### **2. Команды восстановления и поиска** -```bash -# Recovery контекста -"восстанови контекст" / "restore context" -"восстанови полный контекст" / "restore full context" -"быстрое Recovery" / "quick restore" - -# Поиск информации -"найди в memory-bank" / "search memory" -"покажи ошибки" / "show errors" -"покажи решения" / "show solutions" -``` - -### **3. Команды управления структурой** -```bash -# Управление структурой -"создай структуру memory-bank" / "create memory structure" -"реорганизуй memory-bank" / "reorganize memory" -"очисти memory-bank" / "clean memory" -"аудит memory-bank" / "audit memory" -``` - -## 🔧 **Integration с существующими .cursor правилами** - -### **1. Обновление documentation-helper..js** -```JavaScript -// Add поддержку memory-bank команд -const memoryCommands = { - 'memory-add': 'Add запись в memory-bank', - 'memory-update': 'Update запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank' -}; -``` - -### **2. Обновление ai-memory.mdc** -```markdown -### Memory Bank Management: -- `создай запись в memory-bank` - Create новую запись с автоматической категоризацией -- `обнови контекст` - Update activeContext.md с текущим Statusом -- `восстанови контекст` - Восстановить полный контекст из memory-bank -- `аудит memory-bank` - Провести аудит и оптимизацию memory-bank -- `реорганизуй memory-bank` - Реорганизовать структуру по новым правилам -``` - -### **3. Обновление cursor-manager..js** -```JavaScript -// Add команды управления memory-bank -case 'memory-add': - await this.memoryAdd(options); - break; -case 'memory-update': - await this.memoryUpdate(options); - break; -case 'memory-restore': - await this.memoryRestore(options); - break; -case 'memory-audit': - await this.memoryAudit(options); - break; -``` - -## 📊 **Автоматические отчеты и аналитика** - -### **1. Отчеты по memory-bank** -```bash -# Генерация отчетов -node cursor-manager..js memory-report --type=errors -node cursor-manager..js memory-report --type=progress -node cursor-manager..js memory-report --type=full -``` - -### **2. Аналитика использования** -```JavaScript -// Метрики memory-bank -const metrics = { - totalEntries: 0, - entriesByCategory: {}, - entriesByType: {}, - recentActivity: [], - mostReferencedRules: [], - errorResolutionRate: 0 -}; -``` - -## ✅ **Результат полной интеграции** - -### **Преимущества:** -- ✅ **Единая система правил** - все управляется через .cursor -- ✅ **Автоматизация** - Minimum ручного вмешательства -- ✅ **Консистентность** - единообразные форматы и структуры -- ✅ **Масштабируемость** - легко добавлять новые Typeы проектов -- ✅ **AI-Optimization** - все правила учитывают потребности AI -- ✅ **Воспроизводимость** - одинаковые результаты на разных проектах - -### **Workflow:** -1. **Создание проекта** → Автоматическое создание структуры memory-bank -2. **Development** → Автоматическое создание записей через AI команды -3. **Recovery контекста** → Автоматическое чтение и анализ memory-bank -4. **Аудит и Optimization** → Регулярные проверки и улучшения - -**Memory-bank теперь полностью интегрирован с .cursor правилами!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/memory-bank/planning/README.md b/.cursor/rules/memory-bank/planning/README.md deleted file mode 100644 index d61ef5fc..00000000 --- a/.cursor/rules/memory-bank/planning/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Planning & Roadmap - React-TypeScript - -## Files in this category: - -- [feature-roadmap](./feature-roadmap.md) -- [migration-plans](./migration-plans.md) -- [optimization-plans](./optimization-plans.md) -- [tech-debt](./tech-debt.md) - -## Description: - -Планы развития проекта, roadmap, стратегические решения. - -## AI Commands: - -- `добавь в planning` - Add запись в эту категорию -- `обнови planning` - Update файлы в категории -- `покажи planning` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/feature-roadmap.md b/.cursor/rules/memory-bank/planning/feature-roadmap.md deleted file mode 100644 index 90ffb932..00000000 --- a/.cursor/rules/memory-bank/planning/feature-roadmap.md +++ /dev/null @@ -1,12 +0,0 @@ -# Feature Roadmap - React-TypeScript - -## Overview - -This file contains feature planning and roadmap. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/migration-plans.md b/.cursor/rules/memory-bank/planning/migration-plans.md deleted file mode 100644 index 012f80e1..00000000 --- a/.cursor/rules/memory-bank/planning/migration-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# migration plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/optimization-plans.md b/.cursor/rules/memory-bank/planning/optimization-plans.md deleted file mode 100644 index a31a1b10..00000000 --- a/.cursor/rules/memory-bank/planning/optimization-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# optimization plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/tech-debt.md b/.cursor/rules/memory-bank/planning/tech-debt.md deleted file mode 100644 index 01cbe91c..00000000 --- a/.cursor/rules/memory-bank/planning/tech-debt.md +++ /dev/null @@ -1,12 +0,0 @@ -# tech debt - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/README.md b/.cursor/rules/memory-bank/ui/README.md deleted file mode 100644 index 47639357..00000000 --- a/.cursor/rules/memory-bank/ui/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# UI/UX Context - React-TypeScript - -## Files in this category: - -- [accessibility](./accessibility.md) -- [component-library](./component-library.md) -- [performance](./performance.md) -- [responsive-design](./responsive-design.md) -- [styling-patterns](./styling-patterns.md) - -## Description: - -UI/UX решения и улучшения, User Experience. - -## AI Commands: - -- `добавь в ui` - Add запись в эту категорию -- `обнови ui` - Update файлы в категории -- `покажи ui` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/accessibility.md b/.cursor/rules/memory-bank/ui/accessibility.md deleted file mode 100644 index e80643a1..00000000 --- a/.cursor/rules/memory-bank/ui/accessibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# accessibility - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/component-library.md b/.cursor/rules/memory-bank/ui/component-library.md deleted file mode 100644 index 5c32e785..00000000 --- a/.cursor/rules/memory-bank/ui/component-library.md +++ /dev/null @@ -1,12 +0,0 @@ -# component library - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/performance.md b/.cursor/rules/memory-bank/ui/performance.md deleted file mode 100644 index e7b7c8ed..00000000 --- a/.cursor/rules/memory-bank/ui/performance.md +++ /dev/null @@ -1,12 +0,0 @@ -# performance - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/responsive-design.md b/.cursor/rules/memory-bank/ui/responsive-design.md deleted file mode 100644 index d26e30f1..00000000 --- a/.cursor/rules/memory-bank/ui/responsive-design.md +++ /dev/null @@ -1,12 +0,0 @@ -# responsive design - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/styling-patterns.md b/.cursor/rules/memory-bank/ui/styling-patterns.md deleted file mode 100644 index 05d60aae..00000000 --- a/.cursor/rules/memory-bank/ui/styling-patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# styling patterns - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/optimize-for-ai.cjs b/.cursor/rules/optimize-for-ai.cjs deleted file mode 100644 index a13d2ae4..00000000 --- a/.cursor/rules/optimize-for-ai.cjs +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class AIOptimizer { - constructor() { - this.rulesDir = path.join(__dirname); - } - - async optimize() { - console.log('🤖 Optimizing .cursor for AI and Cursor...\n'); - - await this.optimizeMetadata(); - await this.addAITags(); - await this.optimizeStructure(); - await this.createAIIndex(); - await this.validateAIReadiness(); - - console.log('\n✅ AI optimization completed!'); - } - - async optimizeMetadata() { - console.log('📋 Optimizing metadata for AI...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - const optimizedContent = this.optimizeFileMetadata(content, file); - - if (optimizedContent !== content) { - fs.writeFileSync(file, optimizedContent); - console.log(` ✅ Optimized ${path.relative(this.rulesDir, file)}`); - } - } - } - - optimizeFileMetadata(content, filePath) { - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (!metadataMatch) return content; - - const metadata = metadataMatch[1]; - const fileName = path.basename(filePath, '.mdc'); - const dirName = path.basename(path.dirname(filePath)); - - // Оптимизируем globs для лучшего понимания AI - let optimizedGlobs = this.getOptimizedGlobs(fileName, dirName); - let optimizedAlwaysApply = this.shouldAlwaysApply(fileName, dirName); - - // Добавляем AI-специфичные метаданные - let newMetadata = metadata; - - // Обновляем globs если нужно - if (!metadata.includes('globs:') || !metadata.includes(optimizedGlobs)) { - newMetadata = newMetadata.replace(/globs:.*\n/, `globs: ${optimizedGlobs}\n`); - if (!newMetadata.includes('globs:')) { - newMetadata = `globs: ${optimizedGlobs}\n${newMetadata}`; - } - } - - // Обновляем alwaysApply если нужно - if (!metadata.includes('alwaysApply:') || !metadata.includes(optimizedAlwaysApply.toString())) { - newMetadata = newMetadata.replace(/alwaysApply:.*\n/, `alwaysApply: ${optimizedAlwaysApply}\n`); - if (!newMetadata.includes('alwaysApply:')) { - newMetadata = `${newMetadata}alwaysApply: ${optimizedAlwaysApply}\n`; - } - } - - // Добавляем AI-специфичные поля - if (!metadata.includes('aiPriority:')) { - newMetadata = `${newMetadata}aiPriority: ${this.getAIPriority(fileName, dirName)}\n`; - } - - if (!metadata.includes('aiCategory:')) { - newMetadata = `${newMetadata}aiCategory: ${this.getAICategory(dirName)}\n`; - } - - if (newMetadata !== metadata) { - return content.replace(metadataMatch[0], `---\n${newMetadata}---\n\n`); - } - - return content; - } - - getOptimizedGlobs(fileName, dirName) { - const globMap = { - 'architecture': '["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"]', - 'dev': '["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"]', - 'doc': '["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]', - 'plugin': '["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"]', - 'security': '["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"]', - 'ui': '["pages/**/*", "**/*.tsx", "**/*.css", "**/*.scss", "packages/ui/**/*"]', - 'workflow': '["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"]' - }; - - return globMap[dirName] || '["**/*"]'; - } - - shouldAlwaysApply(fileName, dirName) { - const alwaysApplyFiles = [ - 'ai-memory', 'environment', 'index', 'README', - 'principle', 'architecture', 'workflow', 'security', - 'ai-first', 'ai-fallback', 'mdc-file-standards' - ]; - - return alwaysApplyFiles.some(pattern => fileName.includes(pattern)) || dirName === 'architecture'; - } - - getAIPriority(fileName, dirName) { - if (fileName.includes('ai-memory') || fileName.includes('environment')) { - return 'critical'; - } else if (fileName.includes('principle') || fileName.includes('architecture')) { - return 'high'; - } else if (dirName === 'doc' || dirName === 'workflow') { - return 'medium'; - } else { - return 'normal'; - } - } - - getAICategory(dirName) { - const categoryMap = { - 'architecture': 'system-design', - 'dev': 'development-practices', - 'doc': 'documentation', - 'plugin': 'plugin-development', - 'security': 'security', - 'ui': 'user-interface', - 'workflow': 'process-management' - }; - - return categoryMap[dirName] || 'general'; - } - - async addAITags() { - console.log('🏷️ Adding AI-specific tags...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - const taggedContent = this.addAITagsToContent(content, file); - - if (taggedContent !== content) { - fs.writeFileSync(file, taggedContent); - console.log(` ✅ Added AI tags to ${path.relative(this.rulesDir, file)}`); - } - } - } - - addAITagsToContent(content, filePath) { - const fileName = path.basename(filePath, '.mdc'); - const dirName = path.basename(path.dirname(filePath)); - - // Добавляем AI-специфичные комментарии в начало файла - let aiHeader = ''; - - if (fileName.includes('principle')) { - aiHeader = ` - - - -`; - } else if (fileName.includes('architecture')) { - aiHeader = ` - - - -`; - } else if (fileName.includes('ai-')) { - aiHeader = ` - - - -`; - } - - if (aiHeader && !content.includes(' -
- - -`; - } - - createInvalidPage() { - return ` - -Другая страница - -

Это не страница Ozon

-

Некоторый контент

- -`; - } - - async testOzonPageDetection() { - console.log('🔍 Тестирование определения страниц Ozon...'); - - const detectionResults = {}; - - for (const [pageName, html] of Object.entries(this.sampleOzonPages)) { - const isOzonPage = this.detectOzonPage(html); - detectionResults[pageName] = isOzonPage; - console.log(` ${pageName}: ${isOzonPage ? '✅ Ozon' : '❌ не Ozon'}`); - } - - // Проверяем результаты - Assert.isTrue(detectionResults.product_page, 'Страница товара должна быть распознана как Ozon'); - Assert.isFalse(detectionResults.invalid_page, 'Неверная страница не должна распознаваться как Ozon'); - - console.log('✅ Определение страниц Ozon работает корректно'); - return detectionResults; - } - - detectOzonPage(html) { - // Проверяем наличие характерных признаков Ozon - const ozonIndicators = [ - 'ozon.ru', - 'Озон', - 'product-card', - 'product-title', - 'product-price' - ]; - - return ozonIndicators.some(indicator => html.includes(indicator)); - } - - async testProductDataExtraction() { - console.log('🔍 Тестирование извлечения данных товара...'); - - const productHtml = this.sampleOzonPages.product_page; - const extractedData = this.extractProductData(productHtml); - - // Проверяем базовую информацию - Assert.isDefined(extractedData.title, 'Название товара должно быть извлечено'); - Assert.equal(extractedData.title, 'Витамин C с шиповником', 'Название должно быть корректным'); - - Assert.isDefined(extractedData.description, 'Описание должно быть извлечено'); - Assert.isTrue(extractedData.description.includes('иммунитета'), 'Описание должно содержать ключевые слова'); - - Assert.isDefined(extractedData.composition, 'Состав должен быть извлечен'); - Assert.isTrue(extractedData.composition.includes('Витамин C'), 'Состав должен содержать ключевые ингредиенты'); - - Assert.isDefined(extractedData.price, 'Цена должна быть извлечена'); - Assert.equal(extractedData.price, '₽ 1,299', 'Цена должна быть корректной'); - - console.log('✅ Извлечение данных товара работает'); - return extractedData; - } - - extractProductData(html) { - const data = {}; - - // Извлечение названия - const titleMatch = html.match(/]*class="[^"]*product-title[^"]*"[^>]*>([^<]+)<\/h1>/); - data.title = titleMatch ? titleMatch[1].trim() : null; - - // Извлечение описания - const descMatch = html.match(/]*class="[^"]*product-description[^"]*"[^>]*>([\s\S]*?)<\/div>/); - if (descMatch) { - const descHtml = descMatch[1]; - data.description = descHtml.replace(/<[^>]+>/g, '').trim(); - } - - // Извлечение состава - const compMatch = html.match(/]*class="[^"]*product-composition[^"]*"[^>]*>([\s\S]*?)<\/div>/); - if (compMatch) { - const compHtml = compMatch[1]; - // Извлекаем текст из элементов списка - const items = compHtml.match(/]*>([\s\S]*?)<\/li>/g) || []; - data.composition = items.map(item => item.replace(/<[^>]+>/g, '').trim()).join('; '); - } - - // Извлечение цены - const priceMatch = html.match(/]*class="[^"]*price[^"]*"[^>]*>([^<]+)<\/span>/); - data.price = priceMatch ? priceMatch[1].trim() : null; - - return data; - } - - async testCharacteristicsExtraction() { - console.log('🔍 Тестирование извлечения характеристик товара...'); - - const html = this.sampleOzonPages.product_page; - const characteristics = this.extractCharacteristics(html); - - Assert.isDefined(characteristics, 'Характеристики должны быть извлечены'); - Assert.isTrue(characteristics.length > 0, 'Должна быть хотя бы одна характеристика'); - - // Проверяем конкретные характеристики - const formaRelease = characteristics.find(c => c.name.includes('Форма')); - Assert.isDefined(formaRelease, 'Характеристика "Форма выпуска" должна быть найдена'); - Assert.equal(formaRelease.value, 'Таблетки', 'Значение должно быть "Таблетки"'); - - console.log('✅ Извлечение характеристик работает'); - return characteristics; - } - - extractCharacteristics(html) { - const characteristics = []; - - // Ищем все элементы характеристик - const charItems = html.match(/]*class="[^"]*characteristics-item[^"]*"[^>]*>[\s\S]*?<\/div>/g) || []; - - for (const item of charItems) { - const nameMatch = item.match(/]*class="[^"]*name[^"]*"[^>]*>([^<]+)<\/span>/); - const valueMatch = item.match(/]*class="[^"]*value[^"]*"[^>]*>([^<]+)<\/span>/); - - if (nameMatch && valueMatch) { - characteristics.push({ - name: nameMatch[1].trim(), - value: valueMatch[1].trim() - }); - } - } - - return characteristics; - } - - async testCategoryPageParsing() { - console.log('🔍 Тестирование парсинга страницы категории...'); - - const categoryHtml = this.sampleOzonPages.category_page; - const categoryData = this.parseCategoryPage(categoryHtml); - - Assert.isDefined(categoryData.title, 'Название категории должно быть извлечено'); - Assert.equal(categoryData.title, 'Витамины и добавки к пище', 'Название категории должно быть корректным'); - - Assert.isDefined(categoryData.productCount, 'Количество товаров должно быть извлечено'); - Assert.equal(categoryData.productCount, '1256', 'Количество товаров должно быть корректным'); - - Assert.isDefined(categoryData.products, 'Список товаров должен быть извлечен'); - Assert.isTrue(categoryData.products.length >= 2, 'Должно быть найдено несколько товаров'); - - console.log('✅ Парсинг страницы категории работает'); - return categoryData; - } - - parseCategoryPage(html) { - const data = {}; - - // Извлечение названия категории - const titleMatch = html.match(/]*>([^<]+)<\/h1>/); - data.title = titleMatch ? titleMatch[1].trim() : null; - - // Извлечение количества товаров - const countMatch = html.match(/Найдено (\d+) товаров/); - data.productCount = countMatch ? countMatch[1] : null; - - // Извлечение списка товаров - data.products = []; - const productItems = html.match(/]*class="[^"]*product-item[^"]*"[^>]*>[\s\S]*?<\/div>/g) || []; - - for (const item of productItems) { - const nameMatch = item.match(/]*>([^<]+)<\/h3>/); - const descMatch = item.match(/]*>([^<]+)<\/p>/); - const priceMatch = item.match(/]*class="[^"]*price[^"]*"[^>]*>([^<]+)<\/span>/); - - if (nameMatch) { - data.products.push({ - name: nameMatch[1].trim(), - description: descMatch ? descMatch[1].trim() : null, - price: priceMatch ? priceMatch[1].trim() : null - }); - } - } - - return data; - } - - async testHtmlRobustness() { - console.log('🔍 Тестирование устойчивости к различным HTML структурам...'); - - // Тестируем с частично поврежденным HTML - const malformedHtml = ` - - Broken Ozon Page - -
-

Broken Product

-
-

Description without closing p tag -

₽ 999
-
- - - `; - - // Должны извлечь максимум возможной информации несмотря на ошибки - const extracted = this.extractProductData(malformedHtml); - - Assert.isDefined(extracted.title, 'Название должно быть найдено несмотря на ошибки'); - Assert.isDefined(extracted.price, 'Цена должна быть найдена несмотря на ошибки'); - - console.log('✅ Устойчивость к различным HTML структурам подтверждена'); - return { robustness_tested: true }; - } - - async testEncodingHandling() { - console.log('🔍 Тестирование обработки различных кодировок...'); - - // HTML с различными кодировками и символами - const unicodeHtml = ` - - - Товар с юникодом - Озон - -
-

Витамин D₃ форте «Органика»

-
-

Содержит витамин D₃ (холекальциферол) высокой степени очистки. - Дозировка: 2000 МЕ. Производитель: Органика™.

-
-
₽ 1.299,50
-
- - - `; - - const extracted = this.extractProductData(unicodeHtml); - - // Проверяем корректную обработку unicode символов - Assert.isTrue(extracted.title.includes('D₃'), 'Unicode символы должны быть сохранены'); - Assert.isTrue(extracted.title.includes('Органика'), 'Кириллица должна быть сохранена'); - Assert.isTrue(extracted.description.includes('™'), 'Специальные символы должны быть сохранены'); - - console.log('✅ Обработка кодировок работает корректно'); - return { encoding_handled: true }; - } - - async testContentSecurity() { - console.log('🔍 Тестирование безопасности обработки контента...'); - - // HTML с потенциально опасным контентом - const maliciousHtml = ` - - - Untrusted Content - -
-

Malicious Product

-
-

Untrusted description

-
-
₽ 999
-
- - - `; - - const extracted = this.extractProductData(maliciousHtml); - - // Проверяем, что скрипты не выполняются - Assert.isFalse(extracted.title.includes(' - - \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/test-runner.js b/tests/ozon-analyzer-integration/test-runner.js deleted file mode 100644 index 824b4c80..00000000 --- a/tests/ozon-analyzer-integration/test-runner.js +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Озон Analyzer Integration Test Runner - * Комплексный тестовый раннер для проверки интеграции плагина ozon-analyzer - */ - -import { TestSuite } from './utils/test-framework.js'; -import { MockEnvironment } from './mocks/environment.js'; -import { BrowserDetection } from './browser-detection.js'; -import { PluginLoaderTest } from './specs/plugin-loader.test.js'; -import { WorkflowEngineTest } from './specs/workflow-engine.test.js'; -import { CrossEnvironmentTest } from './specs/cross-environment.test.js'; -import { AIApiTest } from './specs/ai-api.test.js'; -import { BridgeCommunicationTest } from './specs/bridge-communication.test.js'; -import { HtmlExtractionTest } from './specs/html-extraction.test.js'; -import { PerformanceBenchmark } from './specs/performance-benchmark.js'; - -export class OzonAnalyzerTestRunner { - constructor() { - this.testSuite = new TestSuite('Ozon Analyzer Integration Tests'); - this.results = { - total: 0, - passed: 0, - failed: 0, - errors: [], - startTime: null, - endTime: null - }; - this.mockEnv = new MockEnvironment(); - this.browserDetection = new BrowserDetection(); - this.compatibilityResults = null; - this.conditionalTestSuites = []; - } - - async initialize() { - console.log('🔄 Инициализация тестового окружения...'); - - try { - // Выполняем browser detection перед настройкой тестов - await this.performBrowserDetection(); - - // Настраиваем conditional тестирование основываясь на compatibility - await this.mockEnv.setup(); - this.registerConditionalTestSuites(); - - console.log('✅ Тестовое окружение инициализировано с conditional testing'); - } catch (error) { - console.error('❌ Ошибка инициализации:', error); - throw error; - } - } - - registerTestSuites() { - console.log('📋 Регистрация тест сьютов...'); - - this.testSuite.addTest('plugin-loader', new PluginLoaderTest()); - this.testSuite.addTest('workflow-engine', new WorkflowEngineTest()); - this.testSuite.addTest('cross-environment', new CrossEnvironmentTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.testSuite.addTest('bridge-communication', new BridgeCommunicationTest()); - this.testSuite.addTest('html-extraction', new HtmlExtractionTest()); - this.testSuite.addTest('performance-benchmark', new PerformanceBenchmark()); - } - - async performBrowserDetection() { - console.log('🔍 Выполнение browser detection...'); - - this.compatibilityResults = await BrowserDetection.performFullDiagnosis(); - - // Выводим отчет о compatibility - console.log('📊 COMPATIBILITY RESULTS:'); - console.log(` Mode: ${this.compatibilityResults.compatibility.mode}`); - console.log(` Chrome: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Readiness Score: ${(this.compatibilityResults.compatibility.readinessScore * 100).toFixed(1)}%`); - - if (this.compatibilityResults.compatibility.limitations && this.compatibilityResults.compatibility.limitations.length > 0) { - console.log(' Limitations:', this.compatibilityResults.compatibility.limitations.join(', ')); - } - - return this.compatibilityResults; - } - - registerConditionalTestSuites() { - console.log('⚖️ Регистрация conditional тест сьютов...'); - - const mode = this.compatibilityResults.compatibility.mode; - const chromeVersion = this.compatibilityResults.browser.majorVersion; - const hasOffscreen = this.compatibilityResults.apis.offscreen; - - // Регистрируем универсальные тесты (для всех версий) - this.testSuite.addTest('plugin-loader', new PluginLoaderTest()); - this.testSuite.addTest('cross-environment', new CrossEnvironmentTest()); - this.testSuite.addTest('bridge-communication', new BridgeCommunicationTest()); - this.testSuite.addTest('browser-detection', this.createBrowserDetectionTest()); - - // Условные тесты для разных Chrome версий - - // Chrome 109+: Полные offscreen document тесты - if (mode === 'full' && hasOffscreen && chromeVersion >= 109) { - console.log('✅ Chrome ≥109 detected - enabling full offscreen tests'); - this.testSuite.addTest('workflow-engine', new WorkflowEngineTest()); - this.testSuite.addTest('html-extraction', new HtmlExtractionTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.testSuite.addTest('performance-benchmark', new PerformanceBenchmark()); - this.conditionalTestSuites.push('offscreen-full'); - } - // Chrome <109: Simplified fallback tests - else if (mode === 'legacy' && chromeVersion >= 90 && chromeVersion < 109) { - console.log('⚠️ Chrome <109 detected - enabling legacy fallback tests'); - this.testSuite.addTest('workflow-engine-legacy', this.createLegacyWorkflowTest()); - this.testSuite.addTest('html-extraction-legacy', this.createLegacyHtmlExtractionTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.conditionalTestSuites.push('offscreen-fallback'); - } - // Partial compatibility или incompatible - else if (mode === 'partial' || mode === 'incompatible') { - console.log('🔶 Partial compatibility detected - enabling minimal tests'); - this.testSuite.addTest('workflow-engine-minimal', this.createMinimalWorkflowTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.conditionalTestSuites.push('minimal'); - } - - // Всегда включаем performance benchmark с conditional логикой - console.log(`📋 Зарегистрировано ${this.getRegisteredTestCount()} тестов`); - } - - getRegisteredTestCount() { - let count = 0; - this.testSuite.tests.forEach(() => count++); - return count; - } - - createBrowserDetectionTest() { - return { - async runAll() { - const results = BrowserDetection.performFullDiagnosis(); - return { - component: 'Browser Detection', - total: 1, - passed: results.compatibility.mode !== 'incompatible' ? 1 : 0, - failed: results.compatibility.mode === 'incompatible' ? 1 : 0, - results: [results], - readiness_score: results.compatibility.readinessScore - }; - } - }; - } - - createLegacyWorkflowTest() { - return { - async runAll() { - console.log('🚀 Запуск simplified workflow тестов (legacy mode)...'); - return { - component: 'Workflow Engine (Legacy)', - total: 5, - passed: 4, - failed: 1, - results: [ - { name: 'Core functionality', success: true, duration: 100 }, - { name: 'Context switching', success: true, duration: 80 }, - { name: 'Fallback logic', success: true, duration: 120 }, - { name: 'Service worker integration', success: true, duration: 90 }, - { name: 'Offscreen emulation', success: false, error: 'Offscreen API unavailable' } - ], - compatibility_mode: 'legacy' - }; - } - }; - } - - createLegacyHtmlExtractionTest() { - return { - async runAll() { - console.log('🚀 Запуск simplified HTML extraction тестов (legacy mode)...'); - return { - component: 'HTML Extraction (Legacy)', - total: 4, - passed: 3, - failed: 1, - results: [ - { name: 'Document parsing', success: true, duration: 200 }, - { name: 'DOM manipulation', success: true, duration: 150 }, - { name: 'Service worker simulation', success: true, duration: 80 }, - { name: 'Tab scripting fallback', success: false, error: 'Limited API access' } - ], - compatibility_mode: 'legacy' - }; - } - }; - } - - createMinimalWorkflowTest() { - return { - async runAll() { - console.log('🚀 Запуск minimal workflow тестов...'); - return { - component: 'Workflow Engine (Minimal)', - total: 3, - passed: 2, - failed: 1, - results: [ - { name: 'Basic workflow execution', success: true, duration: 150 }, - { name: 'Message passing', success: true, duration: 100 }, - { name: 'Advanced features', success: false, error: 'API limitations' } - ], - compatibility_mode: 'minimal' - }; - } - }; - } - - async runAllTests() { - console.log('\n🚀 Запуск полного интеграционного тестирования...'); - this.results.startTime = Date.now(); - - try { - const suiteResults = await this.testSuite.runAll(); - this.results.endTime = Date.now(); - this.results = { ...this.results, ...suiteResults }; - this.generateReport(); - } catch (error) { - console.error('❌ Критическая ошибка выполнения тестов:', error); - this.results.errors.push({ - type: 'test_execution_error', - message: error.message, - stack: error.stack - }); - this.results.endTime = Date.now(); - } - finally { - await this.cleanup(); - } - - return this.results; - } - - generateConditionalReport() { - const duration = this.results.endTime - this.results.startTime; - const finalReadinessScore = this.getFinalReadinessScore(); - - console.log('\n' + '='.repeat(80)); - console.log('📊 CONDITIONAL INTEGRATION TEST RESULTS - OZON ANALYZER'); - console.log('='.repeat(80)); - - // Compatibility информация - console.log('\n🌐 BROWSER COMPATIBILITY:'); - console.log(` Chrome Version: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Compatibility Mode: ${this.compatibilityResults.compatibility.mode}`); - console.log(` Readiness Score: ${(this.compatibilityResults.compatibility.readinessScore * 100).toFixed(1)}%`); - console.log(` Has Offscreen API: ${this.compatibilityResults.apis.offscreen ? '✅' : '❌'}`); - - // Conditional testing информация - console.log('\n⚖️ CONDITIONAL TESTING:'); - console.log(` Test Suites Registered: ${this.conditionalTestSuites.join(', ')}`); - console.log(` Chrome Version: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Testing Mode: ${this.getTestingModeName(this.compatibilityResults.compatibility.mode)}`); - - console.log('\n📈 TEST EXECUTION:'); - console.log(`⏱️ Время выполнения: ${(duration / 1000).toFixed(2)} секунд`); - console.log(`📊 Всего тестов: ${this.results.total}`); - console.log(`✅ Пройдено: ${this.results.passed}`); - console.log(`❌ Провалено: ${this.results.failed}`); - console.log(`📊 Процент успеха: ${((this.results.passed / this.results.total) * 100).toFixed(1)}%`); - console.log(`🎯 Final Readiness Score: ${(finalReadinessScore * 100).toFixed(1)}%`); - - if (this.results.errors.length > 0) { - console.log('\n🚨 CRITICAL ERRORS:'); - this.results.errors.forEach((error, index) => { - console.log(` ${index + 1}. ${error.type}: ${error.message}`); - if (error.stack) { - console.log(` Stack: ${error.stack.substring(0, 200)}...`); - } - }); - } - - // Рекомендации на основе readiness score - console.log('\n💡 RECOMMENDATIONS:'); - if (finalReadinessScore >= 0.95) { - console.log(' 🎉 EXCELLENT! Integration fully production-ready.'); - } else if (finalReadinessScore >= 0.85) { - console.log(' ✅ GOOD! Integration production-ready with minor concerns.'); - } else if (finalReadinessScore >= 0.70) { - console.log(' ⚠️ ACCEPTABLE! Integration functional but needs improvements.'); - } else if (finalReadinessScore >= 0.50) { - console.log(' 🔶 CAUTION! Integration has significant limitations.'); - console.log(' • Consider upgrade to Chrome 109+ for full functionality.'); - } else { - console.log(' ❌ CRITICAL! Integration has major compatibility issues.'); - console.log(' • Upgrade Chrome to version 90+ minimum.'); - console.log(' • Check extension permissions and manifest.'); - } - - console.log('='.repeat(80)); - return finalReadinessScore; - } - - getTestingModeName(mode) { - const modeNames = { - 'full': 'Full Offscreen API Testing', - 'legacy': 'Legacy Fallback Testing', - 'partial': 'Partial API Testing', - 'incompatible': 'Minimal Compatibility Testing', - 'unknown': 'Unknown Compatibility Mode' - }; - return modeNames[mode] || modeNames['unknown']; - } - - getFinalReadinessScore() { - // Комбинируем результаты compatibility и тестового выполнения - const testSuccessRate = this.results.total > 0 - ? this.results.passed / this.results.total - : 0; - - const compatibilityScore = this.compatibilityResults.compatibility.readinessScore || 0; - - // Используем веса для комбинированного score - // 60% - compatibility score, 40% - test execution success - const finalScore = 0.6 * compatibilityScore + 0.4 * testSuccessRate; - - return Math.max(0, Math.min(1, finalScore)); - } - - // Устаревший метод для совместимости, используем новый - generateReport() { - return this.generateConditionalReport(); - } - - async cleanup() { - console.log('🧹 Очистка тестового окружения...'); - await this.mockEnv.teardown(); - } -} - -// Экспорт для использования в браузере и Node.js -export default OzonAnalyzerTestRunner; - -// Автоматический запуск при загрузке в браузере -if (typeof window !== 'undefined') { - window.onload = async () => { - const runner = new OzonAnalyzerTestRunner(); - await runner.initialize(); - await runner.runAllTests(); - }; -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/test-ui.js b/tests/ozon-analyzer-integration/test-ui.js deleted file mode 100644 index 0e0d0fde..00000000 --- a/tests/ozon-analyzer-integration/test-ui.js +++ /dev/null @@ -1,583 +0,0 @@ -import OzonAnalyzerTestRunner from './test-runner.js'; -import { PerformanceBenchmark } from './specs/performance-benchmark.js'; -import { AsyncMessageHandler, sendResilientMessage } from './async-message-handler.js'; - -export class TestUI { - constructor() { - this.testRunner = new OzonAnalyzerTestRunner(); - this.setupEventListeners(); - this.logs = []; - this.isRunning = false; - } - - setupEventListeners() { - const startBtn = document.getElementById('startTestsBtn'); - const benchmarkBtn = document.getElementById('startBenchmarkBtn'); - - startBtn.addEventListener('click', () => this.startTests()); - benchmarkBtn.addEventListener('click', () => this.startBenchmark()); - } - - async startTests() { - if (this.isRunning) return; - - this.isRunning = true; - const startBtn = document.getElementById('startTestsBtn'); - startBtn.textContent = '⏳ Тестирование выполняется...'; - startBtn.disabled = true; - - // Показываем прогресс - document.getElementById('progressSection').style.display = 'block'; - document.getElementById('logsSection').style.display = 'block'; - document.getElementById('resultsSection').style.display = 'block'; - - this.logMessage('🔄 Инициализация тестового окружения...'); - this.logMessage('🔍 Выполнение browser compatibility detection...'); - - try { - await this.testRunner.initialize(); - this.logMessage('✅ Тестовое окружение готово'); - - // Выводим информацию о compatibility - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - const browser = this.testRunner.compatibilityResults.browser; - this.logMessage(`📊 Compatibility Mode: ${compat.mode}`); - this.logMessage(` Chrome Version: ${browser.majorVersion}`); - this.logMessage(` Readiness Score: ${(compat.readinessScore * 100).toFixed(1)}%`); - if (compat.recommendations && compat.recommendations.length > 0) { - compat.recommendations.forEach(rec => { - this.logMessage(`💡 ${rec}`); - }); - } - } - - this.updateProgress(10, 'Запуск conditional тест сьюта...'); - - this.logMessage('\n🚀 ЗАПУСК CONDITIONAL ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ OZON ANALYZER\n'); - this.logMessage(` Условные режимы: ${this.testRunner.conditionalTestSuites.join(', ')}\n`); - - const results = await this.testRunner.runAllTests(); - - this.displayConditionalResults(results); - - this.logMessage(`\n✅ Conditional тестирование завершено!`); - - // Показываем Final Readiness Score - if (this.testRunner.compatibilityResults) { - const finalScore = this.testRunner.getFinalReadinessScore(); - this.logMessage(`🎯 Final Readiness Score: ${(finalScore * 100).toFixed(1)}%`); - - if (finalScore >= 0.9) { - this.logMessage(`🎉 INTEGRATION PRODUCTION-READY!`); - } else if (finalScore >= 0.7) { - this.logMessage(`⚠️ INTEGRATION FUNCTIONAL WITH IMPROVEMENT AREAS`); - } else { - this.logMessage(`🔶 INTEGRATION NEEDS ATTENTION!`); - } - - // Log AsyncMessageHandler statistics if available - this.logAsyncMessageHandlerStats(); - } - - } catch (error) { - this.logMessage(`❌ КРИТИЧЕСКАЯ ОШИБКА: ${error.message}`); - console.error('Test execution error:', error); - } finally { - this.isRunning = false; - startBtn.textContent = '🚀 Повторить тестирование'; - startBtn.disabled = false; - this.updateProgress(100, 'Conditional тестирование завершено'); - } - } - - async startBenchmark() { - if (this.isRunning) return; - - this.isRunning = true; - const benchmarkBtn = document.getElementById('startBenchmarkBtn'); - benchmarkBtn.textContent = '⏳ Бенчмаркинг...'; - benchmarkBtn.disabled = true; - - // Показываем прогресс - document.getElementById('progressSection').style.display = 'block'; - document.getElementById('logsSection').style.display = 'block'; - document.getElementById('resultsSection').style.display = 'block'; - - this.logMessage('🔄 Запуск бенчмаркинга производительности...'); - - try { - this.logMessage('\n🧪 ПРОФИЛИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ OZON ANALYZER\n'); - - const benchmark = new PerformanceBenchmark(); - const results = await benchmark.runAll(); - - this.displayBenchmarkResults(results); - - this.logMessage(`\n✅ Бенчмаркинг завершён!`); - this.logMessage(`📊 Анализ производительности готов к просмотру.`); - - } catch (error) { - this.logMessage(`❌ ОШИБКА БЕНЧМАРКИНГА: ${error.message}`); - console.error('Benchmark execution error:', error); - } finally { - this.isRunning = false; - benchmarkBtn.textContent = '🧪 Повторить бенчмаркинг'; - benchmarkBtn.disabled = false; - this.updateProgress(100, 'Бенчмаркинг завершён'); - } - } - - logMessage(message) { - this.logs.push(message); - const logsContainer = document.getElementById('logsContainer'); - logsContainer.textContent = this.logs.join('\n'); - logsContainer.scrollTop = logsContainer.scrollHeight; - - // Также выводим в консоль для отладки - console.log(message); - } - - updateProgress(percent, text) { - const progressFill = document.getElementById('progressFill'); - const progressText = document.getElementById('progressText'); - - progressFill.style.width = `${percent}%`; - progressText.textContent = text; - } - - displayResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - // Общая статистика - const totalScore = results.passed / results.total * 100; - - resultsTitle.innerHTML = ` - 📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ: - - ${totalScore.toFixed(1)}% успеха - - `; - - let html = ` -
-
- Всего тестов: - ${results.total} -
-
- Пройдено: - ${results.passed} -
-
- Провалено: - ${results.failed} -
-
- Время выполнения: - ${((results.endTime - results.startTime) / 1000).toFixed(2)}s -
-
- -

📋 Детализация по компонентам:

- `; - - // Результаты по компонентам - if (results.testSuiteResults && results.testSuiteResults.length > 0) { - for (const componentResult of results.testSuiteResults) { - const successRate = componentResult.passed / componentResult.total * 100; - const status = successRate === 100 ? 'success' : successRate >= 50 ? 'warning' : 'failure'; - - html += ` -
- ${componentResult.component}: ${componentResult.passed}/${componentResult.total} - ${successRate.toFixed(1)}% -
- `; - } - } - - // Критические ошибки - if (results.errors && results.errors.length > 0) { - html += ` -

🚨 Критические ошибки:

- `; - - for (const error of results.errors) { - html += ` -
- ${error.type} -
${error.message}
- ${error.stack ? ` -
- Stack trace -
${error.stack}
-
- ` : ''} -
- `; - } - } - - // Рекомендации - html += ` -
-

💡 Рекомендации:

-
    - `; - - if (totalScore === 100) { - html += `
  • ✅ Интеграция работает идеально! Можно переходить к продакшену.
  • `; - } else if (totalScore >= 80) { - html += `
  • ⚠️ Интеграция работает с незначительными проблемами. Рекомендуется исправить ошибки перед продакшеном.
  • `; - } else if (totalScore >= 60) { - html += `
  • 🔶 Обнаружены существенные проблемы. Требуется доработка интеграции.
  • `; - } else { - html += `
  • ❌ Критические проблемы интеграции. Необходимо полное перетестирование.
  • `; - } - - if (results.failed > 0) { - html += `
  • 🔍 Рекомендуется детально изучить логи проваленных тестов для диагностики проблем.
  • `; - } - - html += ` -
-
- `; - - resultsContent.innerHTML = html; - - this.logMessage('\n📊 РЕЗУЛЬТАТЫ:'); - this.logMessage(` Пройдено: ${results.passed}/${results.total} (${totalScore.toFixed(1)}%)`); - this.logMessage(` Время: ${((results.endTime - results.startTime) / 1000).toFixed(2)} сек`); - } - - displayConditionalResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - const totalScore = results.passed / results.total * 100; - const finalScore = this.testRunner.getFinalReadinessScore(); - - resultsTitle.innerHTML = ` - 📊 CONDITIONAL INTEGRATION TEST RESULTS: - - ${totalScore.toFixed(1)}% success - -
- - Final Readiness Score: ${(finalScore * 100).toFixed(1)}% - - `; - - let html = ''; - - // Browser Compatibility Section - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - const browser = this.testRunner.compatibilityResults.browser; - - html += ` -
-

🌐 BROWSER COMPATIBILITY

-
- Chrome Version: - ${browser.majorVersion} -
-
- Compatibility Mode: - ${compat.mode} -
-
- Readiness Score: - ${(compat.readinessScore * 100).toFixed(1)}% -
-
- `; - } - - // Conditional Test Suites Info - html += ` -
-

⚖️ CONDITIONAL TESTING

-
- Testing Modes: - ${this.testRunner.conditionalTestSuites.join(', ')} -
- `; - - if (this.testRunner.compatibilityResults && this.testRunner.compatibilityResults.compatibility.limitations) { - html += `
Limitations:${this.testRunner.compatibilityResults.compatibility.limitations.join(', ')}
`; - } - - html += `
`; - - // Test Execution Statistics - html += ` -
-

📈 TEST EXECUTION STATISTICS

-
- Всего тестов: - ${results.total} -
-
- Пройдено: - ${results.passed} -
-
- Провалено: - ${results.failed} -
-
- Время выполнения: - ${((results.endTime - results.startTime) / 1000).toFixed(2)}s -
-
- -

📋 Детализация по компонентам:

- `; - - // Результаты по компонентам - if (results.testSuiteResults && results.testSuiteResults.length > 0) { - for (const componentResult of results.testSuiteResults) { - const successRate = componentResult.passed / componentResult.total * 100; - const status = successRate === 100 ? 'success' : successRate >= 50 ? 'warning' : 'failure'; - - html += ` -
- ${componentResult.component}: ${componentResult.passed}/${componentResult.total} - ${successRate.toFixed(1)}% -
- `; - } - } - - // Production Ready Assessment - const readinessColor = finalScore >= 0.9 ? '#27ae60' : finalScore >= 0.7 ? '#f39c12' : '#e74c3c'; - const readinessText = finalScore >= 0.9 ? 'PRODUCTION-READY!' : finalScore >= 0.7 ? 'FUNCTIONAL' : 'REQUIRES ATTENTION'; - - html += ` -
-

🎯 PRODUCTION READINESS ASSESSMENT

-

- ${readinessText} (${(finalScore * 100).toFixed(1)}%) -

-

- ${finalScore >= 0.9 ? '✅ Your integration is fully ready for production deployment.' : - finalScore >= 0.7 ? '⚠️ Integration is functional but review recommendations above.' : - '🔶 Consider upgrading Chrome and addressing compatibility issues.'} -

-
- `; - - // Оставляем рекомендации и ошибки как есть, но изменяем вызов - html += this.getRecommendationsHtml(results); - html += this.getErrorsHtml(results); - - resultsContent.innerHTML = html; - - this.logMessage('\n📊 CONDITIONAL TEST RESULTS:'); - this.logMessage(` Пройдено: ${results.passed}/${results.total} (${totalScore.toFixed(1)}%)`); - this.logMessage(` Final Readiness: ${(finalScore * 100).toFixed(1)}%`); - this.logMessage(` Время: ${((results.endTime - results.startTime) / 1000).toFixed(2)} сек`); - } - - // Метод дляancell форматирования ошибок вынесен отдельно - getErrorsHtml(results) { - if (!results.errors || results.errors.length === 0) return ''; - - return ` -

🚨 Критические ошибки:

- ${results.errors.map((error, index) => ` -
- ${error.type} -
${error.message}
- ${error.stack ? ` -
- Stack trace -
${error.stack}
-
- ` : ''} -
- `).join('')} - `; - } - - // Метод для рекомендаций вынесен отдельно - getRecommendationsHtml(results) { - const totalScore = results.passed / results.total * 100; - const finalScore = this.testRunner.getFinalReadinessScore(); - - let recommendations = ` -
-

💥 CONDITIONAL RECOMMENDATIONS:

-
    - `; - - if (finalScore >= 0.9) { - recommendations += '
  • ✅ Full production-ready integration!
  • '; - } else if (finalScore >= 0.8) { - recommendations += '
  • ⚠️ Minor optimization opportunities available.
  • '; - } else if (finalScore >= 0.6) { - recommendations += '
  • ⚡ Substantial improvements needed for production.
  • '; - } else { - recommendations += '
  • 🔶 Critical compatibility issues require immediate attention.
  • '; - } - - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - if (compat.recommendations) { - compat.recommendations.forEach(rec => { - recommendations += `
  • 💡 ${rec}
  • `; - }); - } - } - - if (results.failed > 0) { - recommendations += '
  • 🔍 Review failed tests and compatibility limitations.
  • '; - } - - recommendations += '
'; - return recommendations; - } - - logAsyncMessageHandlerStats() { - try { - // If there's a global AsyncMessageHandler instance, log its stats - if (window.AsyncMessageHandler) { - const stats = { - example_retry_logic: '✓ Available', - exponential_backoff: '✓ Implemented', - graceful_degradation: '✓ Fallback mode', - timeout_handling: '✓ Configured' - }; - - this.logMessage('\n🔄 ASYNC MESSAGE HANDLER STATS:'); - Object.entries(stats).forEach(([key, value]) => { - this.logMessage(` ${key}: ${value}`); - }); - this.logMessage(' 📊 Resilient communication layer active!'); - } - } catch (error) { - // Silently ignore if stats can't be logged - console.log('AsyncMessageHandler stats not available'); - } - } - - displayBenchmarkResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - resultsTitle.innerHTML = ` - 🚀 РЕЗУЛЬТАТЫ ПРОФИЛИРОВАНИЯ ПРОИЗВОДИТЕЛЬНОСТИ OZON ANALYZER - `; - - let html = ''; - - // Основные метрики производительности - if (results.analysis && results.analysis[4] && results.analysis[4].result) { - const benchmarkData = results.analysis[4].result; - const analysisResults = benchmarkData.analysisResults; - - // Таблица метрик - html += ` -
-

📊 Performance Metrics (сек):

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Pyodide Initialisierung:${(benchmarkData.benchmarkResults?.pyodideInitialization || 0).toFixed(2)}s25-35s prod
DOM Parsing:${(benchmarkData.benchmarkResults?.htmlParsing || 0).toFixed(2)}s3-6s prod
Sequential AI Calls:${(benchmarkData.benchmarkResults?.sequentialAiCalls || 0).toFixed(2)}s8-26s prod
Total Execution:${(benchmarkData.benchmarkResults?.totalExecutionTime || 0).toFixed(2)}s💡 Optimization Target
Peak Memory:${((benchmarkData.benchmarkResults?.memoryUsage?.pyodidePeak || 0) / 1024 / 1024).toFixed(1)}MBMemory usage
-
- `; - - // Bottlenecks - if (analysisResults.bottlenecks && analysisResults.bottlenecks.length > 0) { - html += ` -
-

🚧 Identified Bottlenecks:

-
    - ${analysisResults.bottlenecks.map(b => `
  • ${b}
  • `).join('')} -
-
- `; - } - - // Рекомендации - if (analysisResults.recommendations && analysisResults.recommendations.length > 0) { - html += ` -
-

💡 Optimization Recommendations:

-
    - ${analysisResults.recommendations.map(r => `
  1. ${r}
  2. `).join('')} -
-
- `; - } - - // Ожидания улучшения - if (analysisResults.estimatedImprovement && benchmarkData.benchmarkResults?.totalExecutionTime) { - const totalTime = benchmarkData.benchmarkResults.totalExecutionTime; - const targetTime = analysisResults.estimatedImprovement.totalPotential; - const improvement = totalTime > 0 ? (100 * (totalTime - targetTime) / totalTime).toFixed(1) : 0; - - html += ` -
-

🎯 Projected Performance Improvement:

-

- Current: ${totalTime.toFixed(2)}s → - Target: ${targetTime.toFixed(2)}s
- ${improvement}% improvement -

-
- `; - } - } else { - html = `
${JSON.stringify(results, null, 2)}
`; - } - - resultsContent.innerHTML = html; - - // Логируем метрики - this.logMessage('\n📈 PERFORMANCE ANALYSIS COMPLETE:'); - if (results.analysis && results.analysis[4] && results.analysis[4].result) { - const br = results.analysis[4].result.benchmarkResults; - this.logMessage(` Pyodide: ${br?.pyodideInitialization?.toFixed(2)}s`); - this.logMessage(` DOM: ${br?.htmlParsing?.toFixed(2)}s`); - this.logMessage(` AI Sequential: ${br?.sequentialAiCalls?.toFixed(2)}s`); - this.logMessage(` Total: ${br?.totalExecutionTime?.toFixed(2)}s`); - } - this.logMessage(` ✅ Analysis ready for optimization implementation!`); - } -} - -// Запуск интерфейса -document.addEventListener('DOMContentLoaded', () => { - window.testUI = new TestUI(); -}); - -// Также делаем доступным глобально для отладки -window.OzonAnalyzerTestRunner = OzonAnalyzerTestRunner; \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/utils/test-framework.js b/tests/ozon-analyzer-integration/utils/test-framework.js deleted file mode 100644 index 4e09b12e..00000000 --- a/tests/ozon-analyzer-integration/utils/test-framework.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Простой тестовый фреймворк для интеграционного тестирования - */ - -export class TestCase { - constructor(name, testFunction) { - this.name = name; - this.testFunction = testFunction; - this.result = null; - this.error = null; - this.duration = 0; - } - - async run() { - const startTime = Date.now(); - try { - this.result = await this.testFunction(); - this.duration = Date.now() - startTime; - return { success: true, result: this.result }; - } catch (error) { - this.error = error; - this.duration = Date.now() - startTime; - return { success: false, error: error }; - } - } -} - -export class TestSuite { - constructor(name) { - this.name = name; - this.tests = new Map(); - this.results = []; - } - - addTest(name, testCase) { - this.tests.set(name, testCase); - } - - async runTest(testName) { - const test = this.tests.get(testName); - if (!test) { - throw new Error(`Тест "${testName}" не найден`); - } - - console.log(`▶️ Выполнение теста: ${testName}`); - const result = { testName, ...await test.run() }; - - if (result.success) { - console.log(`✅ Тест "${testName}" пройден (${test.duration}ms)`); - } else { - console.log(`❌ Тест "${testName}" провален: ${result.error?.message} (${test.duration}ms)`); - } - - return result; - } - - async runAll() { - console.log(`\n📋 Запуск сьюта: ${this.name}`); - console.log(`📊 Количество тестов: ${this.tests.size}\n`); - - const summary = { - total: this.tests.size, - passed: 0, - failed: 0, - errors: [], - duration: 0 - }; - - const startTime = Date.now(); - - for (const [testName, test] of this.tests) { - const result = { testName, ...await test.run() }; - - if (result.success) { - summary.passed++; - console.log(`✅ ${testName} (${test.duration}ms)`); - } else { - summary.failed++; - summary.errors.push({ - testName, - error: result.error?.message, - stack: result.error?.stack - }); - console.log(`❌ ${testName}: ${result.error?.message} (${test.duration}ms)`); - } - - this.results.push(result); - } - - summary.duration = Date.now() - startTime; - - console.log(`\n📊 Результаты сьюта "${this.name}":`); - console.log(` Всего: ${summary.total}`); - console.log(` Пройдено: ${summary.passed}`); - console.log(` Провалено: ${summary.failed}`); - console.log(` Время: ${(summary.duration / 1000).toFixed(2)}s`); - - return summary; - } - - getResults() { - return this.results; - } -} - -export class Assert { - static equal(actual, expected, message = '') { - if (actual !== expected) { - throw new Error(`${message} Expected: ${expected}, Actual: ${actual}`.trim()); - } - } - - static notEqual(actual, expected, message = '') { - if (actual === expected) { - throw new Error(`${message} Expected not equal, but both are: ${actual}`.trim()); - } - } - - static isTrue(value, message = '') { - if (!value) { - throw new Error(`${message} Expected true, got: ${value}`.trim()); - } - } - - static isFalse(value, message = '') { - if (value) { - throw new Error(`${message} Expected false, got: ${value}`.trim()); - } - } - - static isDefined(value, message = '') { - if (value === undefined || value === null) { - throw new Error(`${message} Value is not defined`.trim()); - } - } - - static throws(fn, message = '') { - let threw = false; - try { - fn(); - } catch (e) { - threw = true; - } - - if (!threw) { - throw new Error(`${message} Expected function to throw, but it didn't`.trim()); - } - } - - static async throwsAsync(fn, message = '') { - let threw = false; - try { - await fn(); - } catch (e) { - threw = true; - } - - if (!threw) { - throw new Error(`${message} Expected async function to throw, but it didn't`.trim()); - } - } - - static contains(array, item, message = '') { - if (!Array.isArray(array) || !array.includes(item)) { - throw new Error(`${message} Array ${array} does not contain ${item}`.trim()); - } - } - - static matches(value, regex, message = '') { - if (!regex.test(value)) { - throw new Error(`${message} Value "${value}" does not match pattern ${regex}`.trim()); - } - } - - static type(value, expectedType, message = '') { - const actualType = typeof value; - if (actualType !== expectedType) { - throw new Error(`${message} Expected type ${expectedType}, but got ${actualType}`.trim()); - } - } -} \ No newline at end of file diff --git a/tests/run-mock-tests.cjs b/tests/run-mock-tests.cjs deleted file mode 100644 index a793a36b..00000000 --- a/tests/run-mock-tests.cjs +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Простой runner для mock-тестов HTML chunking процесса - * Запуск: node tests/run-mock-tests.js - */ - -const { readFileSync } = require('fs'); -const { join } = require('path'); - -// Простая реализация test runner -class SimpleTestRunner { - constructor() { - this.tests = []; - this.currentTest = null; - this.passed = 0; - this.failed = 0; - this.results = []; - } - - describe(name, fn) { - console.log(`\n📋 Suite: ${name}`); - console.log('='.repeat(50)); - fn(); - } - - test(name, fn) { - this.currentTest = { name, status: 'pending' }; - try { - const result = fn(); - if (result && typeof result.then === 'function') { - return result.then(() => { - this.currentTest.status = 'passed'; - this.passed++; - console.log(`✅ ${name}`); - }).catch(error => { - this.currentTest.status = 'failed'; - this.currentTest.error = error; - this.failed++; - console.log(`❌ ${name}: ${error.message}`); - }); - } else { - this.currentTest.status = 'passed'; - this.passed++; - console.log(`✅ ${name}`); - } - } catch (error) { - this.currentTest.status = 'failed'; - this.currentTest.error = error; - this.failed++; - console.log(`❌ ${name}: ${error.message}`); - } - } - - expect(value) { - return { - toBe: (expected) => { - if (value !== expected) { - throw new Error(`Expected ${expected}, but got ${value}`); - } - }, - toBeDefined: () => { - if (value === undefined) { - throw new Error(`Expected value to be defined, but got ${value}`); - } - }, - toBeGreaterThan: (expected) => { - if (value <= expected) { - throw new Error(`Expected ${value} to be greater than ${expected}`); - } - }, - toBeLessThan: (expected) => { - if (value >= expected) { - throw new Error(`Expected ${value} to be less than ${expected}`); - } - } - }; - } - - printSummary() { - console.log('\n' + '='.repeat(60)); - console.log('🧪 TEST SUMMARY'); - console.log('='.repeat(60)); - console.log(`✅ Passed: ${this.passed}`); - console.log(`❌ Failed: ${this.failed}`); - console.log(`📊 Total: ${this.passed + this.failed}`); - - if (this.failed === 0) { - console.log('\n🎉 All tests passed!'); - } else { - console.log('\n⚠️ Some tests failed. Check the output above.'); - } - } -} - -// Global test functions -global.describe = function(name, fn) { - global.testRunner.describe(name, fn); -}; - -global.test = function(name, fn) { - return global.testRunner.test(name, fn); -}; - -global.expect = function(value) { - return global.testRunner.expect(value); -}; - -global.beforeEach = function(fn) { - // Simple beforeEach implementation - if (!global.beforeEachFunctions) { - global.beforeEachFunctions = []; - } - global.beforeEachFunctions.push(fn); -}; - -global.afterEach = function(fn) { - // Simple afterEach implementation - if (!global.afterEachFunctions) { - global.afterEachFunctions = []; - } - global.afterEachFunctions.push(fn); -}; - -// Mock implementations -global.jest = { - fn: () => { - const mockFn = function(...args) { - mockFn.calls.push(args); - return undefined; - }; - mockFn.calls = []; - return mockFn; - }, - clearAllMocks: () => { - // Simple mock clearing - }, - restoreAllMocks: () => { - // Simple mock restoration - } -}; - -// Console override for capturing logs -const originalConsole = console; -global.console = { - ...originalConsole, - log: (...args) => { - // Filter out some verbose logs during testing - if (args[0] && typeof args[0] === 'string' && args[0].includes('🔍')) return; - if (args[0] && typeof args[0] === 'string' && args[0].includes('📊')) return; - originalConsole.log(...args); - }, - warn: originalConsole.warn, - error: originalConsole.error -}; - -// Initialize global test runner -global.testRunner = new SimpleTestRunner(); - -// Load and run tests -async function runTests() { - try { - console.log('🚀 Starting HTML Chunking Mock Tests...\n'); - - // Load test file - const testFile = join(__dirname, 'html-chunking-mock.test.ts'); - - // Simple TypeScript compilation (remove type annotations) - let testCode = readFileSync(testFile, 'utf8'); - - // Remove TypeScript type annotations (simple approach) - testCode = testCode - .replace(/:\s*\w+/g, '') // Remove type annotations - .replace(/import\s+type\s+[^;]+;/g, '') // Remove type imports - .replace(/<\w+>/g, '') // Remove generic types - .replace(/export\s+/g, '') // Remove exports - .replace(/private\s+|public\s+|protected\s+/g, '') // Remove access modifiers - .replace(/readonly\s+/g, '') // Remove readonly - .replace(/async\s+/g, '') // Remove async (for simplicity) - .replace(/await\s+/g, '') // Remove await (for simplicity) - .replace(/Promise<[^>]+>/g, 'Promise') // Simplify Promise types - .replace(/:\s*Promise/g, ': Promise') // Fix Promise types - .replace(/interface\s+\w+\s*{[^}]*}/g, '') // Remove interfaces - .replace(/type\s+\w+\s*=\s*[^;]+;/g, ''); // Remove type definitions - - // Execute test code - eval(testCode); - - // Print summary after all tests complete - setTimeout(() => { - global.testRunner.printSummary(); - }, 100); - - } catch (error) { - console.error('❌ Error running tests:', error); - process.exit(1); - } -} - -// Handle async tests -process.on('unhandledRejection', (reason, promise) => { - console.error('❌ Unhandled Rejection at:', promise, 'reason:', reason); -}); - -process.on('uncaughtException', (error) => { - console.error('❌ Uncaught Exception:', error); - process.exit(1); -}); - -// Run the tests -runTests().catch(error => { - console.error('❌ Failed to run tests:', error); - process.exit(1); -}); \ No newline at end of file diff --git a/tests/simple-mock-tests.js b/tests/simple-mock-tests.js deleted file mode 100644 index c7e6d3b9..00000000 --- a/tests/simple-mock-tests.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * Простые mock-тесты для HTML chunking процесса - * Запуск: node tests/simple-mock-tests.js - */ - -console.log('🧪 HTML Chunking Mock Tests'); -console.log('='.repeat(50)); - -// Mock implementations -class MockCircuitBreaker { - constructor() { - this.state = 'CLOSED'; - } - - async execute(operation) { - return await operation(); - } - - getState() { - return this.state; - } -} - -class MockTransferPersistenceManager { - constructor() { - this.storage = new Map(); - } - - async save(transfer, transferId) { - this.storage.set(transferId, { - transferId, - startTime: Date.now(), - totalChunks: transfer.chunks.length, - totalSize: transfer.totalSize, - status: 'active', - lastUpdated: Date.now() - }); - } - - async load(transferId) { - return this.storage.get(transferId) || null; - } - - async loadAll() { - return Object.fromEntries(this.storage); - } -} - -class MockTransferRecoveryManager { - async recoverTransfer(transferId) { - return { - success: true, - transfer: { - chunks: ['mock-chunk-1', 'mock-chunk-2'], - acked: [true, true], - totalSize: 100, - startTime: Date.now(), - htmlAssembledConfirmed: true - }, - html: 'Mock recovered HTML', - strategy: 'mock_recovery', - duration: 50 - }; - } -} - -class MockEnhancedChunkManager { - constructor() { - this.transfers = new Map(); - this.completedTransfers = new Map(); - this.assembledHtmls = new Map(); - this.emergencyBackup = new Map(); - this.globalTransferRefs = new Map(); - this.persistenceManager = new MockTransferPersistenceManager(); - this.recoveryManager = new MockTransferRecoveryManager(); - } - - async sendInChunks(data, transferId) { - const chunks = this.createChunks(data); - const transfer = { - chunks, - acked: new Array(chunks.length).fill(false), - totalSize: data.length, - startTime: Date.now(), - htmlAssembledConfirmed: false - }; - - this.transfers.set(transferId, transfer); - this.globalTransferRefs.set(transferId, transfer); - this.emergencyBackup.set(transferId, transfer); - - // Многоуровневое хранение для надежности - global.emergencyTransfers = global.emergencyTransfers || {}; - global.emergencyTransfers[transferId] = { - id: transferId, - transfer: transfer, - timestamp: Date.now(), - status: 'active' - }; - - global.fixedTransfers = global.fixedTransfers || []; - global.fixedTransfers.push({ - id: transferId, - transfer: transfer, - timestamp: Date.now(), - status: 'active' - }); - - console.log(`✅ CREATED transfer ${transferId} with ${chunks.length} chunks in multi-layer storage`); - } - - createChunks(data) { - const chunks = []; - const chunkSize = 32768; // 32KB - for (let i = 0; i < data.length; i += chunkSize) { - chunks.push(data.slice(i, i + chunkSize)); - } - return chunks; - } - - async acknowledgeChunk(transferId, chunkIndex) { - const transfer = this.transfers.get(transferId); - if (transfer && chunkIndex < transfer.acked.length) { - transfer.acked[chunkIndex] = true; - console.log(`✅ acknowledgeChunk: Chunk ${chunkIndex} acknowledged for transfer ${transferId}`); - } - - // Store acknowledgment in global backup - global.chunkAcknowledgments = global.chunkAcknowledgments || {}; - global.chunkAcknowledgments[transferId] = global.chunkAcknowledgments[transferId] || new Set(); - global.chunkAcknowledgments[transferId].add(chunkIndex); - } - - wasChunked(transferId) { - return this.transfers.has(transferId) || - this.completedTransfers.has(transferId) || - this.emergencyBackup.has(transferId) || - this.globalTransferRefs.has(transferId); - } - - getTransferStats(transferId) { - const transfer = this.transfers.get(transferId) || this.completedTransfers.get(transferId); - if (!transfer) return null; - - const completed = transfer.acked.filter(ack => ack === true).length; - const total = transfer.chunks.length; - const duration = Date.now() - transfer.startTime; - - return { completed, total, duration }; - } - - setAssembledHtml(transferId, html) { - this.assembledHtmls.set(transferId, html); - console.log(`💾 Stored assembled HTML for transfer ${transferId} (${html.length} chars)`); - } - - getAssembledHtml(transferId) { - return this.assembledHtmls.get(transferId) || null; - } - - getAssembledData(transferId) { - const transfer = this.transfers.get(transferId); - if (!transfer) { - console.warn(`❌ Transfer ${transferId} not found`); - return ''; - } - - const completed = transfer.acked.filter(ack => ack === true).length; - const total = transfer.chunks.length; - - if (completed !== total) { - throw new Error(`Transfer ${transferId} not complete: ${completed}/${total} chunks acknowledged`); - } - - return transfer.chunks.join(''); - } - - completeTransfer(transferId) { - const transfer = this.transfers.get(transferId); - if (!transfer) return false; - - this.completedTransfers.set(transferId, transfer); - this.transfers.delete(transferId); - - console.log(`🔄 COMPLETING transfer ${transferId} with multi-layer storage update`); - return true; - } - - async cleanup() { - // Mock cleanup - console.log('🧹 Cleanup completed'); - } -} - -// Test utilities -let testsRun = 0; -let testsPassed = 0; -let testsFailed = 0; - -function test(name, fn) { - testsRun++; - try { - const result = fn(); - if (result && typeof result.then === 'function') { - return result.then(() => { - testsPassed++; - console.log(`✅ ${name}`); - }).catch(error => { - testsFailed++; - console.log(`❌ ${name}: ${error.message}`); - }); - } else { - testsPassed++; - console.log(`✅ ${name}`); - } - } catch (error) { - testsFailed++; - console.log(`❌ ${name}: ${error.message}`); - } -} - -function expect(value) { - return { - toBe: (expected) => { - if (value !== expected) { - throw new Error(`Expected ${expected}, but got ${value}`); - } - }, - toBeDefined: () => { - if (value === undefined) { - throw new Error(`Expected value to be defined, but got ${value}`); - } - }, - toBeGreaterThan: (expected) => { - if (value <= expected) { - throw new Error(`Expected ${value} to be greater than ${expected}`); - } - }, - toBeLessThan: (expected) => { - if (value >= expected) { - throw new Error(`Expected ${value} to be less than ${expected}`); - } - } - }; -} - -// Test suite -async function runTests() { - console.log('\n📋 Тесты создания transfer в multi-layer storage'); - - const chunkManager = new MockEnhancedChunkManager(); - - // Test 1: Multi-layer storage creation - await test('✅ Создание transfer в multi-layer storage', async () => { - const testHtml = 'Test HTML content for chunking'; - const transferId = 'test-transfer-1'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем, что transfer создан во всех слоях хранения - expect(chunkManager.transfers.has(transferId)).toBe(true); - expect(chunkManager.globalTransferRefs.has(transferId)).toBe(true); - expect(chunkManager.emergencyBackup.has(transferId)).toBe(true); - expect(global.emergencyTransfers[transferId]).toBeDefined(); - expect(global.fixedTransfers.some(t => t.id === transferId)).toBe(true); - - console.log('✅ Multi-layer storage verification passed'); - }); - - // Test 2: Acknowledgment processing - await test('✅ Acknowledgment processing без race conditions', async () => { - const testHtml = 'Test'; - const transferId = 'test-ack-transfer'; - - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - const totalChunks = transfer.chunks.length; - - // Имитируем acknowledgments для всех чанков - for (let i = 0; i < totalChunks; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Проверяем, что все чанки acknowledged - const stats = chunkManager.getTransferStats(transferId); - expect(stats.completed).toBe(stats.total); - - // Проверяем, что acknowledgments сохранены в global backup - expect(global.chunkAcknowledgments[transferId]).toBeDefined(); - expect(global.chunkAcknowledgments[transferId].size).toBe(totalChunks); - - console.log('✅ Acknowledgment processing passed'); - }); - - // Test 3: HTML assembly - await test('✅ Обработка HTML_ASSEMBLED', async () => { - const testHtml = 'Assembled HTML content'; - const transferId = 'test-assembled-transfer'; - - // Создаем transfer и подтверждаем все чанки - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - for (let i = 0; i < transfer.chunks.length; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Имитируем получение HTML_ASSEMBLED - chunkManager.setAssembledHtml(transferId, testHtml); - const completed = chunkManager.completeTransfer(transferId); - - expect(completed).toBe(true); - expect(chunkManager.completedTransfers.has(transferId)).toBe(true); - expect(chunkManager.transfers.has(transferId)).toBe(false); - expect(chunkManager.getAssembledHtml(transferId)).toBe(testHtml); - - console.log('✅ HTML_ASSEMBLED processing passed'); - }); - - // Test 4: Recovery system - await test('✅ Работа fallback recovery системы', async () => { - const transferId = 'test-recovery-transfer'; - - // Имитируем потерю transfer (удаляем из основной памяти) - chunkManager.transfers.delete(transferId); - chunkManager.completedTransfers.delete(transferId); - - expect(chunkManager.wasChunked(transferId)).toBe(false); - - // Запускаем recovery - const recoveryResult = await chunkManager.recoveryManager.recoverTransfer(transferId); - - expect(recoveryResult.success).toBe(true); - expect(recoveryResult.transfer).toBeDefined(); - expect(recoveryResult.html).toBeDefined(); - expect(recoveryResult.strategy).toBe('mock_recovery'); - - console.log('✅ Recovery system passed'); - }); - - // Test 5: Multi-layer search - await test('✅ Multi-layer search functionality', async () => { - const testHtml = 'Multi-layer test'; - const transferId = 'test-multi-layer'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем поиск во всех слоях - expect(chunkManager.wasChunked(transferId)).toBe(true); - - // Имитируем повреждение основного слоя - chunkManager.transfers.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в других слоях - - // Имитируем повреждение всех слоев кроме emergency - chunkManager.completedTransfers.delete(transferId); - chunkManager.globalTransferRefs.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в emergency backup - - console.log('✅ Multi-layer search functionality passed'); - }); - - // Test 6: Circuit breaker - test('✅ Circuit breaker protection', () => { - const circuitBreaker = new MockCircuitBreaker(); - - // Проверяем начальное состояние - expect(circuitBreaker.getState()).toBe('CLOSED'); - - // Имитируем успешную операцию - const result = circuitBreaker.execute(async () => { - return 'success'; - }); - - expect(result).toBeDefined(); - - console.log('✅ Circuit breaker protection passed'); - }); - - // Test 7: Persistence - await test('✅ Transfer cleanup и persistence', async () => { - const testHtml = 'Cleanup test'; - const transferId = 'test-cleanup'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем сохранение в persistence - const persisted = await chunkManager.persistenceManager.load(transferId); - expect(persisted).toBeDefined(); - expect(persisted.transferId).toBe(transferId); - expect(persisted.status).toBe('active'); - - // Выполняем cleanup - await chunkManager.cleanup(); - - console.log('✅ Transfer cleanup and persistence passed'); - }); - - // Summary - console.log('\n' + '='.repeat(60)); - console.log('🧪 TEST SUMMARY'); - console.log('='.repeat(60)); - console.log(`✅ Passed: ${testsPassed}`); - console.log(`❌ Failed: ${testsFailed}`); - console.log(`📊 Total: ${testsRun}`); - - if (testsFailed === 0) { - console.log('\n🎉 All tests passed!'); - console.log('✅ HTML chunking process is working correctly'); - console.log('✅ Multi-layer storage is functioning'); - console.log('✅ Recovery mechanisms are operational'); - console.log('✅ Race condition protection is active'); - } else { - console.log('\n⚠️ Some tests failed. Check the output above.'); - } -} - -// Run tests -runTests().catch(error => { - console.error('❌ Failed to run tests:', error); - process.exit(1); -}); \ No newline at end of file diff --git a/tools/E2E_TESTING_GUIDE.md b/tools/E2E_TESTING_GUIDE.md old mode 100644 new mode 100755 diff --git a/tools/dev_helper.sh b/tools/dev_helper.sh old mode 100644 new mode 100755 diff --git a/tools/generate-toc-and-crossrefs.cjs b/tools/generate-toc-and-crossrefs.cjs old mode 100644 new mode 100755 diff --git a/tools/ozon-analyzer-e2e-metrics-collector.js b/tools/ozon-analyzer-e2e-metrics-collector.js old mode 100644 new mode 100755 diff --git a/tools/safe-delete.js b/tools/safe-delete.js old mode 100644 new mode 100755 diff --git a/tools/sync_plans.py b/tools/sync_plans.py old mode 100644 new mode 100755 diff --git a/tools/test-scenarios.js b/tools/test-scenarios.js old mode 100644 new mode 100755 diff --git a/tsconfig.json b/tsconfig.json old mode 100644 new mode 100755 diff --git a/turbo.json b/turbo.json old mode 100644 new mode 100755 diff --git a/types/vite-plugin-node-polyfills.d.ts b/types/vite-plugin-node-polyfills.d.ts old mode 100644 new mode 100755 diff --git a/ui/PluginCard.css b/ui/PluginCard.css old mode 100644 new mode 100755 diff --git a/ui/PluginCard.js b/ui/PluginCard.js old mode 100644 new mode 100755 diff --git a/ui/log-manager.js b/ui/log-manager.js old mode 100644 new mode 100755 diff --git a/ui/test-harness.js b/ui/test-harness.js old mode 100644 new mode 100755 diff --git a/vite.config.mts b/vite.config.mts old mode 100644 new mode 100755 From 2cebc89174024bc2eea6b5fc9ace74718f9ed8e0 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Wed, 22 Oct 2025 04:55:26 +0500 Subject: [PATCH 09/15] =?UTF-8?q?=D0=B2=D0=BE=D1=81=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BD=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=9E=D0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- chrome-extension/public/pyodide/package.json | 2 +- .../side-panel/assets/index-B8ToW130.js | 50 + .../side-panel/assets/index-B8ToW130.js.map | 1 + .../side-panel/assets/index-D46GpuPf.js | 50 - .../side-panel/assets/index-D46GpuPf.js.map | 1 - .../side-panel/assets/index-DTNx5ty5.css | 1 - .../side-panel/assets/index-U9brfXjD.css | 1 + chrome-extension/public/side-panel/index.html | 4 +- chrome-extension/src/background/package.json | 2 +- package.json | 3 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 3 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 3 +- packages/vite-config/package.json | 2 +- packages/zipper/lib/zip-bundle.ts | 2 +- packages/zipper/package.json | 6 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- pnpm-lock.yaml | 4166 ++++------------- public/pyodide/package.json | 2 +- 35 files changed, 909 insertions(+), 3428 deletions(-) create mode 100644 chrome-extension/public/side-panel/assets/index-B8ToW130.js create mode 100644 chrome-extension/public/side-panel/assets/index-B8ToW130.js.map delete mode 100755 chrome-extension/public/side-panel/assets/index-D46GpuPf.js delete mode 100755 chrome-extension/public/side-panel/assets/index-D46GpuPf.js.map delete mode 100755 chrome-extension/public/side-panel/assets/index-DTNx5ty5.css create mode 100644 chrome-extension/public/side-panel/assets/index-U9brfXjD.css mode change 100755 => 100644 chrome-extension/public/side-panel/index.html mode change 100755 => 100644 pnpm-lock.yaml diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 196f7646..6c32d54a 100755 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1381", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1381", + "version": "1.0.1394", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1394", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 26827c22..7e10fed9 100755 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index 860178b5..b15317b7 100755 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1406", + "version": "0.27.1419", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-B8ToW130.js b/chrome-extension/public/side-panel/assets/index-B8ToW130.js new file mode 100644 index 00000000..ff5015c7 --- /dev/null +++ b/chrome-extension/public/side-panel/assets/index-B8ToW130.js @@ -0,0 +1,50 @@ +(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))c(S);new MutationObserver(S=>{for(const C of S)if(C.type==="childList")for(const D of C.addedNodes)D.tagName==="LINK"&&D.rel==="modulepreload"&&c(D)}).observe(document,{childList:!0,subtree:!0});function f(S){const C={};return S.integrity&&(C.integrity=S.integrity),S.referrerPolicy&&(C.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?C.credentials="include":S.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function c(S){if(S.ep)return;S.ep=!0;const C=f(S);fetch(S.href,C)}})();var ds=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function lh(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Do={exports:{}},Va={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Wd;function nh(){if(Wd)return Va;Wd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function f(c,S,C){var D=null;if(C!==void 0&&(D=""+C),S.key!==void 0&&(D=""+S.key),"key"in S){C={};for(var F in S)F!=="key"&&(C[F]=S[F])}else C=S;return S=C.ref,{$$typeof:u,type:c,key:D,ref:S!==void 0?S:null,props:C}}return Va.Fragment=r,Va.jsx=f,Va.jsxs=f,Va}var Fd;function ah(){return Fd||(Fd=1,Do.exports=nh()),Do.exports}var y=ah();const ih=({isDraftSaved:u,isDraftLoading:r,draftError:f,messageLength:c,minLength:S,maxLength:C})=>{const D=()=>r?y.jsx("div",{className:"draft-status-loader"}):f?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):u?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cr?"Загрузка черновика...":f||(u?"Черновик сохранен":c>0&&c=S&&c<=C?"Черновик будет сохранен автоматически":c>C?"Превышен лимит символов":""),U=()=>r?"draft-status loading":f?"draft-status error":u?"draft-status saved":c>0&&c=S&&c<=C?"draft-status ready":c>C?"draft-status error":"draft-status";return c===0&&!r&&!f?null:y.jsxs("div",{className:U(),children:[D(),y.jsx("span",{className:"draft-status-text",children:F()})]})};var Uo={exports:{}},be={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Id;function sh(){if(Id)return be;Id=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),f=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),C=Symbol.for("react.consumer"),D=Symbol.for("react.context"),F=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),q=Symbol.for("react.lazy"),X=Symbol.for("react.activity"),$=Symbol.iterator;function I(g){return g===null||typeof g!="object"?null:(g=$&&g[$]||g["@@iterator"],typeof g=="function"?g:null)}var W={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ae=Object.assign,ie={};function x(g,N,J){this.props=g,this.context=N,this.refs=ie,this.updater=J||W}x.prototype.isReactComponent={},x.prototype.setState=function(g,N){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,N,"setState")},x.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function K(){}K.prototype=x.prototype;function P(g,N,J){this.props=g,this.context=N,this.refs=ie,this.updater=J||W}var B=P.prototype=new K;B.constructor=P,ae(B,x.prototype),B.isPureReactComponent=!0;var te=Array.isArray;function ne(){}var A={H:null,A:null,T:null,S:null},O=Object.prototype.hasOwnProperty;function le(g,N,J){var ee=J.ref;return{$$typeof:u,type:g,key:N,ref:ee!==void 0?ee:null,props:J}}function ye(g,N){return le(g.type,N,g.props)}function ze(g){return typeof g=="object"&&g!==null&&g.$$typeof===u}function he(g){var N={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(J){return N[J]})}var Re=/\/+/g;function et(g,N){return typeof g=="object"&&g!==null&&g.key!=null?he(""+g.key):N.toString(36)}function Pe(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(ne,ne):(g.status="pending",g.then(function(N){g.status==="pending"&&(g.status="fulfilled",g.value=N)},function(N){g.status==="pending"&&(g.status="rejected",g.reason=N)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function z(g,N,J,ee,re){var pe=typeof g;(pe==="undefined"||pe==="boolean")&&(g=null);var ge=!1;if(g===null)ge=!0;else switch(pe){case"bigint":case"string":case"number":ge=!0;break;case"object":switch(g.$$typeof){case u:case r:ge=!0;break;case q:return ge=g._init,z(ge(g._payload),N,J,ee,re)}}if(ge)return re=re(g),ge=ee===""?"."+et(g,0):ee,te(re)?(J="",ge!=null&&(J=ge.replace(Re,"$&/")+"/"),z(re,N,J,"",function(pt){return pt})):re!=null&&(ze(re)&&(re=ye(re,J+(re.key==null||g&&g.key===re.key?"":(""+re.key).replace(Re,"$&/")+"/")+ge)),N.push(re)),1;ge=0;var Je=ee===""?".":ee+":";if(te(g))for(var Ge=0;Ge>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},oh="Platform Settings",ch="Available plugins",rh="AI API Keys",fh="Free",dh="Custom API keys",gh="+ Add new key",mh="Enter API key",yh="Enter key name",hh="Save all keys",ph="Test connections",vh="General Settings",bh="Automatic plugin updates",Sh="Show notifications",_h="Interface theme:",Eh="Light",xh="Dark",Ah="System",Th="Security",Ch="Verify plugin signatures",zh="Isolated execution mode",Mh="Performance",Oh="Maximum number of active plugins:",Dh="Cache plugin data",Uh="Plugin Details",jh={options:uh,options_settings_title:oh,options_plugins_title:ch,options_settings_aiKeys_title:rh,options_settings_aiKeys_badges_free:fh,options_settings_aiKeys_customKeys_title:dh,options_settings_aiKeys_customKeys_addButton:gh,options_settings_aiKeys_customKeys_keyPlaceholder:mh,options_settings_aiKeys_customKeys_namePlaceholder:yh,options_settings_aiKeys_actions_save:hh,options_settings_aiKeys_actions_test:ph,options_settings_general_title:vh,options_settings_general_autoUpdate:bh,options_settings_general_showNotifications:Sh,options_settings_general_theme:_h,options_settings_general_theme_light:Eh,options_settings_general_theme_dark:xh,options_settings_general_theme_system:Ah,options_settings_security_title:Th,options_settings_security_checkSignatures:Ch,options_settings_security_isolatedMode:zh,options_settings_performance_title:Mh,options_settings_performance_maxPlugins:Oh,options_settings_performance_cacheData:Dh,options_plugins_details_title:Uh},wh={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",basic_analysis:"Базовый",deep_analysis:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},Nh="Настройки Платформы",Rh="Доступные плагины",Hh="API Ключи нейросетей",Gh="Бесплатный",Kh="Пользовательские API ключи",Bh="+ Добавить новый ключ",Lh="Введите API ключ",qh="Введите название ключа",Yh="Сохранить все ключи",Ph="Тестировать подключения",Xh="Общие настройки",Qh="Автоматическое обновление плагинов",Vh="Показывать уведомления",Zh="Тема интерфейса:",kh="Светлая",Jh="Тёмная",$h="Системная",Wh="Безопасность",Fh="Проверять подписи плагинов",Ih="Изолированный режим выполнения",ep="Производительность",tp="Максимальное количество активных плагинов:",lp="Кэширование данных плагинов",np="Детали плагина",ap={options:wh,options_settings_title:Nh,options_plugins_title:Rh,options_settings_aiKeys_title:Hh,options_settings_aiKeys_badges_free:Gh,options_settings_aiKeys_customKeys_title:Kh,options_settings_aiKeys_customKeys_addButton:Bh,options_settings_aiKeys_customKeys_keyPlaceholder:Lh,options_settings_aiKeys_customKeys_namePlaceholder:qh,options_settings_aiKeys_actions_save:Yh,options_settings_aiKeys_actions_test:Ph,options_settings_general_title:Xh,options_settings_general_autoUpdate:Qh,options_settings_general_showNotifications:Vh,options_settings_general_theme:Zh,options_settings_general_theme_light:kh,options_settings_general_theme_dark:Jh,options_settings_general_theme_system:$h,options_settings_security_title:Wh,options_settings_security_checkSignatures:Fh,options_settings_security_isolatedMode:Ih,options_settings_performance_title:ep,options_settings_performance_maxPlugins:tp,options_settings_performance_cacheData:lp,options_plugins_details_title:np},tg={en:jh,ru:ap},_g=(u="en")=>({t:V.useMemo(()=>{const f=tg[u]||{},c=(S,C)=>C.split(".").reduce((D,F)=>D&&D[F]?D[F]:void 0,S);return S=>{const C=c(f,S);return C!==void 0?C:S}},[u,tg]),locale:u}),jo=({checked:u,onChange:r,disabled:f,label:c,iconOn:S,iconOff:C})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:f?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:u,disabled:f,onChange:D=>r(D.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:u?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:u?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:u?S:C})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),ip=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),sp=({children:u})=>{const[r,f]=hs.useState(null),c=hs.useCallback(()=>f(null),[]);if(r)return y.jsx(ip,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return f(S),null}};class wo{static ALGORITHM="AES-GCM";static KEY_LENGTH=256;static IV_LENGTH=12;static async getEncryptionKey(){try{const r=await chrome.storage.local.get(["encryptionKey"]);if(r.encryptionKey){const C=new Uint8Array(r.encryptionKey);return await crypto.subtle.importKey("raw",C,this.ALGORITHM,!1,["encrypt","decrypt"])}const f=await crypto.subtle.generateKey({name:this.ALGORITHM,length:this.KEY_LENGTH},!0,["encrypt","decrypt"]),c=await crypto.subtle.exportKey("raw",f),S=new Uint8Array(c);return await chrome.storage.local.set({encryptionKey:Array.from(S)}),f}catch(r){throw console.error("Failed to get/create encryption key:",r),new Error("Не удалось инициализировать шифрование")}}static async encrypt(r){try{const f=await this.getEncryptionKey(),c=crypto.getRandomValues(new Uint8Array(this.IV_LENGTH)),S=new TextEncoder().encode(r),C=await crypto.subtle.encrypt({name:this.ALGORITHM,iv:c},f,S),D=new Uint8Array(C),F=new Uint8Array(c.length+D.length);return F.set(c),F.set(D,c.length),btoa(String.fromCharCode(...F))}catch(f){throw console.error("Encryption failed:",f),new Error("Ошибка шифрования")}}static async decrypt(r){try{const f=await this.getEncryptionKey(),c=new Uint8Array(atob(r).split("").map(F=>F.charCodeAt(0))),S=c.slice(0,this.IV_LENGTH),C=c.slice(this.IV_LENGTH),D=await crypto.subtle.decrypt({name:this.ALGORITHM,iv:S},f,C);return new TextDecoder().decode(D)}catch(f){throw console.error("Decryption failed:",f),new Error("Ошибка расшифрования")}}static validateAPIKey(r){return!r||typeof r!="string"?{isValid:!1,error:"Ключ не может быть пустым"}:r.length<10?{isValid:!1,error:"Ключ слишком короткий"}:r.length>200?{isValid:!1,error:"Ключ слишком длинный"}:/[<>\"'&]/.test(r)?{isValid:!1,error:"Ключ содержит недопустимые символы"}:{isValid:!0}}}class yt{static async saveEncryptedKey(r,f){try{const c=wo.validateAPIKey(f);if(!c.isValid)throw new Error(c.error);const S=await wo.encrypt(f),C=await this.getAllEncryptedKeys();C[r]=S,await chrome.storage.local.set({encryptedApiKeys:C})}catch(c){throw console.error("Failed to save encrypted API key:",c),c}}static async getDecryptedKey(r){try{const c=(await this.getAllEncryptedKeys())[r];return c?await wo.decrypt(c):null}catch(f){return console.error("Failed to get decrypted API key:",f),null}}static async removeKey(r){try{const f=await this.getAllEncryptedKeys();delete f[r],await chrome.storage.local.set({encryptedApiKeys:f})}catch(f){throw console.error("Failed to remove API key:",f),f}}static async getAllEncryptedKeys(){try{return(await chrome.storage.local.get(["encryptedApiKeys"])).encryptedApiKeys||{}}catch(r){return console.error("Failed to get encrypted keys:",r),{}}}static async getAllKeyIds(){try{const r=await this.getAllEncryptedKeys();return Object.keys(r)}catch(r){return console.error("Failed to get key IDs:",r),[]}}static async keyExists(r){try{const f=await this.getAllEncryptedKeys();return r in f}catch(f){return console.error("Failed to check key existence:",f),!1}}}const No="plugin-ozon-analyzer-settings",hl={basic_analysis:{ru:{llm:"",custom_prompt:"Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки."},en:{llm:"",custom_prompt:"Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages."}},deep_analysis:{ru:{llm:"",custom_prompt:"Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению."},en:{llm:"",custom_prompt:"Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations."}},api_keys:{default:""}},Eg=()=>{const[u,r]=V.useState(hl),[f,c]=V.useState(!0);V.useEffect(()=>{S()},[]);const S=async()=>{try{c(!0);const W=(await chrome.storage.local.get([No]))[No];if(W){const ae={...W.api_keys};W.api_keys?.default&&(ae.default=await yt.getDecryptedKey("ozon-analyzer-default")||""),r({...hl,...W,basic_analysis:{ru:{...hl.basic_analysis.ru,...W.basic_analysis?.ru},en:{...hl.basic_analysis.en,...W.basic_analysis?.en}},deep_analysis:{ru:{...hl.deep_analysis.ru,...W.deep_analysis?.ru},en:{...hl.deep_analysis.en,...W.deep_analysis?.en}},api_keys:ae})}else r(hl)}catch(I){console.error("Failed to load plugin settings:",I),r(hl)}finally{c(!1)}},C=async I=>{try{const W={...I};I.api_keys?.default?await yt.saveEncryptedKey("ozon-analyzer-default",I.api_keys.default):await yt.removeKey("ozon-analyzer-default"),W.api_keys={default:""},await chrome.storage.local.set({[No]:W}),r(I)}catch(W){throw console.error("Failed to save plugin settings:",W),W}};return{settings:u,isLoading:f,updateBasicAnalysisSettings:async(I,W)=>{const ae={...u,basic_analysis:{...u.basic_analysis,[I]:W}};await C(ae)},updateDeepAnalysisSettings:async(I,W)=>{const ae={...u,deep_analysis:{...u.deep_analysis,[I]:W}};await C(ae)},updateAPIKey:async I=>{const W={...u,api_keys:{default:I}};await C(W)},getBasicAnalysisSettings:I=>u.basic_analysis[I],getDeepAnalysisSettings:I=>u.deep_analysis[I],getAPIKey:()=>u.api_keys?.default||"",resetToDefaults:async()=>{await C(hl)},saveSettings:C,loadSettings:S}},up=({promptType:u,language:r,globalAIKeys:f,defaultLLMCurl:c,hasDefaultLLM:S,onLLMChange:C})=>{const{settings:D,updateBasicAnalysisSettings:F,updateDeepAnalysisSettings:U}=Eg(),[v,q]=V.useState(c),[X,$]=V.useState(""),I=V.useRef();V.useEffect(()=>{(async()=>{const K=`ozon-analyzer-${u}-${r}`,P=await yt.getDecryptedKey(K)||"";$(P)})()},[u,r]),V.useEffect(()=>{const x=u==="basic_analysis"?D.basic_analysis[r]:D.deep_analysis[r];let K="";x?K=x.llm||(S?"default":""):K=S?"default":"",q(K)},[u,r,D,S]);const W=async x=>{q(x),await(u==="basic_analysis"?F:U)(r,{llm:x,custom_prompt:u==="basic_analysis"?D.basic_analysis[r].custom_prompt:D.deep_analysis[r].custom_prompt}),C(x,x==="default"?X:void 0)},ae=x=>{$(x),I.current&&clearTimeout(I.current),I.current=setTimeout(async()=>{try{const K=`ozon-analyzer-${u}-${r}`;await yt.saveEncryptedKey(K,x),C(v,x)}catch(K){console.error("Failed to save API key:",K)}},500)},ie=()=>[{value:"default",label:"Default LLM"},...f.map(K=>({value:K.id,label:K.name}))];return y.jsxs("div",{style:{marginBottom:"16px"},children:[y.jsxs("label",{style:{display:"block",marginBottom:"8px",fontWeight:"bold"},children:["Выбор нейросети для ",u==="basic_analysis"?"базового":"глубокого"," анализа:"]}),y.jsx("select",{value:v,onChange:x=>W(x.target.value),placeholder:"Выберите нейросеть",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",marginBottom:"8px"},children:ie().map(x=>y.jsx("option",{value:x.value,children:x.label},x.value))}),v==="default"&&y.jsxs("div",{children:[y.jsx("label",{style:{display:"block",marginBottom:"4px",fontSize:"14px"},children:"API ключ:"}),y.jsx("input",{type:"password",value:X,onChange:x=>ae(x.target.value),placeholder:"Введите API ключ",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})]})},op=()=>{const{t:u}=_g("ru"),[r,f]=V.useState([{id:"gemini-flash-lite",name:"Google Gemini Flash Lite - Базовый анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0},{id:"gemini-pro",name:"Gemini 2.5 Pro - Глубокий анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0}]),[c,S]=V.useState([]),C=V.useRef({});V.useEffect(()=>(D(),()=>{Object.values(C.current).forEach(x=>{clearTimeout(x)}),C.current={}}),[]);const D=async()=>{try{console.log("[useAIKeys] Starting to load AI keys...");const K=["gemini-flash-lite","gemini-pro"].map(async te=>{const ne=await yt.getDecryptedKey(te);return console.log(`[useAIKeys] Loaded key ${te}:`,ne?"present":"empty"),{keyId:te,decryptedKey:ne}}),P=await Promise.all(K);console.log("[useAIKeys] Fixed keys results:",P),f(te=>te.map(ne=>{const A=P.find(O=>O.keyId===ne.id);return console.log(`[useAIKeys] Setting key ${ne.id}:`,A?.decryptedKey?"configured":"not_configured"),{...ne,key:A?.decryptedKey||"",status:A?.decryptedKey?"configured":"not_configured"}}));const B=await chrome.storage.local.get(["customKeys"]);if(console.log("[useAIKeys] Custom keys metadata:",B.customKeys),B.customKeys){const te=await Promise.all(B.customKeys.map(async ne=>{const A=await yt.getDecryptedKey(ne.id);return console.log(`[useAIKeys] Custom key ${ne.id}:`,A?"present":"empty"),{...ne,key:A||"",status:A?"configured":"not_configured"}}));console.log("[useAIKeys] Setting custom keys:",te),S(te)}else console.log("[useAIKeys] No custom keys found"),S([]);console.log("[useAIKeys] AI keys loaded successfully")}catch(x){console.error("Failed to load AI keys:",x),f(K=>K.map(P=>({...P,key:"",status:"not_configured"}))),S([])}};return{aiKeys:r,customKeys:c,saveAIKeys:async()=>{try{console.log("[useAIKeys] Starting to save AI keys..."),console.log("[useAIKeys] Current aiKeys:",r),console.log("[useAIKeys] Current customKeys:",c);const x=r.map(async B=>{B.key?(console.log(`[useAIKeys] Saving fixed key ${B.id}`),await yt.saveEncryptedKey(B.id,B.key)):(console.log(`[useAIKeys] Removing fixed key ${B.id}`),await yt.removeKey(B.id))});await Promise.all(x);const K=c.map(async B=>{B.key?(console.log(`[useAIKeys] Saving custom key ${B.id}`),await yt.saveEncryptedKey(B.id,B.key)):(console.log(`[useAIKeys] Removing custom key ${B.id}`),await yt.removeKey(B.id))});await Promise.all(K);const P=c.map(B=>({id:B.id,name:B.name,isFixed:!1,isFree:!1}));console.log("[useAIKeys] Saving custom keys metadata:",P),await chrome.storage.local.set({customKeys:P}),f(B=>B.map(te=>({...te,status:te.key?"configured":"not_configured"}))),S(B=>B.map(te=>({...te,status:te.key?"configured":"not_configured"}))),console.log("[useAIKeys] AI keys saved successfully"),alert(u("options.settings.aiKeys.messages.saved"))}catch(x){console.error("Failed to save AI keys:",x),alert(u("options.settings.aiKeys.messages.saveError"))}},testAIKeys:async()=>{f(x=>x.map(K=>({...K,status:"testing"}))),S(x=>x.map(K=>({...K,status:"testing"}))),setTimeout(()=>{f(x=>x.map(K=>({...K,status:K.key?"configured":"not_configured"}))),S(x=>x.map(K=>({...K,status:K.key?"configured":"not_configured"}))),alert(u("options.settings.aiKeys.messages.testComplete"))},2e3)},addCustomKey:()=>{const x={id:`custom-${Date.now()}`,name:`Пользовательский ключ ${c.length+1}`,key:"",status:"not_configured"};S(K=>[...K,x])},removeCustomKey:async x=>{try{await yt.removeKey(x),S(K=>K.filter(P=>P.id!==x))}catch(K){console.error("Failed to remove custom key:",K),S(P=>P.filter(B=>B.id!==x))}},updateKey:(x,K,P=!1)=>{console.log(`[useAIKeys] Updating key ${x} with value:`,K?"present":"empty"),P?S(B=>B.map(te=>te.id===x?{...te,key:K,status:K?"configured":"not_configured"}:te)):f(B=>B.map(te=>te.id===x?{...te,key:K,status:K?"configured":"not_configured"}:te)),C.current[x]&&clearTimeout(C.current[x]),C.current[x]=setTimeout(async()=>{try{if(console.log(`[useAIKeys] Auto-saving key ${x} after delay`),K?await yt.saveEncryptedKey(x,K):await yt.removeKey(x),P){const te=(await chrome.storage.local.get(["customKeys"])).customKeys||[],ne=te.map(O=>O.id===x?{...O,name:O.name}:O);ne.findIndex(O=>O.id===x)===-1&&K&&ne.push({id:x,name:`Пользовательский ключ ${te.length+1}`,isFixed:!1,isFree:!1}),await chrome.storage.local.set({customKeys:ne.filter(O=>K||O.id!==x)})}console.log(`[useAIKeys] Key ${x} auto-saved successfully`)}catch(B){console.error(`[useAIKeys] Failed to auto-save key ${x}:`,B)}finally{delete C.current[x]}},1e3)},updateCustomKeyName:(x,K)=>{S(P=>P.map(B=>B.id===x?{...B,name:K,status:B.key?"configured":"not_configured"}:B))},getStatusText:x=>{switch(x){case"configured":return u("options.settings.aiKeys.status.configured");case"not_configured":return u("options.settings.aiKeys.status.notConfigured");case"testing":return u("options.settings.aiKeys.status.testing");default:return u("options.settings.aiKeys.status.notConfigured")}},getStatusClass:x=>{switch(x){case"configured":return"status-configured";case"not_configured":return"status-not-configured";case"testing":return"status-testing";default:return""}},getAPIKeyForMCP:async x=>{try{return await yt.getDecryptedKey(x)}catch(K){return console.error("Failed to get API key for MCP:",K),null}},isKeyConfigured:async x=>{try{return await yt.keyExists(x)}catch(K){return console.error("Failed to check if key is configured:",K),!1}}}},cp=(...u)=>u.filter(Boolean).join(" "),rp=({value:u,manifest:r,disabled:f,onSave:c,locale:S,t:C,globalAIKeys:D,pluginSettings:F})=>{const[U,v]=V.useState("basic_analysis"),[q,X]=V.useState("ru"),[$,I]=V.useState(""),W=(B,te)=>{try{const ne=r?.options?.prompts;return!ne||!((ne[B]||{})[te]||{}).LLM?.default?"default":B==="basic_analysis"?"gemini-flash-lite":B==="deep_analysis"?"gemini-pro":"default"}catch{return"default"}},ae=(B,te)=>{try{const ne=r?.options?.prompts;return ne?!!((ne[B]||{})[te]||{}).LLM?.default:!1}catch{return!1}},ie=()=>{try{const B=r?.options?.prompts;if(!B)return"";const A=((B[U]||{})[q]||{}).default||"";return typeof A=="object"?JSON.stringify(A,null,2):A}catch{return""}},x=()=>{try{const ne=((u||{})[U]||{})[q];return typeof ne=="string"?ne:ne==null?"":JSON.stringify(ne,null,2)}catch{return""}};V.useEffect(()=>{I(x())},[U,q,u]);const K=()=>{I(ie())},P=()=>{try{const B={...u};B[U]||(B[U]={ru:"",en:""}),B[U][q]=$,c(B)}catch(B){console.error("Failed to save custom prompt:",B)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:C("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:C("options.plugins.prompts.type")}),y.jsxs("select",{value:U,onChange:B=>v(B.target.value),disabled:f,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"basic_analysis",children:C("options.plugins.prompts.basic_analysis")}),y.jsx("option",{value:"deep_analysis",children:C("options.plugins.prompts.deep_analysis")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:C("options.plugins.prompts.language")}),y.jsxs("select",{value:q,onChange:B=>X(B.target.value),disabled:f,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:C("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:C("options.plugins.prompts.english")})]})]})]}),y.jsx(up,{promptType:U,language:q,globalAIKeys:D,defaultLLMCurl:W(U,q),hasDefaultLLM:ae(U,q),onLLMChange:(B,te)=>{console.log(`LLM changed for ${U} ${q}:`,B,te);const ne={...F};ne.selected_llms||(ne.selected_llms={}),ne.selected_llms[U]||(ne.selected_llms[U]={}),ne.selected_llms[U][q]=B,typeof window<"u"&&window.pyodide&&window.pyodide.globals&&(window.pyodide.globals.pluginSettings=ne)}}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:C("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:ie(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:K,disabled:f,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:f?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:C("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:C("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:$,onChange:B=>I(B.target.value),disabled:f,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:P,disabled:f,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:f?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:C("options.plugins.prompts.save")})})]})})},fp=u=>{const{selectedPlugin:r,locale:f="en",onUpdateSetting:c}=u,{t:S}=_g(f),{aiKeys:C}=op(),{settings:D}=Eg(),[F,U]=V.useState(null),[v,q]=V.useState(null),X=A=>A?typeof A=="string"?A:A[f]||A.ru||A.en||"":"";if(V.useEffect(()=>{r?.manifest?.options&&W()},[r?.id]),V.useEffect(()=>{if(D&&typeof window<"u"&&window.pyodide&&window.pyodide.globals){const A={basic_analysis:{ru:D.basic_analysis?.ru?.llm||"default",en:D.basic_analysis?.en?.llm||"default"},deep_analysis:{ru:D.deep_analysis?.ru?.llm||"default",en:D.deep_analysis?.en?.llm||"default"}},O={...window.pyodide.globals.pluginSettings||{},selected_llms:A};window.pyodide.globals.pluginSettings=O,console.log("Updated pluginSettings in pyodide.globals:",O)}},[D]),!r||typeof r!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:S("options_plugins_details_title")}),y.jsx("p",{children:S("options.plugins.details.selectPlugin")})]});const $=r.settings||{enabled:!0,autorun:!1},I=r.manifest?.host_permissions||[],W=async()=>{if(!(!r||!r.manifest?.options||v!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const O=Object.keys(r.manifest.options).map(ze=>`${r.id}_${ze}`),le=await chrome.storage.local.get(O),ye={};Object.entries(le).forEach(([ze,he])=>{const Re=ze.replace(`${r.id}_`,"");he!==void 0&&(ye[Re]=he)}),q(ye)}}catch(A){console.warn("Failed to load custom settings from chrome.storage.local:",A)}},ae=async(A,O)=>{if(r)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const le=`${r.id}_${A}`;await chrome.storage.local.set({[le]:O}),q(ye=>({...ye,[A]:O})),A==="prompts"&&await ie()}else console.warn("chrome.storage.local is not available")}catch(le){throw console.error(`Failed to save setting ${A} to chrome.storage.local:`,le),le}},ie=async()=>{if(r)try{const A=`${r.id}_prompts`,O=await chrome.storage.local.get([A]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${r.id}`),console.log(` Storage key: ${A}`),console.log(" Сохраненные промпты:",O[A]),O[A]){const le=O[A];console.log(" Структура промптов:"),console.log(` - basic_analysis.ru: ${le.basic_analysis?.ru?"✓":"✗"} (${le.basic_analysis?.ru?.length||0} символов)`),console.log(` - basic_analysis.en: ${le.basic_analysis?.en?"✓":"✗"} (${le.basic_analysis?.en?.length||0} символов)`),console.log(` - deep_analysis.ru: ${le.deep_analysis?.ru?"✓":"✗"} (${le.deep_analysis?.ru?.length||0} символов)`),console.log(` - deep_analysis.en: ${le.deep_analysis?.en?"✓":"✗"} (${le.deep_analysis?.en?.length||0} символов)`)}}catch(A){console.error("Ошибка диагностики промптов:",A)}},x=(A,O)=>{if(v&&v[A]!==void 0)return v[A];if(A==="prompts"&&typeof O=="object"&&O!==null){const le=O,ye={basic_analysis:{ru:{},en:{}},deep_analysis:{ru:{},en:{}}};return le.basic_analysis?.ru?.default&&(ye.basic_analysis.ru=le.basic_analysis.ru.default),le.basic_analysis?.en?.default&&(ye.basic_analysis.en=le.basic_analysis.en.default),le.deep_analysis?.ru?.default&&(ye.deep_analysis.ru=le.deep_analysis.ru.default),le.deep_analysis?.en?.default&&(ye.deep_analysis.en=le.deep_analysis.en.default),ye}return O},K=(A,O)=>{const le=x(A,O.default),ye=F===A||!($.enabled??!0),ze=X(O.label),he=X(O.description);if(A==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(rp,{value:le,manifest:r.manifest,disabled:ye,onSave:Re=>P(A,Re),locale:f,t:S,globalAIKeys:C,pluginSettings:D})},A);if(O.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:le,disabled:ye,onChange:Re=>P(A,Re),label:y.jsxs(y.Fragment,{children:[ze,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"})]})})},A);if(O.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"}),y.jsx("select",{id:A,name:A,value:le,disabled:ye,onChange:Re=>P(A,Re.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:O.values?.map(Re=>y.jsx("option",{value:Re,children:X(O.labels?.[Re])||Re},Re))})]})},A);if(O.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"text",id:A,name:A,value:le,disabled:ye,onChange:Re=>P(A,Re.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},A);if(O.type==="number"){const Re=et=>{const Pe=parseFloat(et.target.value);if(isNaN(Pe))return;let z=Pe;O.min!==void 0&&zO.max&&(z=O.max),P(A,z)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"number",id:A,name:A,value:le,disabled:ye,onChange:Re,min:O.min,max:O.max,step:O.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},A)}return null},P=async(A,O)=>{if(!r)return;const le=r.manifest?.options;if(le&&le[A]){v===null&&await W();try{U(A),await ae(A,O)}catch(ye){console.error(`Failed to update custom setting ${A}:`,ye)}finally{U(null)}}else if(c)try{U(A),await c(r.id,A,O)}catch(ye){console.error(`Failed to update setting ${A}:`,ye)}finally{U(null)}},te=Object.entries(r.manifest?.options??{}).map(([A,O])=>K(A,O)).filter(A=>A!==null),ne=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:$.enabled??!0,disabled:F==="enabled",onChange:A=>P("enabled",A),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:$.autorun??!1,disabled:F==="autorun"||!($.enabled??!0),onChange:A=>P("autorun",A),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(sp,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:r.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",r.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:cp("status-badge",$.enabled?"status-active":"status-inactive"),children:$.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",r.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",r.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:r.description})]}),I.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:I.map((A,O)=>y.jsx("li",{children:A},O))})]}),Array.isArray(r.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(r.manifest?.permissions??[]).map((A,O)=>y.jsx("li",{children:A},O))})]}):null,y.jsx(ne,{}),r.manifest?.options&&Object.keys(r.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),te]}):null]})]})})},dp=({plugin:u,onUpdateSetting:r})=>{const f=r?async(S,C,D)=>{try{return await r(S,C,D),!0}catch(F){return console.error(`Failed to update setting ${C}:`,F),!1}}:void 0,c={selectedPlugin:{...u,description:u.description||"Описание не указано",icon:u.icon||"",manifest:u.manifest||{}},locale:"ru",onUpdateSetting:f};return y.jsx(fp,{...c})},lg=function(u){if(!u)return"unknown-page";try{const r=new URL(u);return r.search="",r.hash="",r.toString()}catch{return u}},gp=({pluginId:u,pageKey:r,debounceMs:f=1e3})=>{const[c,S]=V.useState(""),[C,D]=V.useState(!1),[F,U]=V.useState(!1),[v,q]=V.useState(null),[X,$]=V.useState(""),I=V.useRef(null),W=V.useRef(""),ae=V.useCallback(async P=>{if(P!==W.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:u,pageKey:r,text:P});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:P}),W.current=P,D(!0),q(null),$(P),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:u,pageKey:r,text:P})}catch(B){console.error("[useLazyChatSync] Error saving draft:",B),q("Ошибка сохранения черновика"),D(!1)}}},[u,r]),ie=V.useCallback(async()=>{U(!0),q(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:u,pageKey:r});try{const P=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r});P?.draftText?(S(P.draftText),W.current=P.draftText,D(!0),$(P.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:u,pageKey:r,draft:P.draftText})):(S(""),W.current="",D(!1),$(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:u,pageKey:r}))}catch(P){console.error("[useLazyChatSync] Error loading draft:",P),q("Ошибка загрузки черновика")}finally{U(!1)}},[u,r]),x=V.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:u,pageKey:r});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:""}),W.current="",D(!1),q(null),$(""),S(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:u,pageKey:r})}catch(P){console.error("[useLazyChatSync] Error clearing draft:",P),q("Ошибка очистки черновика")}},[u,r]),K=V.useCallback(P=>{S(P),I.current&&clearTimeout(I.current),P.length===0?x():I.current=setTimeout(()=>{ae(P)},f),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:u,pageKey:r,text:P})},[f,ae,x,u,r]);return V.useEffect(()=>()=>{I.current&&clearTimeout(I.current)},[]),V.useEffect(()=>{ie()},[ie]),V.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",r),D(!1),U(!1),q(null),W.current="",I.current&&(clearTimeout(I.current),I.current=null),ie()},[r,ie]),{message:c,setMessage:K,isDraftSaved:C,isDraftLoading:F,draftError:v,loadDraft:ie,clearDraft:x,draftText:X}};var ys={exports:{}},mp=ys.exports,ng;function yp(){return ng||(ng=1,(function(u,r){(function(f,c){c()})(mp,function(){function f(v,q){return typeof q>"u"?q={autoBom:!1}:typeof q!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),q={autoBom:!q}),q.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(v.type)?new Blob(["\uFEFF",v],{type:v.type}):v}function c(v,q,X){var $=new XMLHttpRequest;$.open("GET",v),$.responseType="blob",$.onload=function(){U($.response,q,X)},$.onerror=function(){console.error("could not download file")},$.send()}function S(v){var q=new XMLHttpRequest;q.open("HEAD",v,!1);try{q.send()}catch{}return 200<=q.status&&299>=q.status}function C(v){try{v.dispatchEvent(new MouseEvent("click"))}catch{var q=document.createEvent("MouseEvents");q.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),v.dispatchEvent(q)}}var D=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof ds=="object"&&ds.global===ds?ds:void 0,F=D.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),U=D.saveAs||(typeof window!="object"||window!==D?function(){}:"download"in HTMLAnchorElement.prototype&&!F?function(v,q,X){var $=D.URL||D.webkitURL,I=document.createElement("a");q=q||v.name||"download",I.download=q,I.rel="noopener",typeof v=="string"?(I.href=v,I.origin===location.origin?C(I):S(I.href)?c(v,q,X):C(I,I.target="_blank")):(I.href=$.createObjectURL(v),setTimeout(function(){$.revokeObjectURL(I.href)},4e4),setTimeout(function(){C(I)},0))}:"msSaveOrOpenBlob"in navigator?function(v,q,X){if(q=q||v.name||"download",typeof v!="string")navigator.msSaveOrOpenBlob(f(v,X),q);else if(S(v))c(v,q,X);else{var $=document.createElement("a");$.href=v,$.target="_blank",setTimeout(function(){C($)})}}:function(v,q,X,$){if($=$||open("","_blank"),$&&($.document.title=$.document.body.innerText="downloading..."),typeof v=="string")return c(v,q,X);var I=v.type==="application/octet-stream",W=/constructor/i.test(D.HTMLElement)||D.safari,ae=/CriOS\/[\d]+/.test(navigator.userAgent);if((ae||I&&W||F)&&typeof FileReader<"u"){var ie=new FileReader;ie.onloadend=function(){var P=ie.result;P=ae?P:P.replace(/^data:[^;]*;/,"data:attachment/file;"),$?$.location.href=P:location=P,$=null},ie.readAsDataURL(v)}else{var x=D.URL||D.webkitURL,K=x.createObjectURL(v);$?$.location=K:location.href=K,$=null,setTimeout(function(){x.revokeObjectURL(K)},4e4)}});D.saveAs=U.saveAs=U,u.exports=U})})(ys)),ys.exports}var hp=yp(),Wn;(function(u){u.Local="local",u.Sync="sync",u.Managed="managed",u.Session="session"})(Wn||(Wn={}));var Qo;(function(u){u.ExtensionPagesOnly="TRUSTED_CONTEXTS",u.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(Qo||(Qo={}));const $n=globalThis.chrome,ag=async(u,r)=>{const f=S=>typeof S=="function",c=S=>S instanceof Promise;return f(u)?(c(u),u(r)):u};let ig=!1;const sg=u=>{if($n&&!$n.storage[u])throw new Error(`"storage" permission in manifest.ts: "storage ${u}" isn't defined`)},xg=(u,r,f)=>{let c=null,S=!1,C=[];const D=f?.storageEnum??Wn.Local,F=f?.liveUpdate??!1,U=f?.serialization?.serialize??(ie=>ie),v=f?.serialization?.deserialize??(ie=>ie);ig===!1&&D===Wn.Session&&f?.sessionAccessForContentScripts===!0&&(sg(D),$n?.storage[D].setAccessLevel({accessLevel:Qo.ExtensionPagesAndContentScripts}).catch(ie=>{console.error(ie),console.error("Please call .setAccessLevel() into different context, like a background script.")}),ig=!0);const q=async()=>{sg(D);const ie=await $n?.storage[D].get([u]);return ie?v(ie[u])??r:r},X=async ie=>{S||(c=await q()),c=await ag(ie,c),await $n?.storage[D].set({[u]:U(c)}),W()},$=ie=>(C=[...C,ie],()=>{C=C.filter(x=>x!==ie)}),I=()=>c,W=()=>{C.forEach(ie=>ie())},ae=async ie=>{if(ie[u]===void 0)return;const x=v(ie[u].newValue);c!==x&&(c=await ag(x,c),W())};return q().then(ie=>{c=ie,S=!0,W()}),F&&$n?.storage[D].onChanged.addListener(ae),{get:q,set:X,getSnapshot:I,subscribe:$}},ug=xg("theme-storage-key",{theme:"system",isLight:Ag()},{storageEnum:Wn.Local,liveUpdate:!0});function Ag(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Ro={...ug,toggle:async()=>{await ug.set(u=>{let r;switch(u.theme){case"light":r="dark";break;case"dark":r="system";break;case"system":default:r="light";break}const f=r==="system"?Ag():r==="light";return{theme:r,isLight:f}})}},Ho=xg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Wn.Local,liveUpdate:!0}),og={...Ho,setAlignment:async u=>{await Ho.set({alignment:u})},getAlignment:async()=>(await Ho.get()).alignment},pp=({plugin:u,currentView:r,isRunning:f,isPaused:c,currentTabUrl:S,onStart:C,onPause:D,onStop:F,onClose:U})=>{const[v,q]=V.useState("chat"),[X,$]=V.useState(lg(S)),[I,W]=V.useState("left");V.useEffect(()=>{const j=lg(S);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:X,newPageKey:j,currentTabUrl:S,timestamp:new Date().toISOString()}),$(j)},[S]),V.useEffect(()=>{const j=async()=>{const p=await og.getAlignment();W(p)};return j(),og.subscribe(()=>{j()})},[]);const{message:ae,setMessage:ie,isDraftSaved:x,isDraftLoading:K,draftError:P,loadDraft:B,clearDraft:te,draftText:ne}=gp({pluginId:u.id,pageKey:X,debounceMs:1e3}),[A,O]=V.useState([]),[le,ye]=V.useState(!1),[ze,he]=V.useState(null),[Re,et]=V.useState(60),[Pe,z]=V.useState(!1),k=V.useRef(null),Q=V.useRef(null);V.useEffect(()=>{},[f]);const Ae=()=>{C()},xe=u.name||(typeof u.manifest?.name=="string"?u.manifest.name:"")||u.id,g=u.id,N=V.useCallback(async j=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...j,messageId:h};try{return await chrome.runtime.sendMessage(p)}catch(Y){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",Y),Y}},[]),J=V.useCallback(j=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...j,messageId:h};chrome.runtime.sendMessage(p)},[]),ee=V.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const j={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},h={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},Y=R=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:R,hasMessages:R&&"messages"in R,hasChat:R&&"chat"in R,messagesValue:R?.messages,chatValue:R?.chat,isMessagesArray:Array.isArray(R?.messages),isChatArray:Array.isArray(R?.chat),responseType:typeof R,responseKeys:R?Object.keys(R):"response is null/undefined",timestamp:new Date().toISOString()}),R===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),O([]);return}let H=null;const Z=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],fe=(_e,ve)=>{let Ce=_e;for(const Xe of ve)if(Ce&&typeof Ce=="object"&&Xe in Ce)Ce=Ce[Xe];else return;return Ce};if(Array.isArray(R))H=R,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:H.length,firstMessage:H[0]?{id:H[0].id,content:H[0].content||H[0].text,role:H[0].role,timestamp:H[0].timestamp}:"no messages"});else if(R&&typeof R=="object"){if(R.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",R.error),he(`Ошибка от background: ${R.error}`),O([]);return}for(const{path:_e,description:ve}of Z){const Ce=fe(R,_e);if(Array.isArray(Ce)){H=Ce,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${ve}':`,{length:H.length,firstMessage:H[0]?{id:H[0].id,content:H[0].content||H[0].text,role:H[0].role,timestamp:H[0].timestamp}:"no messages"});break}}H||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof R,responseKeys:Object.keys(R),responseSample:JSON.stringify(R).substring(0,500),timestamp:new Date().toISOString()}),H=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:R,responseType:typeof R,responseStringified:JSON.stringify(R).substring(0,200),timestamp:new Date().toISOString()}),H=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:H,isArray:Array.isArray(H),length:H?.length,firstMessage:H?.[0],firstMessageType:H?.[0]?typeof H[0]:"none"}),Array.isArray(H)&&H.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",H.length);const _e=H.filter(ve=>!ve||typeof ve!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",ve),!1):!0).map((ve,Ce)=>{try{let Xe=ve.content||ve.text||"";typeof Xe=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",Xe),Xe=JSON.stringify(Xe)):Xe==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Xe=""):Xe=String(Xe);const Xl={id:ve.id||String(ve.timestamp||Date.now()+Ce),text:Xe,isUser:ve.role?ve.role==="user":!!ve.isUser,timestamp:ve.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${Ce}:`,{id:Xl.id,textLength:Xl.text.length,textType:typeof Xl.text,isUser:Xl.isUser}),Xl}catch(Xe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${Ce}:`,Xe,ve),{id:`error_${Date.now()}_${Ce}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:H.length,convertedCount:_e.length,firstConverted:_e[0],allConverted:_e.map(ve=>{try{const Ce=typeof ve.text=="string"?ve.text.substring(0,50):String(ve.text||"").substring(0,50);return{id:ve.id,text:Ce,isUser:ve.isUser,textType:typeof ve.text}}catch(Ce){return console.warn("[PluginControlPanel] Error processing message text:",Ce,ve),{id:ve.id,text:"[ERROR: invalid text]",isUser:ve.isUser,textType:typeof ve.text}}})}),O(_e)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),O([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),Y(j)}catch(R){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",R)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),Y(h)}catch(R){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",R)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),Y(p)}catch(R){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",R)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);V.useCallback(j=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:j,hasMessages:j&&"messages"in j,hasChat:j&&"chat"in j,messagesValue:j?.messages,chatValue:j?.chat,isMessagesArray:Array.isArray(j?.messages),isChatArray:Array.isArray(j?.chat),responseType:typeof j,responseKeys:j?Object.keys(j):"response is null/undefined",timestamp:new Date().toISOString()}),j===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),O([]);return}let h=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],Y=(R,H)=>{let Z=R;for(const fe of H)if(Z&&typeof Z=="object"&&fe in Z)Z=Z[fe];else return;return Z};if(Array.isArray(j))h=j,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});else if(j&&typeof j=="object"){if(j.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",j.error),he(`Ошибка от background: ${j.error}`),O([]);return}for(const{path:R,description:H}of p){const Z=Y(j,R);if(Array.isArray(Z)){h=Z,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${H}':`,{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});break}}h||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof j,responseKeys:Object.keys(j),responseSample:JSON.stringify(j).substring(0,500),timestamp:new Date().toISOString()}),h=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:j,responseType:typeof j,responseStringified:JSON.stringify(j).substring(0,200),timestamp:new Date().toISOString()}),h=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:h,isArray:Array.isArray(h),length:h?.length,firstMessage:h?.[0],firstMessageType:h?.[0]?typeof h[0]:"none"}),Array.isArray(h)&&h.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",h.length);const R=h.filter(H=>!H||typeof H!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",H),!1):!0).map((H,Z)=>{try{let fe=H.content||H.text||"",_e=H.timestamp||Date.now();if(typeof fe=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",fe),fe=JSON.stringify(fe);else if(fe==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),fe="";else{fe=String(fe),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${Z}:`,fe);try{const Ce=JSON.parse(fe);typeof Ce=="object"&&Ce!==null&&"content"in Ce&&(console.log("[PluginControlPanel] Распарсен JSON из content:",Ce),fe=String(Ce.content||""),Ce.timestamp&&typeof Ce.timestamp=="number"&&(_e=Ce.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${Z}:`,fe)}const ve={id:H.id||String(_e+Z),text:fe,isUser:H.role?H.role==="user":!!H.isUser,timestamp:_e};return console.log(`[PluginControlPanel] Конвертировано сообщение ${Z}:`,{id:ve.id,textLength:ve.text.length,textType:typeof ve.text,isUser:ve.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${Z}:`,ve.text),ve}catch(fe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${Z}:`,fe,H),{id:`error_${Date.now()}_${Z}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:h.length,convertedCount:R.length,firstConverted:R[0],allConverted:R.map(H=>{try{const Z=typeof H.text=="string"?H.text.substring(0,50):String(H.text||"").substring(0,50);return{id:H.id,text:Z,isUser:H.isUser,textType:typeof H.text}}catch(Z){return console.warn("[PluginControlPanel] Error processing message text:",Z,H),{id:H.id,text:"[ERROR: invalid text]",isUser:H.isUser,textType:typeof H.text}}})}),O(R)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),O([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const re=V.useCallback(async()=>{ye(!0),he(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:g,pageKey:X,currentTabUrl:S,timestamp:new Date().toISOString(),isRunning:f,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const j=await N({type:"GET_PLUGIN_CHAT",pluginId:g,pageKey:X});console.log("[PluginControlPanel] loadChat - получен ответ от background:",j),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:le,messagesCount:A.length,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),j?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",j.error),he(`Ошибка загрузки чата: ${j.error}`),O([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(h=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:h,hasMessages:h&&"messages"in h,hasChat:h&&"chat"in h,messagesValue:h?.messages,chatValue:h?.chat,isMessagesArray:Array.isArray(h?.messages),isChatArray:Array.isArray(h?.chat),responseType:typeof h,responseKeys:h?Object.keys(h):"response is null/undefined",timestamp:new Date().toISOString()}),h===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),O([]);return}let p=null;const Y=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],R=(H,Z)=>{let fe=H;for(const _e of Z)if(fe&&typeof fe=="object"&&_e in fe)fe=fe[_e];else return;return fe};if(Array.isArray(h))p=h,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(h&&typeof h=="object"){if(h.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",h.error),he(`Ошибка от background: ${h.error}`),O([]);return}for(const{path:H,description:Z}of Y){const fe=R(h,H);if(Array.isArray(fe)){p=fe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${Z}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof h,responseKeys:Object.keys(h),responseSample:JSON.stringify(h).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:h,responseType:typeof h,responseStringified:JSON.stringify(h).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const H=p.filter(Z=>!Z||typeof Z!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",Z),!1):!0).map((Z,fe)=>{try{let _e=Z.content||Z.text||"",ve=Z.timestamp||Date.now();if(typeof _e=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",_e),_e=JSON.stringify(_e);else if(_e==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),_e="";else{_e=String(_e);try{const Xe=JSON.parse(_e);typeof Xe=="object"&&Xe!==null&&"content"in Xe&&(console.log("[PluginControlPanel] Распаршен JSON из content:",Xe),_e=String(Xe.content||""),Xe.timestamp&&typeof Xe.timestamp=="number"&&(ve=Xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const Ce={id:Z.id||String(ve+fe),text:_e,isUser:Z.role?Z.role==="user":!!Z.isUser,timestamp:ve};return console.log(`[PluginControlPanel] Конвертировано сообщение ${fe}:`,{id:Ce.id,textLength:Ce.text.length,textType:typeof Ce.text,isUser:Ce.isUser}),Ce}catch(_e){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${fe}:`,_e,Z),{id:`error_${Date.now()}_${fe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:H.length,firstConverted:H[0],allConverted:H.map(Z=>{try{const fe=typeof Z.text=="string"?Z.text.substring(0,50):String(Z.text||"").substring(0,50);return{id:Z.id,text:fe,isUser:Z.isUser,textType:typeof Z.text}}catch(fe){return console.warn("[PluginControlPanel] Error processing message text:",fe,Z),{id:Z.id,text:"[ERROR: invalid text]",isUser:Z.isUser,textType:typeof Z.text}}})}),O(H)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),O([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(j),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(j){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",j),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:le,messagesCount:A.length,errorType:typeof j,errorMessage:j instanceof Error?j.message:String(j),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:j,errorType:typeof j,errorMessage:j instanceof Error?j.message:String(j),errorStack:j instanceof Error?j.stack:"No stack trace",errorName:j instanceof Error?j.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:g,pageKey:X}),ye(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let h="Ошибка связи с background";j instanceof Error?(h+=`: ${j.message}`,j.name==="TypeError"&&j.message.includes("substring")&&(h+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:j,stack:j.stack,context:{pluginId:g,pageKey:X}}))):h+=`: ${String(j)}`,he(h),O([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[g,X,N,S,f,c]);V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:g,pageKey:X,currentTabUrl:S,timestamp:new Date().toISOString()}),re().catch(j=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",j)})},[re]),V.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),re().catch(j=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",j)}),B()},[X,re,B]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:g,pageKey:X,timestamp:new Date().toISOString()});const j=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===g&&p.pageKey===X&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:le,messagesCount:A.length,currentPageKey:X,pluginId:g,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),re().catch(Y=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",Y)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:le,messagesCount:A.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),he(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,R=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const Z=JSON.parse(Y);typeof Z=="object"&&Z!==null&&"content"in Z&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",Z),Y=String(Z.content||""),Z.timestamp&&typeof Z.timestamp=="number"&&(R=Z.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const H={id:p.message.id||`pyodide_${R}_${Math.random()}`,text:Y,isUser:!1,timestamp:R};console.log("[PluginControlPanel] Adding Pyodide message to chat:",H),O(Z=>[...Z,H]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",Y,p);const R={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};O(H=>[...H,R])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},h=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(j),chrome.runtime.onMessage.removeListener(h),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(j),chrome.runtime.onMessage.removeListener(h)}},[g,X,J,re]),V.useEffect(()=>{r==="chat"&&B()},[r,B]),V.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:g,pageKey:X,draftText:ne,message:ae,currentView:r,isRunning:f,isPaused:c})}),V.useEffect(()=>{const j=console.error;return console.error=(...h)=>{const p=h.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",h),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),j.apply(console,h)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=j,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),V.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const j=h=>{const p=h.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,R=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const Z=JSON.parse(Y);typeof Z=="object"&&Z!==null&&"content"in Z&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",Z),Y=String(Z.content||""),Z.timestamp&&typeof Z.timestamp=="number"&&(R=Z.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const H={id:p.message.id||`pyodide_${R}_${Math.random()}`,text:Y,isUser:!1,timestamp:R};console.log("[PluginControlPanel] Adding Pyodide message to chat:",H),O(Z=>[...Z,H]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",Y,p);const R={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};O(H=>[...H,R])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",j),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",j),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),V.useEffect(()=>{typeof ne=="string"&&(ie(ne),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",ne))},[ne,ie]);const pe=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:ae}),!ae.trim())return;const j={id:Date.now().toString(),text:ae.trim(),timestamp:Date.now()};ie(""),he(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),J({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:g,pageKey:X,message:{role:"user",content:j.text,timestamp:j.timestamp}}),te()};V.useEffect(()=>{const j=p=>{if(!Pe)return;const Y=document.querySelector(".chat-view");if(!Y)return;const R=Y.getBoundingClientRect(),H=R.bottom-p.clientY,Z=100,fe=R.height-80;H>=Z&&H<=fe&&et(R.height-H)},h=()=>{z(!1),document.body.style.cursor="",document.body.style.userSelect=""};return Pe&&(document.addEventListener("mousemove",j),document.addEventListener("mouseup",h)),()=>{document.removeEventListener("mousemove",j),document.removeEventListener("mouseup",h)}},[Pe]),V.useEffect(()=>{k.current?.scrollIntoView({behavior:"smooth"})},[A]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:A.length,firstMessage:A[0]?{id:A[0].id,text:typeof A[0].text=="string"?A[0].text.substring(0,50):String(A[0].text||"").substring(0,50),isUser:A[0].isUser,timestamp:A[0].timestamp,textType:typeof A[0].text}:null,allMessages:A.map(j=>{try{const h=typeof j.text=="string"?j.text.substring(0,30):String(j.text||"").substring(0,30);return{id:j.id,text:h,isUser:j.isUser,textType:typeof j.text}}catch(h){return console.warn("[PluginControlPanel] Error in message logging:",h,j),{id:j.id,text:"[LOGGING ERROR]",isUser:j.isUser,textType:typeof j.text}}}),timestamp:new Date().toISOString()})},[A]),V.useEffect(()=>{r==="chat"&&setTimeout(()=>Q.current?.focus(),100)},[r]);const ge=j=>{ie(j.target.value);const h=j.target;h.style.height="auto";const p=Math.min(Math.max(h.scrollHeight,60),200);h.style.height=`${p}px`,et(p)},Je=j=>{j.key==="Enter"&&!j.shiftKey&&(j.preventDefault(),pe())},Ge=()=>{ye(!0),he(null),J({type:"DELETE_PLUGIN_CHAT",pluginId:g,pageKey:X}),O([]),te()},pt=()=>{const j=JSON.stringify(A,null,2),h=new Blob([j],{type:"application/json"});hp.saveAs(h,`plugin-chat-${g}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":I},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:u.iconUrl||`plugins/${u.id}/${u.icon||"icon.svg"}`,alt:`${xe} icon`,onError:j=>{const h=typeof xe=="string"&&xe.length>0?xe.charAt(0):"P";j.currentTarget.src=`data:image/svg+xml;utf8,${h}`}}),y.jsx("h3",{children:xe})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${f?"stop-mode":"start-mode"}`,onClick:Ae,disabled:f||c,title:f?"Остановить":"Запустить",children:f?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:D,disabled:!f||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:F,disabled:!f,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:U,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${v==="chat"?"active":""}`,onClick:()=>q("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${v==="details"?"active":""}`,onClick:()=>q("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[v==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:Ge,disabled:le||!!ze,children:le?"Очистка...":ze?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:pt,disabled:le||!!ze,children:le?"Экспорт...":ze?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ee,disabled:le,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[le&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),ze&&y.jsx("div",{className:"chat-error",children:ze}),!le&&!ze&&A.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:A.map((j,h)=>{console.log("[PluginControlPanel] render message:",h,j);let p=j.text,Y=j.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",h,":",j.text);try{const R=JSON.parse(p);if(typeof R=="object"&&R!==null&&"content"in R){console.log("[PluginControlPanel] Парсинг JSON в рендере:",R);let H=R.content;typeof H=="object"?p=JSON.stringify(H):p=String(H||""),R.timestamp&&typeof R.timestamp=="number"&&(Y=R.timestamp)}else if(typeof R=="string")p=R;else if(typeof R=="object"&&R!==null){const H=Object.values(R).filter(Z=>typeof Z=="string");H.length>0?p=String(H[0]):p=JSON.stringify(R)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",h,":",p),y.jsx("div",{className:`chat-message ${j.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(Y).toLocaleTimeString()})]})},j.id||h)})}),y.jsx("div",{ref:k})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:Q,className:"message-textarea",value:ae,onChange:ge,onKeyPress:Je,placeholder:"Напишите сообщение...",style:{height:`${Re}px`}}),y.jsx("button",{className:"send-btn",onClick:pe,disabled:!ae.trim(),children:"📤"}),y.jsx(ih,{isDraftSaved:x,isDraftLoading:K,draftError:P,messageLength:ae.length,minLength:10,maxLength:1e3})]})]}),v==="details"&&y.jsx(dp,{plugin:u})]})]})},vp=({toasts:u,onRemove:r})=>y.jsx("div",{className:"toast-container",children:u.map(f=>y.jsx(bp,{toast:f,onRemove:r},f.id))}),bp=({toast:u,onRemove:r})=>{const[f,c]=V.useState(!1),[S,C]=V.useState(!1);V.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),u.duration>0){const F=setTimeout(()=>{D()},u.duration);return()=>clearTimeout(F)}},[u.duration]);const D=V.useCallback(()=>{C(!0),setTimeout(()=>{r(u.id)},300)},[u.id,r]);return y.jsx("div",{className:`toast toast-${u.type} ${f?"toast-visible":""} ${S?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:u.message}),y.jsx("button",{className:"toast-close",onClick:D,"aria-label":"Закрыть уведомление",children:"×"})]})})},Sp=({theme:u,isLight:r,onToggle:f,isInSidebar:c=!1})=>{const S=()=>{switch(u){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},C=()=>{switch(u){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},D={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:f,style:D,title:C(),children:S()})};function Tg(u){var r,f,c="";if(typeof u=="string"||typeof u=="number")c+=u;else if(typeof u=="object")if(Array.isArray(u)){var S=u.length;for(r=0;r{const r=Ap(u),{conflictingClassGroups:f,conflictingClassGroupModifiers:c}=u;return{getClassGroupId:D=>{const F=D.split($o);return F[0]===""&&F.length!==1&&F.shift(),Cg(F,r)||xp(D)},getConflictingClassGroupIds:(D,F)=>{const U=f[D]||[];return F&&c[D]?[...U,...c[D]]:U}}},Cg=(u,r)=>{if(u.length===0)return r.classGroupId;const f=u[0],c=r.nextPart.get(f),S=c?Cg(u.slice(1),c):void 0;if(S)return S;if(r.validators.length===0)return;const C=u.join($o);return r.validators.find(({validator:D})=>D(C))?.classGroupId},cg=/^\[(.+)\]$/,xp=u=>{if(cg.test(u)){const r=cg.exec(u)[1],f=r?.substring(0,r.indexOf(":"));if(f)return"arbitrary.."+f}},Ap=u=>{const{theme:r,classGroups:f}=u,c={nextPart:new Map,validators:[]};for(const S in f)Vo(f[S],c,S,r);return c},Vo=(u,r,f,c)=>{u.forEach(S=>{if(typeof S=="string"){const C=S===""?r:rg(r,S);C.classGroupId=f;return}if(typeof S=="function"){if(Tp(S)){Vo(S(c),r,f,c);return}r.validators.push({validator:S,classGroupId:f});return}Object.entries(S).forEach(([C,D])=>{Vo(D,rg(r,C),f,c)})})},rg=(u,r)=>{let f=u;return r.split($o).forEach(c=>{f.nextPart.has(c)||f.nextPart.set(c,{nextPart:new Map,validators:[]}),f=f.nextPart.get(c)}),f},Tp=u=>u.isThemeGetter,Cp=u=>{if(u<1)return{get:()=>{},set:()=>{}};let r=0,f=new Map,c=new Map;const S=(C,D)=>{f.set(C,D),r++,r>u&&(r=0,c=f,f=new Map)};return{get(C){let D=f.get(C);if(D!==void 0)return D;if((D=c.get(C))!==void 0)return S(C,D),D},set(C,D){f.has(C)?f.set(C,D):S(C,D)}}},Zo="!",ko=":",zp=ko.length,Mp=u=>{const{prefix:r,experimentalParseClassName:f}=u;let c=S=>{const C=[];let D=0,F=0,U=0,v;for(let W=0;WU?v-U:void 0;return{modifiers:C,hasImportantModifier:$,baseClassName:X,maybePostfixModifierPosition:I}};if(r){const S=r+ko,C=c;c=D=>D.startsWith(S)?C(D.substring(S.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:D,maybePostfixModifierPosition:void 0}}if(f){const S=c;c=C=>f({className:C,parseClassName:S})}return c},Op=u=>u.endsWith(Zo)?u.substring(0,u.length-1):u.startsWith(Zo)?u.substring(1):u,Dp=u=>{const r=Object.fromEntries(u.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const S=[];let C=[];return c.forEach(D=>{D[0]==="["||r[D]?(S.push(...C.sort(),D),C=[]):C.push(D)}),S.push(...C.sort()),S}},Up=u=>({cache:Cp(u.cacheSize),parseClassName:Mp(u),sortModifiers:Dp(u),...Ep(u)}),jp=/\s+/,wp=(u,r)=>{const{parseClassName:f,getClassGroupId:c,getConflictingClassGroupIds:S,sortModifiers:C}=r,D=[],F=u.trim().split(jp);let U="";for(let v=F.length-1;v>=0;v-=1){const q=F[v],{isExternal:X,modifiers:$,hasImportantModifier:I,baseClassName:W,maybePostfixModifierPosition:ae}=f(q);if(X){U=q+(U.length>0?" "+U:U);continue}let ie=!!ae,x=c(ie?W.substring(0,ae):W);if(!x){if(!ie){U=q+(U.length>0?" "+U:U);continue}if(x=c(W),!x){U=q+(U.length>0?" "+U:U);continue}ie=!1}const K=C($).join(":"),P=I?K+Zo:K,B=P+x;if(D.includes(B))continue;D.push(B);const te=S(x,ie);for(let ne=0;ne0?" "+U:U)}return U};function Np(){let u=0,r,f,c="";for(;u{if(typeof u=="string")return u;let r,f="";for(let c=0;cX(q),u());return f=Up(v),c=f.cache.get,S=f.cache.set,C=F,F(U)}function F(U){const v=c(U);if(v)return v;const q=wp(U,f);return S(U,q),q}return function(){return C(Np.apply(null,arguments))}}const st=u=>{const r=f=>f[u]||[];return r.isThemeGetter=!0,r},Mg=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,Og=/^\((?:(\w[\w-]*):)?(.+)\)$/i,Hp=/^\d+\/\d+$/,Gp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,Kp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Bp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,Lp=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,qp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,Jn=u=>Hp.test(u),Te=u=>!!u&&!Number.isNaN(Number(u)),Pl=u=>!!u&&Number.isInteger(Number(u)),Go=u=>u.endsWith("%")&&Te(u.slice(0,-1)),pl=u=>Gp.test(u),Yp=()=>!0,Pp=u=>Kp.test(u)&&!Bp.test(u),Dg=()=>!1,Xp=u=>Lp.test(u),Qp=u=>qp.test(u),Vp=u=>!ue(u)&&!oe(u),Zp=u=>Fn(u,wg,Dg),ue=u=>Mg.test(u),rn=u=>Fn(u,Ng,Pp),Ko=u=>Fn(u,Fp,Te),fg=u=>Fn(u,Ug,Dg),kp=u=>Fn(u,jg,Qp),gs=u=>Fn(u,Rg,Xp),oe=u=>Og.test(u),Za=u=>In(u,Ng),Jp=u=>In(u,Ip),dg=u=>In(u,Ug),$p=u=>In(u,wg),Wp=u=>In(u,jg),ms=u=>In(u,Rg,!0),Fn=(u,r,f)=>{const c=Mg.exec(u);return c?c[1]?r(c[1]):f(c[2]):!1},In=(u,r,f=!1)=>{const c=Og.exec(u);return c?c[1]?r(c[1]):f:!1},Ug=u=>u==="position"||u==="percentage",jg=u=>u==="image"||u==="url",wg=u=>u==="length"||u==="size"||u==="bg-size",Ng=u=>u==="length",Fp=u=>u==="number",Ip=u=>u==="family-name",Rg=u=>u==="shadow",e0=()=>{const u=st("color"),r=st("font"),f=st("text"),c=st("font-weight"),S=st("tracking"),C=st("leading"),D=st("breakpoint"),F=st("container"),U=st("spacing"),v=st("radius"),q=st("shadow"),X=st("inset-shadow"),$=st("text-shadow"),I=st("drop-shadow"),W=st("blur"),ae=st("perspective"),ie=st("aspect"),x=st("ease"),K=st("animate"),P=()=>["auto","avoid","all","avoid-page","page","left","right","column"],B=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],te=()=>[...B(),oe,ue],ne=()=>["auto","hidden","clip","visible","scroll"],A=()=>["auto","contain","none"],O=()=>[oe,ue,U],le=()=>[Jn,"full","auto",...O()],ye=()=>[Pl,"none","subgrid",oe,ue],ze=()=>["auto",{span:["full",Pl,oe,ue]},Pl,oe,ue],he=()=>[Pl,"auto",oe,ue],Re=()=>["auto","min","max","fr",oe,ue],et=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],Pe=()=>["start","end","center","stretch","center-safe","end-safe"],z=()=>["auto",...O()],k=()=>[Jn,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...O()],Q=()=>[u,oe,ue],Ae=()=>[...B(),dg,fg,{position:[oe,ue]}],xe=()=>["no-repeat",{repeat:["","x","y","space","round"]}],g=()=>["auto","cover","contain",$p,Zp,{size:[oe,ue]}],N=()=>[Go,Za,rn],J=()=>["","none","full",v,oe,ue],ee=()=>["",Te,Za,rn],re=()=>["solid","dashed","dotted","double"],pe=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ge=()=>[Te,Go,dg,fg],Je=()=>["","none",W,oe,ue],Ge=()=>["none",Te,oe,ue],pt=()=>["none",Te,oe,ue],j=()=>[Te,oe,ue],h=()=>[Jn,"full",...O()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[pl],breakpoint:[pl],color:[Yp],container:[pl],"drop-shadow":[pl],ease:["in","out","in-out"],font:[Vp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[pl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[pl],shadow:[pl],spacing:["px",Te],text:[pl],"text-shadow":[pl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",Jn,ue,oe,ie]}],container:["container"],columns:[{columns:[Te,ue,oe,F]}],"break-after":[{"break-after":P()}],"break-before":[{"break-before":P()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:te()}],overflow:[{overflow:ne()}],"overflow-x":[{"overflow-x":ne()}],"overflow-y":[{"overflow-y":ne()}],overscroll:[{overscroll:A()}],"overscroll-x":[{"overscroll-x":A()}],"overscroll-y":[{"overscroll-y":A()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:le()}],"inset-x":[{"inset-x":le()}],"inset-y":[{"inset-y":le()}],start:[{start:le()}],end:[{end:le()}],top:[{top:le()}],right:[{right:le()}],bottom:[{bottom:le()}],left:[{left:le()}],visibility:["visible","invisible","collapse"],z:[{z:[Pl,"auto",oe,ue]}],basis:[{basis:[Jn,"full","auto",F,...O()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Te,Jn,"auto","initial","none",ue]}],grow:[{grow:["",Te,oe,ue]}],shrink:[{shrink:["",Te,oe,ue]}],order:[{order:[Pl,"first","last","none",oe,ue]}],"grid-cols":[{"grid-cols":ye()}],"col-start-end":[{col:ze()}],"col-start":[{"col-start":he()}],"col-end":[{"col-end":he()}],"grid-rows":[{"grid-rows":ye()}],"row-start-end":[{row:ze()}],"row-start":[{"row-start":he()}],"row-end":[{"row-end":he()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":Re()}],"auto-rows":[{"auto-rows":Re()}],gap:[{gap:O()}],"gap-x":[{"gap-x":O()}],"gap-y":[{"gap-y":O()}],"justify-content":[{justify:[...et(),"normal"]}],"justify-items":[{"justify-items":[...Pe(),"normal"]}],"justify-self":[{"justify-self":["auto",...Pe()]}],"align-content":[{content:["normal",...et()]}],"align-items":[{items:[...Pe(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...Pe(),{baseline:["","last"]}]}],"place-content":[{"place-content":et()}],"place-items":[{"place-items":[...Pe(),"baseline"]}],"place-self":[{"place-self":["auto",...Pe()]}],p:[{p:O()}],px:[{px:O()}],py:[{py:O()}],ps:[{ps:O()}],pe:[{pe:O()}],pt:[{pt:O()}],pr:[{pr:O()}],pb:[{pb:O()}],pl:[{pl:O()}],m:[{m:z()}],mx:[{mx:z()}],my:[{my:z()}],ms:[{ms:z()}],me:[{me:z()}],mt:[{mt:z()}],mr:[{mr:z()}],mb:[{mb:z()}],ml:[{ml:z()}],"space-x":[{"space-x":O()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":O()}],"space-y-reverse":["space-y-reverse"],size:[{size:k()}],w:[{w:[F,"screen",...k()]}],"min-w":[{"min-w":[F,"screen","none",...k()]}],"max-w":[{"max-w":[F,"screen","none","prose",{screen:[D]},...k()]}],h:[{h:["screen","lh",...k()]}],"min-h":[{"min-h":["screen","lh","none",...k()]}],"max-h":[{"max-h":["screen","lh",...k()]}],"font-size":[{text:["base",f,Za,rn]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,oe,Ko]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Go,ue]}],"font-family":[{font:[Jp,ue,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[S,oe,ue]}],"line-clamp":[{"line-clamp":[Te,"none",oe,Ko]}],leading:[{leading:[C,...O()]}],"list-image":[{"list-image":["none",oe,ue]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",oe,ue]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:Q()}],"text-color":[{text:Q()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...re(),"wavy"]}],"text-decoration-thickness":[{decoration:[Te,"from-font","auto",oe,rn]}],"text-decoration-color":[{decoration:Q()}],"underline-offset":[{"underline-offset":[Te,"auto",oe,ue]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:O()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",oe,ue]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",oe,ue]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Ae()}],"bg-repeat":[{bg:xe()}],"bg-size":[{bg:g()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},Pl,oe,ue],radial:["",oe,ue],conic:[Pl,oe,ue]},Wp,kp]}],"bg-color":[{bg:Q()}],"gradient-from-pos":[{from:N()}],"gradient-via-pos":[{via:N()}],"gradient-to-pos":[{to:N()}],"gradient-from":[{from:Q()}],"gradient-via":[{via:Q()}],"gradient-to":[{to:Q()}],rounded:[{rounded:J()}],"rounded-s":[{"rounded-s":J()}],"rounded-e":[{"rounded-e":J()}],"rounded-t":[{"rounded-t":J()}],"rounded-r":[{"rounded-r":J()}],"rounded-b":[{"rounded-b":J()}],"rounded-l":[{"rounded-l":J()}],"rounded-ss":[{"rounded-ss":J()}],"rounded-se":[{"rounded-se":J()}],"rounded-ee":[{"rounded-ee":J()}],"rounded-es":[{"rounded-es":J()}],"rounded-tl":[{"rounded-tl":J()}],"rounded-tr":[{"rounded-tr":J()}],"rounded-br":[{"rounded-br":J()}],"rounded-bl":[{"rounded-bl":J()}],"border-w":[{border:ee()}],"border-w-x":[{"border-x":ee()}],"border-w-y":[{"border-y":ee()}],"border-w-s":[{"border-s":ee()}],"border-w-e":[{"border-e":ee()}],"border-w-t":[{"border-t":ee()}],"border-w-r":[{"border-r":ee()}],"border-w-b":[{"border-b":ee()}],"border-w-l":[{"border-l":ee()}],"divide-x":[{"divide-x":ee()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ee()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...re(),"hidden","none"]}],"divide-style":[{divide:[...re(),"hidden","none"]}],"border-color":[{border:Q()}],"border-color-x":[{"border-x":Q()}],"border-color-y":[{"border-y":Q()}],"border-color-s":[{"border-s":Q()}],"border-color-e":[{"border-e":Q()}],"border-color-t":[{"border-t":Q()}],"border-color-r":[{"border-r":Q()}],"border-color-b":[{"border-b":Q()}],"border-color-l":[{"border-l":Q()}],"divide-color":[{divide:Q()}],"outline-style":[{outline:[...re(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Te,oe,ue]}],"outline-w":[{outline:["",Te,Za,rn]}],"outline-color":[{outline:Q()}],shadow:[{shadow:["","none",q,ms,gs]}],"shadow-color":[{shadow:Q()}],"inset-shadow":[{"inset-shadow":["none",X,ms,gs]}],"inset-shadow-color":[{"inset-shadow":Q()}],"ring-w":[{ring:ee()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:Q()}],"ring-offset-w":[{"ring-offset":[Te,rn]}],"ring-offset-color":[{"ring-offset":Q()}],"inset-ring-w":[{"inset-ring":ee()}],"inset-ring-color":[{"inset-ring":Q()}],"text-shadow":[{"text-shadow":["none",$,ms,gs]}],"text-shadow-color":[{"text-shadow":Q()}],opacity:[{opacity:[Te,oe,ue]}],"mix-blend":[{"mix-blend":[...pe(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":pe()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Te]}],"mask-image-linear-from-pos":[{"mask-linear-from":ge()}],"mask-image-linear-to-pos":[{"mask-linear-to":ge()}],"mask-image-linear-from-color":[{"mask-linear-from":Q()}],"mask-image-linear-to-color":[{"mask-linear-to":Q()}],"mask-image-t-from-pos":[{"mask-t-from":ge()}],"mask-image-t-to-pos":[{"mask-t-to":ge()}],"mask-image-t-from-color":[{"mask-t-from":Q()}],"mask-image-t-to-color":[{"mask-t-to":Q()}],"mask-image-r-from-pos":[{"mask-r-from":ge()}],"mask-image-r-to-pos":[{"mask-r-to":ge()}],"mask-image-r-from-color":[{"mask-r-from":Q()}],"mask-image-r-to-color":[{"mask-r-to":Q()}],"mask-image-b-from-pos":[{"mask-b-from":ge()}],"mask-image-b-to-pos":[{"mask-b-to":ge()}],"mask-image-b-from-color":[{"mask-b-from":Q()}],"mask-image-b-to-color":[{"mask-b-to":Q()}],"mask-image-l-from-pos":[{"mask-l-from":ge()}],"mask-image-l-to-pos":[{"mask-l-to":ge()}],"mask-image-l-from-color":[{"mask-l-from":Q()}],"mask-image-l-to-color":[{"mask-l-to":Q()}],"mask-image-x-from-pos":[{"mask-x-from":ge()}],"mask-image-x-to-pos":[{"mask-x-to":ge()}],"mask-image-x-from-color":[{"mask-x-from":Q()}],"mask-image-x-to-color":[{"mask-x-to":Q()}],"mask-image-y-from-pos":[{"mask-y-from":ge()}],"mask-image-y-to-pos":[{"mask-y-to":ge()}],"mask-image-y-from-color":[{"mask-y-from":Q()}],"mask-image-y-to-color":[{"mask-y-to":Q()}],"mask-image-radial":[{"mask-radial":[oe,ue]}],"mask-image-radial-from-pos":[{"mask-radial-from":ge()}],"mask-image-radial-to-pos":[{"mask-radial-to":ge()}],"mask-image-radial-from-color":[{"mask-radial-from":Q()}],"mask-image-radial-to-color":[{"mask-radial-to":Q()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":B()}],"mask-image-conic-pos":[{"mask-conic":[Te]}],"mask-image-conic-from-pos":[{"mask-conic-from":ge()}],"mask-image-conic-to-pos":[{"mask-conic-to":ge()}],"mask-image-conic-from-color":[{"mask-conic-from":Q()}],"mask-image-conic-to-color":[{"mask-conic-to":Q()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Ae()}],"mask-repeat":[{mask:xe()}],"mask-size":[{mask:g()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",oe,ue]}],filter:[{filter:["","none",oe,ue]}],blur:[{blur:Je()}],brightness:[{brightness:[Te,oe,ue]}],contrast:[{contrast:[Te,oe,ue]}],"drop-shadow":[{"drop-shadow":["","none",I,ms,gs]}],"drop-shadow-color":[{"drop-shadow":Q()}],grayscale:[{grayscale:["",Te,oe,ue]}],"hue-rotate":[{"hue-rotate":[Te,oe,ue]}],invert:[{invert:["",Te,oe,ue]}],saturate:[{saturate:[Te,oe,ue]}],sepia:[{sepia:["",Te,oe,ue]}],"backdrop-filter":[{"backdrop-filter":["","none",oe,ue]}],"backdrop-blur":[{"backdrop-blur":Je()}],"backdrop-brightness":[{"backdrop-brightness":[Te,oe,ue]}],"backdrop-contrast":[{"backdrop-contrast":[Te,oe,ue]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Te,oe,ue]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Te,oe,ue]}],"backdrop-invert":[{"backdrop-invert":["",Te,oe,ue]}],"backdrop-opacity":[{"backdrop-opacity":[Te,oe,ue]}],"backdrop-saturate":[{"backdrop-saturate":[Te,oe,ue]}],"backdrop-sepia":[{"backdrop-sepia":["",Te,oe,ue]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":O()}],"border-spacing-x":[{"border-spacing-x":O()}],"border-spacing-y":[{"border-spacing-y":O()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",oe,ue]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Te,"initial",oe,ue]}],ease:[{ease:["linear","initial",x,oe,ue]}],delay:[{delay:[Te,oe,ue]}],animate:[{animate:["none",K,oe,ue]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[ae,oe,ue]}],"perspective-origin":[{"perspective-origin":te()}],rotate:[{rotate:Ge()}],"rotate-x":[{"rotate-x":Ge()}],"rotate-y":[{"rotate-y":Ge()}],"rotate-z":[{"rotate-z":Ge()}],scale:[{scale:pt()}],"scale-x":[{"scale-x":pt()}],"scale-y":[{"scale-y":pt()}],"scale-z":[{"scale-z":pt()}],"scale-3d":["scale-3d"],skew:[{skew:j()}],"skew-x":[{"skew-x":j()}],"skew-y":[{"skew-y":j()}],transform:[{transform:[oe,ue,"","none","gpu","cpu"]}],"transform-origin":[{origin:te()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:h()}],"translate-x":[{"translate-x":h()}],"translate-y":[{"translate-y":h()}],"translate-z":[{"translate-z":h()}],"translate-none":["translate-none"],accent:[{accent:Q()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:Q()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",oe,ue]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":O()}],"scroll-mx":[{"scroll-mx":O()}],"scroll-my":[{"scroll-my":O()}],"scroll-ms":[{"scroll-ms":O()}],"scroll-me":[{"scroll-me":O()}],"scroll-mt":[{"scroll-mt":O()}],"scroll-mr":[{"scroll-mr":O()}],"scroll-mb":[{"scroll-mb":O()}],"scroll-ml":[{"scroll-ml":O()}],"scroll-p":[{"scroll-p":O()}],"scroll-px":[{"scroll-px":O()}],"scroll-py":[{"scroll-py":O()}],"scroll-ps":[{"scroll-ps":O()}],"scroll-pe":[{"scroll-pe":O()}],"scroll-pt":[{"scroll-pt":O()}],"scroll-pr":[{"scroll-pr":O()}],"scroll-pb":[{"scroll-pb":O()}],"scroll-pl":[{"scroll-pl":O()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",oe,ue]}],fill:[{fill:["none",...Q()]}],"stroke-w":[{stroke:[Te,Za,rn,Ko]}],stroke:[{stroke:["none",...Q()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},t0=Rp(e0),gg=(...u)=>t0(_p(u));var Bo,mg;function l0(){if(mg)return Bo;mg=1;var u=function(K){return r(K)&&!f(K)};function r(x){return!!x&&typeof x=="object"}function f(x){var K=Object.prototype.toString.call(x);return K==="[object RegExp]"||K==="[object Date]"||C(x)}var c=typeof Symbol=="function"&&Symbol.for,S=c?Symbol.for("react.element"):60103;function C(x){return x.$$typeof===S}function D(x){return Array.isArray(x)?[]:{}}function F(x,K){return K.clone!==!1&&K.isMergeableObject(x)?ae(D(x),x,K):x}function U(x,K,P){return x.concat(K).map(function(B){return F(B,P)})}function v(x,K){if(!K.customMerge)return ae;var P=K.customMerge(x);return typeof P=="function"?P:ae}function q(x){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(x).filter(function(K){return Object.propertyIsEnumerable.call(x,K)}):[]}function X(x){return Object.keys(x).concat(q(x))}function $(x,K){try{return K in x}catch{return!1}}function I(x,K){return $(x,K)&&!(Object.hasOwnProperty.call(x,K)&&Object.propertyIsEnumerable.call(x,K))}function W(x,K,P){var B={};return P.isMergeableObject(x)&&X(x).forEach(function(te){B[te]=F(x[te],P)}),X(K).forEach(function(te){I(x,te)||($(x,te)&&P.isMergeableObject(K[te])?B[te]=v(te,P)(x[te],K[te],P):B[te]=F(K[te],P))}),B}function ae(x,K,P){P=P||{},P.arrayMerge=P.arrayMerge||U,P.isMergeableObject=P.isMergeableObject||u,P.cloneUnlessOtherwiseSpecified=F;var B=Array.isArray(K),te=Array.isArray(x),ne=B===te;return ne?B?P.arrayMerge(x,K,P):W(x,K,P):F(K,P)}ae.all=function(K,P){if(!Array.isArray(K))throw new Error("first argument should be an array");return K.reduce(function(B,te){return ae(B,te,P)},{})};var ie=ae;return Bo=ie,Bo}l0();var Lo={exports:{}},ka={},qo={exports:{}},Yo={};/** + * @license React + * scheduler.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var yg;function n0(){return yg||(yg=1,(function(u){function r(z,k){var Q=z.length;z.push(k);e:for(;0>>1,xe=z[Ae];if(0>>1;AeS(J,Q))eeS(re,J)?(z[Ae]=re,z[ee]=Q,Ae=ee):(z[Ae]=J,z[N]=Q,Ae=N);else if(eeS(re,Q))z[Ae]=re,z[ee]=Q,Ae=ee;else break e}}return k}function S(z,k){var Q=z.sortIndex-k.sortIndex;return Q!==0?Q:z.id-k.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;u.unstable_now=function(){return C.now()}}else{var D=Date,F=D.now();u.unstable_now=function(){return D.now()-F}}var U=[],v=[],q=1,X=null,$=3,I=!1,W=!1,ae=!1,ie=!1,x=typeof setTimeout=="function"?setTimeout:null,K=typeof clearTimeout=="function"?clearTimeout:null,P=typeof setImmediate<"u"?setImmediate:null;function B(z){for(var k=f(v);k!==null;){if(k.callback===null)c(v);else if(k.startTime<=z)c(v),k.sortIndex=k.expirationTime,r(U,k);else break;k=f(v)}}function te(z){if(ae=!1,B(z),!W)if(f(U)!==null)W=!0,ne||(ne=!0,he());else{var k=f(v);k!==null&&Pe(te,k.startTime-z)}}var ne=!1,A=-1,O=5,le=-1;function ye(){return ie?!0:!(u.unstable_now()-lez&&ye());){var Ae=X.callback;if(typeof Ae=="function"){X.callback=null,$=X.priorityLevel;var xe=Ae(X.expirationTime<=z);if(z=u.unstable_now(),typeof xe=="function"){X.callback=xe,B(z),k=!0;break t}X===f(U)&&c(U),B(z)}else c(U);X=f(U)}if(X!==null)k=!0;else{var g=f(v);g!==null&&Pe(te,g.startTime-z),k=!1}}break e}finally{X=null,$=Q,I=!1}k=void 0}}finally{k?he():ne=!1}}}var he;if(typeof P=="function")he=function(){P(ze)};else if(typeof MessageChannel<"u"){var Re=new MessageChannel,et=Re.port2;Re.port1.onmessage=ze,he=function(){et.postMessage(null)}}else he=function(){x(ze,0)};function Pe(z,k){A=x(function(){z(u.unstable_now())},k)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(z){z.callback=null},u.unstable_forceFrameRate=function(z){0>z||125Ae?(z.sortIndex=Q,r(v,z),f(U)===null&&z===f(v)&&(ae?(K(A),A=-1):ae=!0,Pe(te,Q-Ae))):(z.sortIndex=xe,r(U,z),W||I||(W=!0,ne||(ne=!0,he()))),z},u.unstable_shouldYield=ye,u.unstable_wrapCallback=function(z){var k=$;return function(){var Q=$;$=k;try{return z.apply(this,arguments)}finally{$=Q}}}})(Yo)),Yo}var hg;function a0(){return hg||(hg=1,qo.exports=n0()),qo.exports}var Po={exports:{}},mt={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var pg;function i0(){if(pg)return mt;pg=1;var u=Jo();function r(U){var v="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Po.exports=i0(),Po.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var bg;function u0(){if(bg)return ka;bg=1;var u=a0(),r=Jo(),f=s0();function c(e){var t="https://react.dev/errors/"+e;if(1xe||(e.current=Ae[xe],Ae[xe]=null,xe--)}function J(e,t){xe++,Ae[xe]=e.current,e.current=t}var ee=g(null),re=g(null),pe=g(null),ge=g(null);function Je(e,t){switch(J(pe,t),J(re,e),J(ee,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Sd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Sd(t),e=_d(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}N(ee),J(ee,e)}function Ge(){N(ee),N(re),N(pe)}function pt(e){e.memoizedState!==null&&J(ge,e);var t=ee.current,l=_d(t,e.type);t!==l&&(J(re,e),J(ee,l))}function j(e){re.current===e&&(N(ee),N(re)),ge.current===e&&(N(ge),Ya._currentValue=Q)}var h,p;function Y(e){if(h===void 0)try{throw Error()}catch(l){var t=l.stack.trim().match(/\n( *(at )?)/);h=t&&t[1]||"",p=-1)":-1a||d[n]!==E[a]){var w=` +`+d[n].replace(" at new "," at ");return e.displayName&&w.includes("")&&(w=w.replace("",e.displayName)),w}while(1<=n&&0<=a);break}}}finally{R=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?Y(l):""}function Z(e,t){switch(e.tag){case 26:case 27:case 5:return Y(e.type);case 16:return Y("Lazy");case 13:return e.child!==t&&t!==null?Y("Suspense Fallback"):Y("Suspense");case 19:return Y("SuspenseList");case 0:case 15:return H(e.type,!1);case 11:return H(e.type.render,!1);case 1:return H(e.type,!0);case 31:return Y("Activity");default:return""}}function fe(e){try{var t="",l=null;do t+=Z(e,l),l=e,e=e.return;while(e);return t}catch(n){return` +Error generating stack: `+n.message+` +`+n.stack}}var _e=Object.prototype.hasOwnProperty,ve=u.unstable_scheduleCallback,Ce=u.unstable_cancelCallback,Xe=u.unstable_shouldYield,Xl=u.unstable_requestPaint,Tt=u.unstable_now,Hg=u.unstable_getCurrentPriorityLevel,Wo=u.unstable_ImmediatePriority,Fo=u.unstable_UserBlockingPriority,Ja=u.unstable_NormalPriority,Gg=u.unstable_LowPriority,Io=u.unstable_IdlePriority,Kg=u.log,Bg=u.unstable_setDisableYieldValue,ea=null,Ct=null;function vl(e){if(typeof Kg=="function"&&Bg(e),Ct&&typeof Ct.setStrictMode=="function")try{Ct.setStrictMode(ea,e)}catch{}}var zt=Math.clz32?Math.clz32:Yg,Lg=Math.log,qg=Math.LN2;function Yg(e){return e>>>=0,e===0?32:31-(Lg(e)/qg|0)|0}var $a=256,Wa=262144,Fa=4194304;function Ql(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ia(e,t,l){var n=e.pendingLanes;if(n===0)return 0;var a=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var o=n&134217727;return o!==0?(n=o&~i,n!==0?a=Ql(n):(s&=o,s!==0?a=Ql(s):l||(l=o&~e,l!==0&&(a=Ql(l))))):(o=n&~i,o!==0?a=Ql(o):s!==0?a=Ql(s):l||(l=n&~e,l!==0&&(a=Ql(l)))),a===0?0:t!==0&&t!==a&&(t&i)===0&&(i=a&-a,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:a}function ta(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Pg(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ec(){var e=Fa;return Fa<<=1,(Fa&62914560)===0&&(Fa=4194304),e}function ps(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function la(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Xg(e,t,l,n,a,i){var s=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,d=e.expirationTimes,E=e.hiddenUpdates;for(l=s&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var $g=/[\n"\\]/g;function Ht(e){return e.replace($g,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function xs(e,t,l,n,a,i,s,o){e.name="",s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.type=s:e.removeAttribute("type"),t!=null?s==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Rt(t)):e.value!==""+Rt(t)&&(e.value=""+Rt(t)):s!=="submit"&&s!=="reset"||e.removeAttribute("value"),t!=null?As(e,s,Rt(t)):l!=null?As(e,s,Rt(l)):n!=null&&e.removeAttribute("value"),a==null&&i!=null&&(e.defaultChecked=!!i),a!=null&&(e.checked=a&&typeof a!="function"&&typeof a!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.name=""+Rt(o):e.removeAttribute("name")}function gc(e,t,l,n,a,i,s,o){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){Es(e);return}l=l!=null?""+Rt(l):"",t=t!=null?""+Rt(t):l,o||t===e.value||(e.value=t),e.defaultValue=t}n=n??a,n=typeof n!="function"&&typeof n!="symbol"&&!!n,e.checked=o?e.checked:!!n,e.defaultChecked=!!n,s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(e.name=s),Es(e)}function As(e,t,l){t==="number"&&li(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function hn(e,t,l,n){if(e=e.options,t){t={};for(var a=0;a"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Os=!1;if(el)try{var sa={};Object.defineProperty(sa,"passive",{get:function(){Os=!0}}),window.addEventListener("test",sa,sa),window.removeEventListener("test",sa,sa)}catch{Os=!1}var Sl=null,Ds=null,ai=null;function Sc(){if(ai)return ai;var e,t=Ds,l=t.length,n,a="value"in Sl?Sl.value:Sl.textContent,i=a.length;for(e=0;e=ca),Cc=" ",zc=!1;function Mc(e,t){switch(e){case"keyup":return Am.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Oc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Sn=!1;function Cm(e,t){switch(e){case"compositionend":return Oc(t);case"keypress":return t.which!==32?null:(zc=!0,Cc);case"textInput":return e=t.data,e===Cc&&zc?null:e;default:return null}}function zm(e,t){if(Sn)return e==="compositionend"||!Rs&&Mc(e,t)?(e=Sc(),ai=Ds=Sl=null,Sn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=n}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Gc(l)}}function Bc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Bc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Lc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=li(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=li(e.document)}return t}function Ks(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var Rm=el&&"documentMode"in document&&11>=document.documentMode,_n=null,Bs=null,ga=null,Ls=!1;function qc(e,t,l){var n=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Ls||_n==null||_n!==li(n)||(n=_n,"selectionStart"in n&&Ks(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),ga&&da(ga,n)||(ga=n,n=Wi(Bs,"onSelect"),0>=s,a-=s,kt=1<<32-zt(t)+a|l<Ee?(Ue=ce,ce=null):Ue=ce.sibling;var Ne=T(b,ce,_[Ee],G);if(Ne===null){ce===null&&(ce=Ue);break}e&&ce&&Ne.alternate===null&&t(b,ce),m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne,ce=Ue}if(Ee===_.length)return l(b,ce),je&&ll(b,Ee),de;if(ce===null){for(;Ee<_.length;Ee++)ce=L(b,_[Ee],G),ce!==null&&(m=i(ce,m,Ee),we===null?de=ce:we.sibling=ce,we=ce);return je&&ll(b,Ee),de}for(ce=n(ce);Ee<_.length;Ee++)Ue=M(ce,b,Ee,_[Ee],G),Ue!==null&&(e&&Ue.alternate!==null&&ce.delete(Ue.key===null?Ee:Ue.key),m=i(Ue,m,Ee),we===null?de=Ue:we.sibling=Ue,we=Ue);return e&&ce.forEach(function(Yl){return t(b,Yl)}),je&&ll(b,Ee),de}function me(b,m,_,G){if(_==null)throw Error(c(151));for(var de=null,we=null,ce=m,Ee=m=0,Ue=null,Ne=_.next();ce!==null&&!Ne.done;Ee++,Ne=_.next()){ce.index>Ee?(Ue=ce,ce=null):Ue=ce.sibling;var Yl=T(b,ce,Ne.value,G);if(Yl===null){ce===null&&(ce=Ue);break}e&&ce&&Yl.alternate===null&&t(b,ce),m=i(Yl,m,Ee),we===null?de=Yl:we.sibling=Yl,we=Yl,ce=Ue}if(Ne.done)return l(b,ce),je&&ll(b,Ee),de;if(ce===null){for(;!Ne.done;Ee++,Ne=_.next())Ne=L(b,Ne.value,G),Ne!==null&&(m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne);return je&&ll(b,Ee),de}for(ce=n(ce);!Ne.done;Ee++,Ne=_.next())Ne=M(ce,b,Ee,Ne.value,G),Ne!==null&&(e&&Ne.alternate!==null&&ce.delete(Ne.key===null?Ee:Ne.key),m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne);return e&&ce.forEach(function(th){return t(b,th)}),je&&ll(b,Ee),de}function Ye(b,m,_,G){if(typeof _=="object"&&_!==null&&_.type===ae&&_.key===null&&(_=_.props.children),typeof _=="object"&&_!==null){switch(_.$$typeof){case I:e:{for(var de=_.key;m!==null;){if(m.key===de){if(de=_.type,de===ae){if(m.tag===7){l(b,m.sibling),G=a(m,_.props.children),G.return=b,b=G;break e}}else if(m.elementType===de||typeof de=="object"&&de!==null&&de.$$typeof===O&&ln(de)===m.type){l(b,m.sibling),G=a(m,_.props),ba(G,_),G.return=b,b=G;break e}l(b,m);break}else t(b,m);m=m.sibling}_.type===ae?(G=Wl(_.props.children,b.mode,G,_.key),G.return=b,b=G):(G=mi(_.type,_.key,_.props,null,b.mode,G),ba(G,_),G.return=b,b=G)}return s(b);case W:e:{for(de=_.key;m!==null;){if(m.key===de)if(m.tag===4&&m.stateNode.containerInfo===_.containerInfo&&m.stateNode.implementation===_.implementation){l(b,m.sibling),G=a(m,_.children||[]),G.return=b,b=G;break e}else{l(b,m);break}else t(b,m);m=m.sibling}G=Zs(_,b.mode,G),G.return=b,b=G}return s(b);case O:return _=ln(_),Ye(b,m,_,G)}if(Pe(_))return se(b,m,_,G);if(he(_)){if(de=he(_),typeof de!="function")throw Error(c(150));return _=de.call(_),me(b,m,_,G)}if(typeof _.then=="function")return Ye(b,m,_i(_),G);if(_.$$typeof===P)return Ye(b,m,pi(b,_),G);Ei(b,_)}return typeof _=="string"&&_!==""||typeof _=="number"||typeof _=="bigint"?(_=""+_,m!==null&&m.tag===6?(l(b,m.sibling),G=a(m,_),G.return=b,b=G):(l(b,m),G=Vs(_,b.mode,G),G.return=b,b=G),s(b)):l(b,m)}return function(b,m,_,G){try{va=0;var de=Ye(b,m,_,G);return jn=null,de}catch(ce){if(ce===Un||ce===bi)throw ce;var we=Ot(29,ce,null,b.mode);return we.lanes=G,we.return=b,we}finally{}}}var an=rr(!0),fr=rr(!1),Tl=!1;function iu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function su(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Cl(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function zl(e,t,l){var n=e.updateQueue;if(n===null)return null;if(n=n.shared,(He&2)!==0){var a=n.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),n.pending=t,t=gi(e),kc(e,null,l),t}return di(e,n,t,l),gi(e)}function Sa(e,t,l){if(t=t.updateQueue,t!==null&&(t=t.shared,(l&4194048)!==0)){var n=t.lanes;n&=e.pendingLanes,l|=n,t.lanes=l,lc(e,l)}}function uu(e,t){var l=e.updateQueue,n=e.alternate;if(n!==null&&(n=n.updateQueue,l===n)){var a=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var s={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};i===null?a=i=s:i=i.next=s,l=l.next}while(l!==null);i===null?a=i=t:i=i.next=t}else a=i=t;l={baseState:n.baseState,firstBaseUpdate:a,lastBaseUpdate:i,shared:n.shared,callbacks:n.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=t:e.next=t,l.lastBaseUpdate=t}var ou=!1;function _a(){if(ou){var e=Dn;if(e!==null)throw e}}function Ea(e,t,l,n){ou=!1;var a=e.updateQueue;Tl=!1;var i=a.firstBaseUpdate,s=a.lastBaseUpdate,o=a.shared.pending;if(o!==null){a.shared.pending=null;var d=o,E=d.next;d.next=null,s===null?i=E:s.next=E,s=d;var w=e.alternate;w!==null&&(w=w.updateQueue,o=w.lastBaseUpdate,o!==s&&(o===null?w.firstBaseUpdate=E:o.next=E,w.lastBaseUpdate=d))}if(i!==null){var L=a.baseState;s=0,w=E=d=null,o=i;do{var T=o.lane&-536870913,M=T!==o.lane;if(M?(De&T)===T:(n&T)===T){T!==0&&T===On&&(ou=!0),w!==null&&(w=w.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var se=e,me=o;T=t;var Ye=l;switch(me.tag){case 1:if(se=me.payload,typeof se=="function"){L=se.call(Ye,L,T);break e}L=se;break e;case 3:se.flags=se.flags&-65537|128;case 0:if(se=me.payload,T=typeof se=="function"?se.call(Ye,L,T):se,T==null)break e;L=X({},L,T);break e;case 2:Tl=!0}}T=o.callback,T!==null&&(e.flags|=64,M&&(e.flags|=8192),M=a.callbacks,M===null?a.callbacks=[T]:M.push(T))}else M={lane:T,tag:o.tag,payload:o.payload,callback:o.callback,next:null},w===null?(E=w=M,d=L):w=w.next=M,s|=T;if(o=o.next,o===null){if(o=a.shared.pending,o===null)break;M=o,o=M.next,M.next=null,a.lastBaseUpdate=M,a.shared.pending=null}}while(!0);w===null&&(d=L),a.baseState=d,a.firstBaseUpdate=E,a.lastBaseUpdate=w,i===null&&(a.shared.lanes=0),jl|=s,e.lanes=s,e.memoizedState=L}}function dr(e,t){if(typeof e!="function")throw Error(c(191,e));e.call(t)}function gr(e,t){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ei?i:8;var s=z.T,o={};z.T=o,zu(e,!1,t,l);try{var d=a(),E=z.S;if(E!==null&&E(o,d),d!==null&&typeof d=="object"&&typeof d.then=="function"){var w=Xm(d,n);Ta(e,t,w,Nt(e))}else Ta(e,t,n,Nt(e))}catch(L){Ta(e,t,{then:function(){},status:"rejected",reason:L},Nt())}finally{k.p=i,s!==null&&o.types!==null&&(s.types=o.types),z.T=s}}function $m(){}function Tu(e,t,l,n){if(e.tag!==5)throw Error(c(476));var a=Qr(e).queue;Xr(e,a,t,Q,l===null?$m:function(){return Vr(e),l(n)})}function Qr(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Q,baseState:Q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:Q},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Vr(e){var t=Qr(e);t.next===null&&(t=e.alternate.memoizedState),Ta(e,t.next.queue,{},Nt())}function Cu(){return ft(Ya)}function Zr(){return Ie().memoizedState}function kr(){return Ie().memoizedState}function Wm(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Nt();e=Cl(l);var n=zl(t,e,l);n!==null&&(At(n,t,l),Sa(n,t,l)),t={cache:tu()},e.payload=t;return}t=t.return}}function Fm(e,t,l){var n=Nt();l={lane:n,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},ji(e)?$r(t,l):(l=Xs(e,t,l,n),l!==null&&(At(l,e,n),Wr(l,t,n)))}function Jr(e,t,l){var n=Nt();Ta(e,t,l,n)}function Ta(e,t,l,n){var a={lane:n,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(ji(e))$r(t,a);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,o=i(s,l);if(a.hasEagerState=!0,a.eagerState=o,Mt(o,s))return di(e,t,a,0),Qe===null&&fi(),!1}catch{}finally{}if(l=Xs(e,t,a,n),l!==null)return At(l,e,n),Wr(l,t,n),!0}return!1}function zu(e,t,l,n){if(n={lane:2,revertLane:io(),gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},ji(e)){if(t)throw Error(c(479))}else t=Xs(e,l,n,2),t!==null&&At(t,e,2)}function ji(e){var t=e.alternate;return e===Se||t!==null&&t===Se}function $r(e,t){Nn=Ti=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function Wr(e,t,l){if((l&4194048)!==0){var n=t.lanes;n&=e.pendingLanes,l|=n,t.lanes=l,lc(e,l)}}var Ca={readContext:ft,use:Mi,useCallback:$e,useContext:$e,useEffect:$e,useImperativeHandle:$e,useLayoutEffect:$e,useInsertionEffect:$e,useMemo:$e,useReducer:$e,useRef:$e,useState:$e,useDebugValue:$e,useDeferredValue:$e,useTransition:$e,useSyncExternalStore:$e,useId:$e,useHostTransitionStatus:$e,useFormState:$e,useActionState:$e,useOptimistic:$e,useMemoCache:$e,useCacheRefresh:$e};Ca.useEffectEvent=$e;var Fr={readContext:ft,use:Mi,useCallback:function(e,t){return ht().memoizedState=[e,t===void 0?null:t],e},useContext:ft,useEffect:Rr,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Di(4194308,4,Br.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Di(4194308,4,e,t)},useInsertionEffect:function(e,t){Di(4,2,e,t)},useMemo:function(e,t){var l=ht();t=t===void 0?null:t;var n=e();if(sn){vl(!0);try{e()}finally{vl(!1)}}return l.memoizedState=[n,t],n},useReducer:function(e,t,l){var n=ht();if(l!==void 0){var a=l(t);if(sn){vl(!0);try{l(t)}finally{vl(!1)}}}else a=t;return n.memoizedState=n.baseState=a,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:a},n.queue=e,e=e.dispatch=Fm.bind(null,Se,e),[n.memoizedState,e]},useRef:function(e){var t=ht();return e={current:e},t.memoizedState=e},useState:function(e){e=Su(e);var t=e.queue,l=Jr.bind(null,Se,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:xu,useDeferredValue:function(e,t){var l=ht();return Au(l,e,t)},useTransition:function(){var e=Su(!1);return e=Xr.bind(null,Se,e.queue,!0,!1),ht().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var n=Se,a=ht();if(je){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Qe===null)throw Error(c(349));(De&127)!==0||br(n,t,l)}a.memoizedState=l;var i={value:l,getSnapshot:t};return a.queue=i,Rr(_r.bind(null,n,i,e),[e]),n.flags|=2048,Hn(9,{destroy:void 0},Sr.bind(null,n,i,l,t),null),l},useId:function(){var e=ht(),t=Qe.identifierPrefix;if(je){var l=Jt,n=kt;l=(n&~(1<<32-zt(n)-1)).toString(32)+l,t="_"+t+"R_"+l,l=Ci++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof n.is=="string"?s.createElement("select",{is:n.is}):s.createElement("select"),n.multiple?i.multiple=!0:n.size&&(i.size=n.size);break;default:i=typeof n.is=="string"?s.createElement(a,{is:n.is}):s.createElement(a)}}i[ct]=t,i[vt]=n;e:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)i.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break e;for(;s.sibling===null;){if(s.return===null||s.return===t)break e;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=i;e:switch(gt(i,a,n),a){case"button":case"input":case"select":case"textarea":n=!!n.autoFocus;break e;case"img":n=!0;break e;default:n=!1}n&&ol(t)}}return Ze(t),qu(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,l),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==n&&ol(t);else{if(typeof n!="string"&&t.stateNode===null)throw Error(c(166));if(e=pe.current,zn(t)){if(e=t.stateNode,l=t.memoizedProps,n=null,a=rt,a!==null)switch(a.tag){case 27:case 5:n=a.memoizedProps}e[ct]=t,e=!!(e.nodeValue===l||n!==null&&n.suppressHydrationWarning===!0||vd(e.nodeValue,l)),e||xl(t,!0)}else e=Fi(e).createTextNode(n),e[ct]=t,t.stateNode=e}return Ze(t),null;case 31:if(l=t.memoizedState,e===null||e.memoizedState!==null){if(n=zn(t),l!==null){if(e===null){if(!n)throw Error(c(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(c(557));e[ct]=t}else Fl(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ze(t),e=!1}else l=Ws(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return t.flags&256?(Ut(t),t):(Ut(t),null);if((t.flags&128)!==0)throw Error(c(558))}return Ze(t),null;case 13:if(n=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=zn(t),n!==null&&n.dehydrated!==null){if(e===null){if(!a)throw Error(c(318));if(a=t.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(c(317));a[ct]=t}else Fl(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ze(t),a=!1}else a=Ws(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(Ut(t),t):(Ut(t),null)}return Ut(t),(t.flags&128)!==0?(t.lanes=l,t):(l=n!==null,e=e!==null&&e.memoizedState!==null,l&&(n=t.child,a=null,n.alternate!==null&&n.alternate.memoizedState!==null&&n.alternate.memoizedState.cachePool!==null&&(a=n.alternate.memoizedState.cachePool.pool),i=null,n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(i=n.memoizedState.cachePool.pool),i!==a&&(n.flags|=2048)),l!==e&&l&&(t.child.flags|=8192),Gi(t,t.updateQueue),Ze(t),null);case 4:return Ge(),e===null&&co(t.stateNode.containerInfo),Ze(t),null;case 10:return al(t.type),Ze(t),null;case 19:if(N(Fe),n=t.memoizedState,n===null)return Ze(t),null;if(a=(t.flags&128)!==0,i=n.rendering,i===null)if(a)Ma(n,!1);else{if(We!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Ai(e),i!==null){for(t.flags|=128,Ma(n,!1),e=i.updateQueue,t.updateQueue=e,Gi(t,e),t.subtreeFlags=0,e=l,l=t.child;l!==null;)Jc(l,e),l=l.sibling;return J(Fe,Fe.current&1|2),je&&ll(t,n.treeForkCount),t.child}e=e.sibling}n.tail!==null&&Tt()>Yi&&(t.flags|=128,a=!0,Ma(n,!1),t.lanes=4194304)}else{if(!a)if(e=Ai(i),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Gi(t,e),Ma(n,!0),n.tail===null&&n.tailMode==="hidden"&&!i.alternate&&!je)return Ze(t),null}else 2*Tt()-n.renderingStartTime>Yi&&l!==536870912&&(t.flags|=128,a=!0,Ma(n,!1),t.lanes=4194304);n.isBackwards?(i.sibling=t.child,t.child=i):(e=n.last,e!==null?e.sibling=i:t.child=i,n.last=i)}return n.tail!==null?(e=n.tail,n.rendering=e,n.tail=e.sibling,n.renderingStartTime=Tt(),e.sibling=null,l=Fe.current,J(Fe,a?l&1|2:l&1),je&&ll(t,n.treeForkCount),e):(Ze(t),null);case 22:case 23:return Ut(t),ru(),n=t.memoizedState!==null,e!==null?e.memoizedState!==null!==n&&(t.flags|=8192):n&&(t.flags|=8192),n?(l&536870912)!==0&&(t.flags&128)===0&&(Ze(t),t.subtreeFlags&6&&(t.flags|=8192)):Ze(t),l=t.updateQueue,l!==null&&Gi(t,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),n=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),n!==l&&(t.flags|=2048),e!==null&&N(tn),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),al(tt),Ze(t),null;case 25:return null;case 30:return null}throw Error(c(156,t.tag))}function ny(e,t){switch(Js(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return al(tt),Ge(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return j(t),null;case 31:if(t.memoizedState!==null){if(Ut(t),t.alternate===null)throw Error(c(340));Fl()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Ut(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(c(340));Fl()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return N(Fe),null;case 4:return Ge(),null;case 10:return al(t.type),null;case 22:case 23:return Ut(t),ru(),e!==null&&N(tn),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return al(tt),null;case 25:return null;default:return null}}function xf(e,t){switch(Js(t),t.tag){case 3:al(tt),Ge();break;case 26:case 27:case 5:j(t);break;case 4:Ge();break;case 31:t.memoizedState!==null&&Ut(t);break;case 13:Ut(t);break;case 19:N(Fe);break;case 10:al(t.type);break;case 22:case 23:Ut(t),ru(),e!==null&&N(tn);break;case 24:al(tt)}}function Oa(e,t){try{var l=t.updateQueue,n=l!==null?l.lastEffect:null;if(n!==null){var a=n.next;l=a;do{if((l.tag&e)===e){n=void 0;var i=l.create,s=l.inst;n=i(),s.destroy=n}l=l.next}while(l!==a)}}catch(o){Be(t,t.return,o)}}function Dl(e,t,l){try{var n=t.updateQueue,a=n!==null?n.lastEffect:null;if(a!==null){var i=a.next;n=i;do{if((n.tag&e)===e){var s=n.inst,o=s.destroy;if(o!==void 0){s.destroy=void 0,a=t;var d=l,E=o;try{E()}catch(w){Be(a,d,w)}}}n=n.next}while(n!==i)}}catch(w){Be(t,t.return,w)}}function Af(e){var t=e.updateQueue;if(t!==null){var l=e.stateNode;try{gr(t,l)}catch(n){Be(e,e.return,n)}}}function Tf(e,t,l){l.props=un(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(n){Be(e,t,n)}}function Da(e,t){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var n=e.stateNode;break;case 30:n=e.stateNode;break;default:n=e.stateNode}typeof l=="function"?e.refCleanup=l(n):l.current=n}}catch(a){Be(e,t,a)}}function $t(e,t){var l=e.ref,n=e.refCleanup;if(l!==null)if(typeof n=="function")try{n()}catch(a){Be(e,t,a)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(a){Be(e,t,a)}else l.current=null}function Cf(e){var t=e.type,l=e.memoizedProps,n=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":l.autoFocus&&n.focus();break e;case"img":l.src?n.src=l.src:l.srcSet&&(n.srcset=l.srcSet)}}catch(a){Be(e,e.return,a)}}function Yu(e,t,l){try{var n=e.stateNode;Ty(n,e.type,l,t),n[vt]=t}catch(a){Be(e,e.return,a)}}function zf(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Gl(e.type)||e.tag===4}function Pu(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||zf(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Gl(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Xu(e,t,l){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,t):(t=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,t.appendChild(e),l=l._reactRootContainer,l!=null||t.onclick!==null||(t.onclick=It));else if(n!==4&&(n===27&&Gl(e.type)&&(l=e.stateNode,t=null),e=e.child,e!==null))for(Xu(e,t,l),e=e.sibling;e!==null;)Xu(e,t,l),e=e.sibling}function Ki(e,t,l){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?l.insertBefore(e,t):l.appendChild(e);else if(n!==4&&(n===27&&Gl(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Ki(e,t,l),e=e.sibling;e!==null;)Ki(e,t,l),e=e.sibling}function Mf(e){var t=e.stateNode,l=e.memoizedProps;try{for(var n=e.type,a=t.attributes;a.length;)t.removeAttributeNode(a[0]);gt(t,n,l),t[ct]=e,t[vt]=l}catch(i){Be(e,e.return,i)}}var cl=!1,at=!1,Qu=!1,Of=typeof WeakSet=="function"?WeakSet:Set,ot=null;function ay(e,t){if(e=e.containerInfo,go=is,e=Lc(e),Ks(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else e:{l=(l=e.ownerDocument)&&l.defaultView||window;var n=l.getSelection&&l.getSelection();if(n&&n.rangeCount!==0){l=n.anchorNode;var a=n.anchorOffset,i=n.focusNode;n=n.focusOffset;try{l.nodeType,i.nodeType}catch{l=null;break e}var s=0,o=-1,d=-1,E=0,w=0,L=e,T=null;t:for(;;){for(var M;L!==l||a!==0&&L.nodeType!==3||(o=s+a),L!==i||n!==0&&L.nodeType!==3||(d=s+n),L.nodeType===3&&(s+=L.nodeValue.length),(M=L.firstChild)!==null;)T=L,L=M;for(;;){if(L===e)break t;if(T===l&&++E===a&&(o=s),T===i&&++w===n&&(d=s),(M=L.nextSibling)!==null)break;L=T,T=L.parentNode}L=M}l=o===-1||d===-1?null:{start:o,end:d}}else l=null}l=l||{start:0,end:0}}else l=null;for(mo={focusedElem:e,selectionRange:l},is=!1,ot=t;ot!==null;)if(t=ot,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,ot=e;else for(;ot!==null;){switch(t=ot,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),gt(i,n,l),i[ct]=e,ut(i),n=i;break e;case"link":var s=Rd("link","href",a).get(n+(l.href||""));if(s){for(var o=0;oYe&&(s=Ye,Ye=me,me=s);var b=Kc(o,me),m=Kc(o,Ye);if(b&&m&&(M.rangeCount!==1||M.anchorNode!==b.node||M.anchorOffset!==b.offset||M.focusNode!==m.node||M.focusOffset!==m.offset)){var _=L.createRange();_.setStart(b.node,b.offset),M.removeAllRanges(),me>Ye?(M.addRange(_),M.extend(m.node,m.offset)):(_.setEnd(m.node,m.offset),M.addRange(_))}}}}for(L=[],M=o;M=M.parentNode;)M.nodeType===1&&L.push({element:M,left:M.scrollLeft,top:M.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;ol?32:l,z.T=null,l=Fu,Fu=null;var i=Nl,s=ml;if(it=0,qn=Nl=null,ml=0,(He&6)!==0)throw Error(c(331));var o=He;if(He|=4,Lf(i.current),Gf(i,i.current,s,l),He=o,Ha(0,!1),Ct&&typeof Ct.onPostCommitFiberRoot=="function")try{Ct.onPostCommitFiberRoot(ea,i)}catch{}return!0}finally{k.p=a,z.T=n,ad(e,t)}}function sd(e,t,l){t=Kt(l,t),t=Uu(e.stateNode,t,2),e=zl(e,t,2),e!==null&&(la(e,2),Wt(e))}function Be(e,t,l){if(e.tag===3)sd(e,e,l);else for(;t!==null;){if(t.tag===3){sd(t,e,l);break}else if(t.tag===1){var n=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof n.componentDidCatch=="function"&&(wl===null||!wl.has(n))){e=Kt(l,e),l=uf(2),n=zl(t,l,2),n!==null&&(of(l,n,t,e),la(n,2),Wt(n));break}}t=t.return}}function lo(e,t,l){var n=e.pingCache;if(n===null){n=e.pingCache=new uy;var a=new Set;n.set(t,a)}else a=n.get(t),a===void 0&&(a=new Set,n.set(t,a));a.has(l)||(ku=!0,a.add(l),e=dy.bind(null,e,t,l),t.then(e,e))}function dy(e,t,l){var n=e.pingCache;n!==null&&n.delete(t),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,Qe===e&&(De&l)===l&&(We===4||We===3&&(De&62914560)===De&&300>Tt()-qi?(He&2)===0&&Yn(e,0):Ju|=l,Ln===De&&(Ln=0)),Wt(e)}function ud(e,t){t===0&&(t=ec()),e=$l(e,t),e!==null&&(la(e,t),Wt(e))}function gy(e){var t=e.memoizedState,l=0;t!==null&&(l=t.retryLane),ud(e,l)}function my(e,t){var l=0;switch(e.tag){case 31:case 13:var n=e.stateNode,a=e.memoizedState;a!==null&&(l=a.retryLane);break;case 19:n=e.stateNode;break;case 22:n=e.stateNode._retryCache;break;default:throw Error(c(314))}n!==null&&n.delete(t),ud(e,l)}function yy(e,t){return ve(e,t)}var ki=null,Xn=null,no=!1,Ji=!1,ao=!1,Hl=0;function Wt(e){e!==Xn&&e.next===null&&(Xn===null?ki=Xn=e:Xn=Xn.next=e),Ji=!0,no||(no=!0,py())}function Ha(e,t){if(!ao&&Ji){ao=!0;do for(var l=!1,n=ki;n!==null;){if(e!==0){var a=n.pendingLanes;if(a===0)var i=0;else{var s=n.suspendedLanes,o=n.pingedLanes;i=(1<<31-zt(42|e)+1)-1,i&=a&~(s&~o),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(l=!0,fd(n,i))}else i=De,i=Ia(n,n===Qe?i:0,n.cancelPendingCommit!==null||n.timeoutHandle!==-1),(i&3)===0||ta(n,i)||(l=!0,fd(n,i));n=n.next}while(l);ao=!1}}function hy(){od()}function od(){Ji=no=!1;var e=0;Hl!==0&&zy()&&(e=Hl);for(var t=Tt(),l=null,n=ki;n!==null;){var a=n.next,i=cd(n,t);i===0?(n.next=null,l===null?ki=a:l.next=a,a===null&&(Xn=l)):(l=n,(e!==0||(i&3)!==0)&&(Ji=!0)),n=a}it!==0&&it!==5||Ha(e),Hl!==0&&(Hl=0)}function cd(e,t){for(var l=e.suspendedLanes,n=e.pingedLanes,a=e.expirationTimes,i=e.pendingLanes&-62914561;0o)break;var w=d.transferSize,L=d.initiatorType;w&&bd(L)&&(d=d.responseEnd,s+=w*(d"u"?null:document;function Ud(e,t,l){var n=Qn;if(n&&typeof t=="string"&&t){var a=Ht(t);a='link[rel="'+e+'"][href="'+a+'"]',typeof l=="string"&&(a+='[crossorigin="'+l+'"]'),Dd.has(a)||(Dd.add(a),e={rel:e,crossOrigin:l,href:t},n.querySelector(a)===null&&(t=n.createElement("link"),gt(t,"link",e),ut(t),n.head.appendChild(t)))}}function Hy(e){yl.D(e),Ud("dns-prefetch",e,null)}function Gy(e,t){yl.C(e,t),Ud("preconnect",e,t)}function Ky(e,t,l){yl.L(e,t,l);var n=Qn;if(n&&e&&t){var a='link[rel="preload"][as="'+Ht(t)+'"]';t==="image"&&l&&l.imageSrcSet?(a+='[imagesrcset="'+Ht(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(a+='[imagesizes="'+Ht(l.imageSizes)+'"]')):a+='[href="'+Ht(e)+'"]';var i=a;switch(t){case"style":i=Vn(e);break;case"script":i=Zn(e)}Xt.has(i)||(e=X({rel:"preload",href:t==="image"&&l&&l.imageSrcSet?void 0:e,as:t},l),Xt.set(i,e),n.querySelector(a)!==null||t==="style"&&n.querySelector(La(i))||t==="script"&&n.querySelector(qa(i))||(t=n.createElement("link"),gt(t,"link",e),ut(t),n.head.appendChild(t)))}}function By(e,t){yl.m(e,t);var l=Qn;if(l&&e){var n=t&&typeof t.as=="string"?t.as:"script",a='link[rel="modulepreload"][as="'+Ht(n)+'"][href="'+Ht(e)+'"]',i=a;switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=Zn(e)}if(!Xt.has(i)&&(e=X({rel:"modulepreload",href:e},t),Xt.set(i,e),l.querySelector(a)===null)){switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(qa(i)))return}n=l.createElement("link"),gt(n,"link",e),ut(n),l.head.appendChild(n)}}}function Ly(e,t,l){yl.S(e,t,l);var n=Qn;if(n&&e){var a=mn(n).hoistableStyles,i=Vn(e);t=t||"default";var s=a.get(i);if(!s){var o={loading:0,preload:null};if(s=n.querySelector(La(i)))o.loading=5;else{e=X({rel:"stylesheet",href:e,"data-precedence":t},l),(l=Xt.get(i))&&_o(e,l);var d=s=n.createElement("link");ut(d),gt(d,"link",e),d._p=new Promise(function(E,w){d.onload=E,d.onerror=w}),d.addEventListener("load",function(){o.loading|=1}),d.addEventListener("error",function(){o.loading|=2}),o.loading|=4,es(s,t,n)}s={type:"stylesheet",instance:s,count:1,state:o},a.set(i,s)}}}function qy(e,t){yl.X(e,t);var l=Qn;if(l&&e){var n=mn(l).hoistableScripts,a=Zn(e),i=n.get(a);i||(i=l.querySelector(qa(a)),i||(e=X({src:e,async:!0},t),(t=Xt.get(a))&&Eo(e,t),i=l.createElement("script"),ut(i),gt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function Yy(e,t){yl.M(e,t);var l=Qn;if(l&&e){var n=mn(l).hoistableScripts,a=Zn(e),i=n.get(a);i||(i=l.querySelector(qa(a)),i||(e=X({src:e,async:!0,type:"module"},t),(t=Xt.get(a))&&Eo(e,t),i=l.createElement("script"),ut(i),gt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function jd(e,t,l,n){var a=(a=pe.current)?Ii(a):null;if(!a)throw Error(c(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(t=Vn(l.href),l=mn(a).hoistableStyles,n=l.get(t),n||(n={type:"style",instance:null,count:0,state:null},l.set(t,n)),n):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=Vn(l.href);var i=mn(a).hoistableStyles,s=i.get(e);if(s||(a=a.ownerDocument||a,s={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,s),(i=a.querySelector(La(e)))&&!i._p&&(s.instance=i,s.state.loading=5),Xt.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},Xt.set(e,l),i||Py(a,e,l,s.state))),t&&n===null)throw Error(c(528,""));return s}if(t&&n!==null)throw Error(c(529,""));return null;case"script":return t=l.async,l=l.src,typeof l=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=Zn(l),l=mn(a).hoistableScripts,n=l.get(t),n||(n={type:"script",instance:null,count:0,state:null},l.set(t,n)),n):{type:"void",instance:null,count:0,state:null};default:throw Error(c(444,e))}}function Vn(e){return'href="'+Ht(e)+'"'}function La(e){return'link[rel="stylesheet"]['+e+"]"}function wd(e){return X({},e,{"data-precedence":e.precedence,precedence:null})}function Py(e,t,l,n){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?n.loading=1:(t=e.createElement("link"),n.preload=t,t.addEventListener("load",function(){return n.loading|=1}),t.addEventListener("error",function(){return n.loading|=2}),gt(t,"link",l),ut(t),e.head.appendChild(t))}function Zn(e){return'[src="'+Ht(e)+'"]'}function qa(e){return"script[async]"+e}function Nd(e,t,l){if(t.count++,t.instance===null)switch(t.type){case"style":var n=e.querySelector('style[data-href~="'+Ht(l.href)+'"]');if(n)return t.instance=n,ut(n),n;var a=X({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return n=(e.ownerDocument||e).createElement("style"),ut(n),gt(n,"style",a),es(n,l.precedence,e),t.instance=n;case"stylesheet":a=Vn(l.href);var i=e.querySelector(La(a));if(i)return t.state.loading|=4,t.instance=i,ut(i),i;n=wd(l),(a=Xt.get(a))&&_o(n,a),i=(e.ownerDocument||e).createElement("link"),ut(i);var s=i;return s._p=new Promise(function(o,d){s.onload=o,s.onerror=d}),gt(i,"link",n),t.state.loading|=4,es(i,l.precedence,e),t.instance=i;case"script":return i=Zn(l.src),(a=e.querySelector(qa(i)))?(t.instance=a,ut(a),a):(n=l,(a=Xt.get(i))&&(n=X({},l),Eo(n,a)),e=e.ownerDocument||e,a=e.createElement("script"),ut(a),gt(a,"link",n),e.head.appendChild(a),t.instance=a);case"void":return null;default:throw Error(c(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(n=t.instance,t.state.loading|=4,es(n,l.precedence,e));return t.instance}function es(e,t,l){for(var n=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),a=n.length?n[n.length-1]:null,i=a,s=0;s title"):null)}function Xy(e,t,l){if(l===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return e=t.disabled,typeof t.precedence=="string"&&e==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Gd(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function Qy(e,t,l,n){if(l.type==="stylesheet"&&(typeof n.media!="string"||matchMedia(n.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var a=Vn(n.href),i=t.querySelector(La(a));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=ls.bind(e),t.then(e,e)),l.state.loading|=4,l.instance=i,ut(i);return}i=t.ownerDocument||t,n=wd(n),(a=Xt.get(a))&&_o(n,a),i=i.createElement("link"),ut(i);var s=i;s._p=new Promise(function(o,d){s.onload=o,s.onerror=d}),gt(i,"link",n),l.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,t),(t=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=ls.bind(e),t.addEventListener("load",l),t.addEventListener("error",l))}}var xo=0;function Vy(e,t){return e.stylesheets&&e.count===0&&as(e,e.stylesheets),0xo?50:800)+t);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(n),clearTimeout(a)}}:null}function ls(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)as(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var ns=null;function as(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,ns=new Map,t.forEach(Zy,e),ns=null,ls.call(e))}function Zy(e,t){if(!(t.state.loading&4)){var l=ns.get(e);if(l)var n=l.get(null);else{l=new Map,ns.set(e,l);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Lo.exports=u0(),Lo.exports}var c0=o0();const r0=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),f0=({children:u})=>{const[r,f]=hs.useState(null),c=hs.useCallback(()=>f(null),[]);if(r)return y.jsx(r0,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return f(S),null}},d0=({plugin:u,selected:r,onClick:f,isLight:c})=>{const S=u.settings?.enabled??!0;return y.jsxs("div",{className:`plugin-card${r?" selected":""}${c?"":" dark"}`,onClick:f,style:{border:S?"2px solid aqua":"2px solid #ccc"},children:[y.jsx("div",{className:"plugin-card-name",children:u.name}),y.jsxs("div",{className:"plugin-card-version",children:["v",u.version]}),y.jsx("div",{className:"plugin-card-description",children:u.description||"Описание не указано"}),y.jsx("div",{className:`plugin-card-status${S?" enabled":" disabled"}`,children:S?"Активен":"Неактивен"})]})},Xo=u=>{try{const r=new URL(u);return`${r.protocol}//${r.hostname}${r.pathname}`}catch{return u}},g0=()=>{const[u,r]=V.useState([]),[f,c]=V.useState(null),[S,C]=V.useState(!1),[D,F]=V.useState("chat"),[U,v]=V.useState(null),[q,X]=V.useState(null),[$,I]=V.useState([]),[W,ae]=V.useState(null),[ie,x]=V.useState("system"),[K,P]=V.useState(!0);V.useEffect(()=>{const h=async()=>{const Y=await Ro.get();x(Y.theme),P(Y.isLight)};return h(),Ro.subscribe(()=>{h()})},[]);const B=V.useRef(null),te=V.useRef(!1),ne=V.useRef([]),A=V.useRef(null),[O,le]=V.useState("connecting"),ye=V.useCallback(()=>te.current&&B.current!==null,[]),ze=V.useCallback(async(h,p=3)=>{if(!ye()){console.log("[SidePanel] Port not ready, queuing message:",h),ne.current.push(h);return}for(let Y=0;YsetTimeout(H,500*(Y+1)))}throw new Error("Failed to send message after all retries")},[ye]),he=V.useCallback(()=>{for(;ne.current.length>0&&ye();){const h=ne.current.shift();h&&ze(h).catch(p=>{console.error("[SidePanel] Failed to process queued message:",p)})}},[ye,ze]),Re=async(h,p)=>{const Y=`sidepanel_state_${h}`,R={selectedPluginId:p,showControlPanel:!0,timestamp:Date.now()};console.log("[SidePanel] Сохраняем состояние panel для страницы:",h,R),await chrome.storage.local.set({[Y]:R})},et=async h=>{const p=`sidepanel_state_${h}`;console.log("[SidePanel] Очищаем состояние panel для страницы:",h),await chrome.storage.local.remove(p)},Pe=async(h,p)=>{const Y=`sidepanel_state_${h}`,H=(await chrome.storage.local.get(Y))[Y];if(console.log("[SidePanel] Проверяем сохраненное состояние для страницы:",h,H),H&&H.selectedPluginId){const Z=p.find(fe=>fe.id===H.selectedPluginId);if(Z&&re(Z))return console.log("[SidePanel] Восстанавливаем состояние чата для страницы:",h,{pluginId:Z.id,pluginName:Z.name}),c(Z),C(!0),!0;console.log("[SidePanel] Плагин из сохраненного состояния не найден или не разрешен:",{pluginId:H.selectedPluginId,pluginFound:!!Z,isAllowed:Z?re(Z):!1})}return!1},z=V.useCallback(h=>{I(p=>p.filter(Y=>Y.id!==h))},[]),k=V.useCallback((h,p="success",Y=3e3)=>{const R=Date.now().toString(),H={id:R,message:h,type:p,duration:Y,timestamp:Date.now()};I(Z=>[...Z,H]),setTimeout(()=>{z(R)},Y)},[z]);V.useEffect(()=>{console.log("[SidePanel] useEffect вызван - загружаем плагины и URL"),J()},[]);const Q=V.useCallback(async(h=3,p=1e3)=>{console.log("[SidePanel][HEARTBEAT] Starting heartbeat ping with retry");for(let Y=0;YsetTimeout(H,p*(Y+1))))}return console.error("[SidePanel][HEARTBEAT] 💥 All heartbeat attempts failed - connection lost"),le("disconnected"),!1},[ye,ze]),Ae=V.useCallback(async()=>{console.log("[SidePanel] Attempting to reconnect port...");try{B.current&&(B.current.disconnect(),B.current=null),te.current=!1;const h=chrome.runtime.connect();B.current=h,console.log("[SidePanel] New port created:",h.name),te.current=!0;const p=R=>{console.log("[SidePanel] Received message from background via port:",R),R.type==="GET_PLUGINS_RESPONSE"&&R.plugins&&Array.isArray(R.plugins)?(console.log("[SidePanel] Setting plugins from port message:",R.plugins),r(R.plugins),console.log("[SidePanel] ✅ Plugins loaded successfully")):R.type==="GET_PLUGINS_RESPONSE"&&R.error&&(console.error("[SidePanel] Error from background script:",R.error),k("Ошибка загрузки плагинов","error"))},Y=()=>{console.log("[SidePanel] Port disconnected, will attempt reconnection"),te.current=!1,B.current=null,le("disconnected"),setTimeout(()=>{Ae()},1e3)};h.onMessage.addListener(p),h.onDisconnect.addListener(Y),he()}catch(h){console.error("[SidePanel] Failed to reconnect port:",h),k("Не удалось переподключить порт","error")}},[he,k]),xe=V.useCallback(async(h,p=3)=>{for(let Y=0;YsetTimeout(H,500*(Y+1)))}throw new Error("Failed to send message after all retries")},[]),g=V.useCallback(()=>{A.current&&clearInterval(A.current),console.log("[SidePanel][HEARTBEAT] 🚀 Starting heartbeat with 10s interval"),A.current=setInterval(async()=>{if(!await Q()){console.warn("[SidePanel][HEARTBEAT] ⚠️ Heartbeat failed, attempting to reconnect port...");try{await Ae()}catch(p){console.error("[SidePanel][HEARTBEAT] ❌ Port reconnection failed:",p)}}},1e4)},[Q,Ae]),N=V.useCallback(()=>{A.current&&(clearInterval(A.current),A.current=null)},[]);V.useEffect(()=>(console.log("[SidePanel] Запуск heartbeat механизма и подключения к порту"),g(),()=>{console.log("[SidePanel] Остановка heartbeat механизма и отключение порта"),N(),B.current&&B.current.disconnect()}),[g,N]),V.useEffect(()=>{console.log("[SidePanel] useEffect: Starting port-based plugin loading"),(async()=>{try{console.log("[SidePanel] Connecting to background script via port..."),await Ae();const p=5e3,Y=100;let R=0;for(;!ye()&&RsetTimeout(H,Y)),R+=Y;if(ye())await ze({type:"GET_PLUGINS"}),console.log("[SidePanel] Sent GET_PLUGINS message via port");else throw new Error("Port not ready after waiting")}catch(p){console.error("[SidePanel] Error in port-based plugin loading:",p),k("Ошибка связи с background script","error")}})()},[Ae,ye,ze,k]),V.useEffect(()=>{const h=()=>J();return chrome.tabs.onActivated.addListener(h),chrome.tabs.onUpdated.addListener(h),()=>{chrome.tabs.onActivated.removeListener(h),chrome.tabs.onUpdated.removeListener(h)}},[]);const J=async()=>{try{console.log("[SidePanel] Получение URL активной вкладки...");let h=null;try{const p=await chrome.tabs.query({active:!0,currentWindow:!0});console.log("[SidePanel] Способ 1 - найденные вкладки:",p),p[0]?.url&&(h=p[0])}catch(p){console.log("[SidePanel] Способ 1 не сработал:",p)}if(!h)try{const p=await chrome.tabs.query({windowId:chrome.windows.WINDOW_ID_CURRENT});console.log("[SidePanel] Способ 2 - все вкладки в окне:",p),h=p.find(Y=>Y.active)}catch(p){console.log("[SidePanel] Способ 2 не сработал:",p)}if(!h)try{const p=await xe({type:"GET_ACTIVE_TAB_URL"});if(console.log("[SidePanel] Способ 3 - ответ от background:",p),p?.url){ae(p.url);return}}catch(p){console.log("[SidePanel] Способ 3 не сработал:",p)}h?.url?(console.log("[SidePanel] Устанавливаем URL:",h.url),ae(h.url)):(console.log("[SidePanel] URL не найден, activeTab:",h),ae(null))}catch(h){console.error("[SidePanel] Ошибка получения URL активной вкладки:",h),ae(null)}},ee=h=>{if(h==="")return/^https?:\/\/.+/;const p=h.match(/^(\*|http|https):\/\/([^/]+)\/(.*)$/);if(!p)return null;const[,Y,R,H]=p,Z=Y==="*"?"https?":Y;if(R.startsWith("*.")){const _e="(?:[\\w-]+\\.)*"+R.slice(2).replace(/\./g,"\\."),ve=H.replace(/\*/g,".*");return new RegExp(`^${Z}://${_e}/${ve}$`)}else{const fe=R.replace(/\./g,"\\."),_e=H.replace(/\*/g,".*");return new RegExp(`^${Z}://${fe}/${_e}$`)}},re=h=>{const p=Array.isArray(h.manifest?.host_permissions)?h.manifest?.host_permissions:h.host_permissions||[],Y=W||window.location.href;let R=!1;const H=[];console.log(`[SidePanel] Проверка плагина '${h.name}' для URL: ${Y}`),console.log("[SidePanel] host_permissions:",p);for(const Z of p){const fe=ee(Z);if(!fe){H.push(`[${h.name}] Pattern '${Z}' не преобразован в RegExp`);continue}const _e=fe.test(Y);H.push(`[${h.name}] Pattern: '${Z}' → ${fe} => ${_e}`),_e&&(R=!0)}return R?console.log(`[SidePanel][DEBUG] Плагин '${h.name}' отображается для URL: ${Y}`):console.info(`[SidePanel] Плагин '${h.name}' не отображается для URL: ${Y}`),R},pe=async h=>{if(c(h),C(!0),F("chat"),W){const p=Xo(W);await Re(p,h.id)}},ge=async()=>{if(f){v(f.id),X(null);try{const h=f.name||f.manifest?.name||f.id;await xe({type:"RUN_WORKFLOW",pluginId:f.id}),k(`Плагин ${h} запущен`,"success")}catch(h){console.error("Failed to run workflow:",h),k(`Ошибка запуска плагина ${f.name}`,"error")}finally{v(null)}}},Je=async()=>{f&&(q===f.id?(X(null),k(`Плагин ${f.name} возобновлен`,"success")):(X(f.id),k(`Плагин ${f.name} приостановлен`,"warning")))},Ge=async()=>{if(f)try{await xe({type:"STOP_WORKFLOW",pluginId:f.id}),v(null),X(null),k(`Плагин ${f.name} остановлен`,"success")}catch(h){console.error("Failed to stop workflow:",h),k(`Ошибка остановки плагина ${f.name}`,"error")}},pt=()=>{if(C(!1),c(null),F("chat"),W){const h=Xo(W);et(h)}};V.useEffect(()=>{const h=(p,Y,R)=>{if(console.log("[SidePanel] Принято сообщение от Pyodide:",p),p.type==="PYODIDE_MESSAGE"){if(!f)return console.warn("[SidePanel][PYODIDE_MESSAGE] Игнорируем: selectedPlugin не установлен"),!0;if(console.log("[SidePanel] PYODIDE_MESSAGE получено:",p.message),!p.message||!p.message.content)return console.warn("[SidePanel][PYODIDE_MESSAGE] Игнорируем: message.content отсутствует или пустой",{hasMessage:!!p.message,hasContent:!!(p.message&&p.message.content)}),!0;console.log("[SidePanel] Отправляем PYODIDE_MESSAGE_UPDATE в PluginControlPanel");const H=new CustomEvent("PYODIDE_MESSAGE_UPDATE",{detail:{type:"PYODIDE_MESSAGE_UPDATE",message:p.message,timestamp:p.timestamp}});return window.dispatchEvent(H),console.log("[SidePanel] Событие PYODIDE_MESSAGE_UPDATE отправлено"),!0}return!1};return chrome.runtime.onMessage.addListener(h),console.log("[SidePanel] Handler для PYODIDE_MESSAGE зарегистрирован"),()=>{chrome.runtime.onMessage.removeListener(h),console.log("[SidePanel] Handler для PYODIDE_MESSAGE удален")}},[f]),V.useEffect(()=>{console.log("[SidePanel] currentTabUrl изменился:",W),(async()=>{if(!W||u.length===0)return;const p=Xo(W);await Pe(p,u)||(console.log("[SidePanel] Состояние не восстановлено, проверяем сброс плагина"),f&&!re(f)&&(console.log("[SidePanel] Плагин не разрешен для новой страницы, сбрасываем состояние"),c(null),C(!1),v(null),X(null)))})()},[W,u,f]);const j=ie==="dark"||ie==="system"&&!K;return y.jsx(f0,{children:y.jsxs("div",{className:gg("App",j?"bg-gray-800":"bg-slate-50"),children:[y.jsx("header",{className:gg("App-header",j?"text-gray-100":"text-gray-900"),children:y.jsxs("div",{className:"header-controls",children:[y.jsx(Sp,{theme:ie,isLight:K,onToggle:Ro.toggle,isInSidebar:!0}),y.jsx("button",{onClick:()=>chrome.runtime.openOptionsPage(),className:"settings-btn",title:"Открыть настройки",children:y.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"3"}),y.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"})]})})]})}),y.jsx("main",{className:"side-panel-main",children:y.jsxs("section",{className:"plugins-section",children:[y.jsx("h3",{children:"Доступные плагины"}),y.jsx("div",{className:"plugins-grid",children:(()=>{console.log("[SidePanel] === РЕНДЕР ==="),console.log("[SidePanel] Состояние plugins:",u),console.log("[SidePanel] Состояние currentTabUrl:",W);const h=u.filter(re);return console.log("[SidePanel] Всего плагинов:",u.length),console.log("[SidePanel] Отфильтрованных плагинов:",h.length),console.log("[SidePanel] Отфильтрованные плагины:",h),h.map(p=>y.jsx(d0,{plugin:p,selected:f?.id===p.id,onClick:()=>pe(p),isLight:!j},p.id))})()})]})}),S&&f&&y.jsx(pp,{plugin:f,currentView:D,isRunning:U===f.id,isPaused:q===f.id,currentTabUrl:W,onStart:ge,onPause:Je,onStop:Ge,onClose:pt}),y.jsx(vp,{toasts:$,onRemove:z})]})})},m0=()=>{const u=document.querySelector("#app-container");if(!u)throw new Error("Can not find #app-container");c0.createRoot(u).render(y.jsx(g0,{}))};m0(); +//# sourceMappingURL=index-B8ToW130.js.map diff --git a/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map b/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map new file mode 100644 index 00000000..6ac1e45a --- /dev/null +++ b/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index-B8ToW130.js","sources":["../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react-jsx-runtime.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js","../../../../pages/side-panel/src/components/DraftStatus.tsx","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/index.js","../../../../pages/options/src/hooks/useTranslations.ts","../../../../pages/options/src/components/ToggleButton.tsx","../../../../pages/options/src/components/ErrorDisplay.tsx","../../../../pages/options/src/components/LocalErrorBoundary.tsx","../../../../pages/options/src/utils/encryption.ts","../../../../pages/options/src/hooks/usePluginSettings.ts","../../../../pages/options/src/components/LLMSelector.tsx","../../../../pages/options/src/hooks/useAIKeys.ts","../../../../pages/options/src/components/PluginDetails.tsx","../../../../pages/side-panel/src/components/PluginDetails.tsx","../../../../packages/shared/lib/utils/helpers.ts","../../../../pages/side-panel/src/hooks/useLazyChatSync.ts","../../../../node_modules/.pnpm/file-saver@2.0.5/node_modules/file-saver/dist/FileSaver.min.js","../../../../packages/storage/dist/lib/base/enums.js","../../../../packages/storage/dist/lib/base/base.js","../../../../packages/storage/dist/lib/impl/example-theme-storage.js","../../../../packages/storage/dist/lib/impl/example-chat-alignment-storage.js","../../../../pages/side-panel/src/components/PluginControlPanel.tsx","../../../../pages/side-panel/src/components/ToastNotifications.tsx","../../../../pages/options/src/components/ThemeSwitcher.tsx","../../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../../node_modules/.pnpm/tailwind-merge@3.3.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs","../../../../packages/ui/dist/lib/utils.js","../../../../node_modules/.pnpm/deepmerge@4.3.1/node_modules/deepmerge/dist/cjs.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.production.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom-client.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/client.js","../../../../pages/side-panel/src/components/ErrorDisplay.tsx","../../../../pages/side-panel/src/components/LocalErrorBoundary.tsx","../../../../pages/side-panel/src/components/PluginCard.tsx","../../../../pages/side-panel/src/SidePanel.tsx","../../../../pages/side-panel/src/index.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","import './DraftStatus.css';\n\ninterface DraftStatusProps {\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n messageLength: number;\n minLength: number;\n maxLength: number;\n}\n\nexport const DraftStatus: React.FC = ({\n isDraftSaved,\n isDraftLoading,\n draftError,\n messageLength,\n minLength,\n maxLength,\n}) => {\n const getStatusIcon = () => {\n if (isDraftLoading) {\n // CSS-анимированный круг для лоадера\n return
;\n }\n\n if (draftError) {\n return (\n \n \n \n \n \n );\n }\n\n if (isDraftSaved) {\n return (\n \n \n \n );\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return (\n \n \n \n \n );\n }\n\n return null;\n };\n\n const getStatusText = () => {\n if (isDraftLoading) {\n return 'Загрузка черновика...';\n }\n\n if (draftError) {\n return draftError;\n }\n\n if (isDraftSaved) {\n return 'Черновик сохранен';\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return `Еще ${minLength - messageLength} символов для сохранения`;\n }\n\n if (messageLength >= minLength && messageLength <= maxLength) {\n return 'Черновик будет сохранен автоматически';\n }\n\n if (messageLength > maxLength) {\n return 'Превышен лимит символов';\n }\n\n return '';\n };\n\n const getStatusClass = () => {\n if (isDraftLoading) return 'draft-status loading';\n if (draftError) return 'draft-status error';\n if (isDraftSaved) return 'draft-status saved';\n if (messageLength > 0 && messageLength < minLength) return 'draft-status pending';\n if (messageLength >= minLength && messageLength <= maxLength) return 'draft-status ready';\n if (messageLength > maxLength) return 'draft-status error';\n return 'draft-status';\n };\n\n if (messageLength === 0 && !isDraftLoading && !draftError) {\n return null; // Не показываем статус, если нет текста\n }\n\n return (\n
\n {getStatusIcon()}\n {getStatusText()}\n
\n );\n};\n","/**\n * @license React\n * react.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\"),\n REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\"),\n MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar ReactNoopUpdateQueue = {\n isMounted: function () {\n return !1;\n },\n enqueueForceUpdate: function () {},\n enqueueReplaceState: function () {},\n enqueueSetState: function () {}\n },\n assign = Object.assign,\n emptyObject = {};\nfunction Component(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nComponent.prototype.isReactComponent = {};\nComponent.prototype.setState = function (partialState, callback) {\n if (\n \"object\" !== typeof partialState &&\n \"function\" !== typeof partialState &&\n null != partialState\n )\n throw Error(\n \"takes an object of state variables to update or a function which returns an object of state variables.\"\n );\n this.updater.enqueueSetState(this, partialState, callback, \"setState\");\n};\nComponent.prototype.forceUpdate = function (callback) {\n this.updater.enqueueForceUpdate(this, callback, \"forceUpdate\");\n};\nfunction ComponentDummy() {}\nComponentDummy.prototype = Component.prototype;\nfunction PureComponent(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nvar pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());\npureComponentPrototype.constructor = PureComponent;\nassign(pureComponentPrototype, Component.prototype);\npureComponentPrototype.isPureReactComponent = !0;\nvar isArrayImpl = Array.isArray;\nfunction noop() {}\nvar ReactSharedInternals = { H: null, A: null, T: null, S: null },\n hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction ReactElement(type, key, props) {\n var refProp = props.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== refProp ? refProp : null,\n props: props\n };\n}\nfunction cloneAndReplaceKey(oldElement, newKey) {\n return ReactElement(oldElement.type, newKey, oldElement.props);\n}\nfunction isValidElement(object) {\n return (\n \"object\" === typeof object &&\n null !== object &&\n object.$$typeof === REACT_ELEMENT_TYPE\n );\n}\nfunction escape(key) {\n var escaperLookup = { \"=\": \"=0\", \":\": \"=2\" };\n return (\n \"$\" +\n key.replace(/[=:]/g, function (match) {\n return escaperLookup[match];\n })\n );\n}\nvar userProvidedKeyEscapeRegex = /\\/+/g;\nfunction getElementKey(element, index) {\n return \"object\" === typeof element && null !== element && null != element.key\n ? escape(\"\" + element.key)\n : index.toString(36);\n}\nfunction resolveThenable(thenable) {\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n default:\n switch (\n (\"string\" === typeof thenable.status\n ? thenable.then(noop, noop)\n : ((thenable.status = \"pending\"),\n thenable.then(\n function (fulfilledValue) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"fulfilled\"),\n (thenable.value = fulfilledValue));\n },\n function (error) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"rejected\"), (thenable.reason = error));\n }\n )),\n thenable.status)\n ) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n }\n }\n throw thenable;\n}\nfunction mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {\n var type = typeof children;\n if (\"undefined\" === type || \"boolean\" === type) children = null;\n var invokeCallback = !1;\n if (null === children) invokeCallback = !0;\n else\n switch (type) {\n case \"bigint\":\n case \"string\":\n case \"number\":\n invokeCallback = !0;\n break;\n case \"object\":\n switch (children.$$typeof) {\n case REACT_ELEMENT_TYPE:\n case REACT_PORTAL_TYPE:\n invokeCallback = !0;\n break;\n case REACT_LAZY_TYPE:\n return (\n (invokeCallback = children._init),\n mapIntoArray(\n invokeCallback(children._payload),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n )\n );\n }\n }\n if (invokeCallback)\n return (\n (callback = callback(children)),\n (invokeCallback =\n \"\" === nameSoFar ? \".\" + getElementKey(children, 0) : nameSoFar),\n isArrayImpl(callback)\n ? ((escapedPrefix = \"\"),\n null != invokeCallback &&\n (escapedPrefix =\n invokeCallback.replace(userProvidedKeyEscapeRegex, \"$&/\") + \"/\"),\n mapIntoArray(callback, array, escapedPrefix, \"\", function (c) {\n return c;\n }))\n : null != callback &&\n (isValidElement(callback) &&\n (callback = cloneAndReplaceKey(\n callback,\n escapedPrefix +\n (null == callback.key ||\n (children && children.key === callback.key)\n ? \"\"\n : (\"\" + callback.key).replace(\n userProvidedKeyEscapeRegex,\n \"$&/\"\n ) + \"/\") +\n invokeCallback\n )),\n array.push(callback)),\n 1\n );\n invokeCallback = 0;\n var nextNamePrefix = \"\" === nameSoFar ? \".\" : nameSoFar + \":\";\n if (isArrayImpl(children))\n for (var i = 0; i < children.length; i++)\n (nameSoFar = children[i]),\n (type = nextNamePrefix + getElementKey(nameSoFar, i)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (((i = getIteratorFn(children)), \"function\" === typeof i))\n for (\n children = i.call(children), i = 0;\n !(nameSoFar = children.next()).done;\n\n )\n (nameSoFar = nameSoFar.value),\n (type = nextNamePrefix + getElementKey(nameSoFar, i++)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (\"object\" === type) {\n if (\"function\" === typeof children.then)\n return mapIntoArray(\n resolveThenable(children),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n );\n array = String(children);\n throw Error(\n \"Objects are not valid as a React child (found: \" +\n (\"[object Object]\" === array\n ? \"object with keys {\" + Object.keys(children).join(\", \") + \"}\"\n : array) +\n \"). If you meant to render a collection of children, use an array instead.\"\n );\n }\n return invokeCallback;\n}\nfunction mapChildren(children, func, context) {\n if (null == children) return children;\n var result = [],\n count = 0;\n mapIntoArray(children, result, \"\", \"\", function (child) {\n return func.call(context, child, count++);\n });\n return result;\n}\nfunction lazyInitializer(payload) {\n if (-1 === payload._status) {\n var ctor = payload._result;\n ctor = ctor();\n ctor.then(\n function (moduleObject) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 1), (payload._result = moduleObject);\n },\n function (error) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 2), (payload._result = error);\n }\n );\n -1 === payload._status && ((payload._status = 0), (payload._result = ctor));\n }\n if (1 === payload._status) return payload._result.default;\n throw payload._result;\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n Children = {\n map: mapChildren,\n forEach: function (children, forEachFunc, forEachContext) {\n mapChildren(\n children,\n function () {\n forEachFunc.apply(this, arguments);\n },\n forEachContext\n );\n },\n count: function (children) {\n var n = 0;\n mapChildren(children, function () {\n n++;\n });\n return n;\n },\n toArray: function (children) {\n return (\n mapChildren(children, function (child) {\n return child;\n }) || []\n );\n },\n only: function (children) {\n if (!isValidElement(children))\n throw Error(\n \"React.Children.only expected to receive a single React element child.\"\n );\n return children;\n }\n };\nexports.Activity = REACT_ACTIVITY_TYPE;\nexports.Children = Children;\nexports.Component = Component;\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.Profiler = REACT_PROFILER_TYPE;\nexports.PureComponent = PureComponent;\nexports.StrictMode = REACT_STRICT_MODE_TYPE;\nexports.Suspense = REACT_SUSPENSE_TYPE;\nexports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n ReactSharedInternals;\nexports.__COMPILER_RUNTIME = {\n __proto__: null,\n c: function (size) {\n return ReactSharedInternals.H.useMemoCache(size);\n }\n};\nexports.cache = function (fn) {\n return function () {\n return fn.apply(null, arguments);\n };\n};\nexports.cacheSignal = function () {\n return null;\n};\nexports.cloneElement = function (element, config, children) {\n if (null === element || void 0 === element)\n throw Error(\n \"The argument must be a React element, but you passed \" + element + \".\"\n );\n var props = assign({}, element.props),\n key = element.key;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n !hasOwnProperty.call(config, propName) ||\n \"key\" === propName ||\n \"__self\" === propName ||\n \"__source\" === propName ||\n (\"ref\" === propName && void 0 === config.ref) ||\n (props[propName] = config[propName]);\n var propName = arguments.length - 2;\n if (1 === propName) props.children = children;\n else if (1 < propName) {\n for (var childArray = Array(propName), i = 0; i < propName; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n return ReactElement(element.type, key, props);\n};\nexports.createContext = function (defaultValue) {\n defaultValue = {\n $$typeof: REACT_CONTEXT_TYPE,\n _currentValue: defaultValue,\n _currentValue2: defaultValue,\n _threadCount: 0,\n Provider: null,\n Consumer: null\n };\n defaultValue.Provider = defaultValue;\n defaultValue.Consumer = {\n $$typeof: REACT_CONSUMER_TYPE,\n _context: defaultValue\n };\n return defaultValue;\n};\nexports.createElement = function (type, config, children) {\n var propName,\n props = {},\n key = null;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n hasOwnProperty.call(config, propName) &&\n \"key\" !== propName &&\n \"__self\" !== propName &&\n \"__source\" !== propName &&\n (props[propName] = config[propName]);\n var childrenLength = arguments.length - 2;\n if (1 === childrenLength) props.children = children;\n else if (1 < childrenLength) {\n for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n if (type && type.defaultProps)\n for (propName in ((childrenLength = type.defaultProps), childrenLength))\n void 0 === props[propName] &&\n (props[propName] = childrenLength[propName]);\n return ReactElement(type, key, props);\n};\nexports.createRef = function () {\n return { current: null };\n};\nexports.forwardRef = function (render) {\n return { $$typeof: REACT_FORWARD_REF_TYPE, render: render };\n};\nexports.isValidElement = isValidElement;\nexports.lazy = function (ctor) {\n return {\n $$typeof: REACT_LAZY_TYPE,\n _payload: { _status: -1, _result: ctor },\n _init: lazyInitializer\n };\n};\nexports.memo = function (type, compare) {\n return {\n $$typeof: REACT_MEMO_TYPE,\n type: type,\n compare: void 0 === compare ? null : compare\n };\n};\nexports.startTransition = function (scope) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = scope(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n returnValue.then(noop, reportGlobalError);\n } catch (error) {\n reportGlobalError(error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n};\nexports.unstable_useCacheRefresh = function () {\n return ReactSharedInternals.H.useCacheRefresh();\n};\nexports.use = function (usable) {\n return ReactSharedInternals.H.use(usable);\n};\nexports.useActionState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useActionState(action, initialState, permalink);\n};\nexports.useCallback = function (callback, deps) {\n return ReactSharedInternals.H.useCallback(callback, deps);\n};\nexports.useContext = function (Context) {\n return ReactSharedInternals.H.useContext(Context);\n};\nexports.useDebugValue = function () {};\nexports.useDeferredValue = function (value, initialValue) {\n return ReactSharedInternals.H.useDeferredValue(value, initialValue);\n};\nexports.useEffect = function (create, deps) {\n return ReactSharedInternals.H.useEffect(create, deps);\n};\nexports.useEffectEvent = function (callback) {\n return ReactSharedInternals.H.useEffectEvent(callback);\n};\nexports.useId = function () {\n return ReactSharedInternals.H.useId();\n};\nexports.useImperativeHandle = function (ref, create, deps) {\n return ReactSharedInternals.H.useImperativeHandle(ref, create, deps);\n};\nexports.useInsertionEffect = function (create, deps) {\n return ReactSharedInternals.H.useInsertionEffect(create, deps);\n};\nexports.useLayoutEffect = function (create, deps) {\n return ReactSharedInternals.H.useLayoutEffect(create, deps);\n};\nexports.useMemo = function (create, deps) {\n return ReactSharedInternals.H.useMemo(create, deps);\n};\nexports.useOptimistic = function (passthrough, reducer) {\n return ReactSharedInternals.H.useOptimistic(passthrough, reducer);\n};\nexports.useReducer = function (reducer, initialArg, init) {\n return ReactSharedInternals.H.useReducer(reducer, initialArg, init);\n};\nexports.useRef = function (initialValue) {\n return ReactSharedInternals.H.useRef(initialValue);\n};\nexports.useState = function (initialState) {\n return ReactSharedInternals.H.useState(initialState);\n};\nexports.useSyncExternalStore = function (\n subscribe,\n getSnapshot,\n getServerSnapshot\n) {\n return ReactSharedInternals.H.useSyncExternalStore(\n subscribe,\n getSnapshot,\n getServerSnapshot\n );\n};\nexports.useTransition = function () {\n return ReactSharedInternals.H.useTransition();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react.production.js');\n} else {\n module.exports = require('./cjs/react.development.js');\n}\n","import * as React from 'react';\n\n// Импортируем переводы\nimport enTranslations from '../locales/en.json';\nimport ruTranslations from '../locales/ru.json';\n\nconst translations = {\n en: enTranslations,\n ru: ruTranslations,\n};\n\nexport type Locale = 'en' | 'ru';\n\nexport const useTranslations = (locale: Locale = 'en') => {\n const t = React.useMemo(() => {\n const dict: any = translations[locale] || {};\n\n // Функция для получения значения по пути с точками\n const getNestedValue = (obj: any, path: string): string => {\n return path.split('.').reduce((current, key) => {\n return current && current[key] ? current[key] : undefined;\n }, obj);\n };\n\n return (key: string) => {\n const value = getNestedValue(dict, key);\n return value !== undefined ? value : key;\n };\n }, [locale, translations]);\n\n return { t, locale };\n};\n","import React from 'react';\n\ninterface ToggleButtonProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n label?: React.ReactNode;\n iconOn?: React.ReactNode; // AI-First: иконка для состояния ON\n iconOff?: React.ReactNode; // AI-First: иконка для состояния OFF\n}\n\nconst ToggleButton: React.FC = ({ checked, onChange, disabled, label, iconOn, iconOff }) => (\n \n);\n\nexport default ToggleButton;\n","import React from 'react';\n\nconst ErrorDisplay: React.FC<{ error?: Error; resetError?: () => void }> = ({ error, resetError }) => (\n
\n

Произошла ошибка

\n {error &&
{error.message}
}\n {resetError && }\n
\n);\n\nexport default ErrorDisplay;\n","import React from 'react';\nimport ErrorDisplay from './ErrorDisplay';\n\nconst LocalErrorBoundary: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [error, setError] = React.useState(null);\n const resetError = React.useCallback(() => setError(null), []);\n\n if (error) {\n return ;\n }\n\n try {\n return <>{children};\n } catch (err) {\n setError(err as Error);\n return null;\n }\n};\n\nexport default LocalErrorBoundary;\n","/**\n * Утилиты для шифрования API-ключей\n * Использует Web Crypto API для безопасного хранения в chrome.storage.local\n */\n\nexport class APIKeyEncryption {\n private static readonly ALGORITHM = 'AES-GCM';\n private static readonly KEY_LENGTH = 256;\n private static readonly IV_LENGTH = 12;\n\n /**\n * Получает или создает ключ шифрования из chrome.storage.local\n */\n private static async getEncryptionKey(): Promise {\n try {\n // Проверяем, есть ли уже ключ в хранилище\n const result = await chrome.storage.local.get(['encryptionKey']);\n if (result.encryptionKey) {\n // Восстанавливаем ключ из массива байтов\n const keyData = new Uint8Array(result.encryptionKey);\n return await crypto.subtle.importKey(\n 'raw',\n keyData,\n this.ALGORITHM,\n false,\n ['encrypt', 'decrypt']\n );\n }\n\n // Создаем новый ключ\n const key = await crypto.subtle.generateKey(\n {\n name: this.ALGORITHM,\n length: this.KEY_LENGTH,\n },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Сохраняем ключ в хранилище\n const exportedKey = await crypto.subtle.exportKey('raw', key);\n const keyArray = new Uint8Array(exportedKey);\n await chrome.storage.local.set({\n encryptionKey: Array.from(keyArray)\n });\n\n return key;\n } catch (error) {\n console.error('Failed to get/create encryption key:', error);\n throw new Error('Не удалось инициализировать шифрование');\n }\n }\n\n /**\n * Шифрует текст\n */\n static async encrypt(text: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n const encodedText = new TextEncoder().encode(text);\n\n const encrypted = await crypto.subtle.encrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n encodedText\n );\n\n // Объединяем IV и зашифрованные данные\n const encryptedArray = new Uint8Array(encrypted);\n const resultArray = new Uint8Array(iv.length + encryptedArray.length);\n resultArray.set(iv);\n resultArray.set(encryptedArray, iv.length);\n\n // Кодируем в base64 для хранения в storage\n return btoa(String.fromCharCode(...resultArray));\n } catch (error) {\n console.error('Encryption failed:', error);\n throw new Error('Ошибка шифрования');\n }\n }\n\n /**\n * Расшифровывает текст\n */\n static async decrypt(encryptedText: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n\n // Декодируем из base64\n const encryptedArray = new Uint8Array(\n atob(encryptedText).split('').map(char => char.charCodeAt(0))\n );\n\n // Извлекаем IV и зашифрованные данные\n const iv = encryptedArray.slice(0, this.IV_LENGTH);\n const data = encryptedArray.slice(this.IV_LENGTH);\n\n const decrypted = await crypto.subtle.decrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n data\n );\n\n return new TextDecoder().decode(decrypted);\n } catch (error) {\n console.error('Decryption failed:', error);\n throw new Error('Ошибка расшифрования');\n }\n }\n\n /**\n * Валидация API ключа\n */\n static validateAPIKey(key: string): { isValid: boolean; error?: string } {\n if (!key || typeof key !== 'string') {\n return { isValid: false, error: 'Ключ не может быть пустым' };\n }\n\n if (key.length < 10) {\n return { isValid: false, error: 'Ключ слишком короткий' };\n }\n\n if (key.length > 200) {\n return { isValid: false, error: 'Ключ слишком длинный' };\n }\n\n // Проверяем на наличие потенциально опасных символов\n if (/[<>\\\"'&]/.test(key)) {\n return { isValid: false, error: 'Ключ содержит недопустимые символы' };\n }\n\n return { isValid: true };\n }\n}\n\n/**\n * Утилиты для безопасной работы с API ключами\n */\nexport class APIKeyManager {\n /**\n * Сохраняет API ключ с шифрованием\n */\n static async saveEncryptedKey(keyId: string, apiKey: string): Promise {\n try {\n const validation = APIKeyEncryption.validateAPIKey(apiKey);\n if (!validation.isValid) {\n throw new Error(validation.error);\n }\n\n const encryptedKey = await APIKeyEncryption.encrypt(apiKey);\n const keys = await this.getAllEncryptedKeys();\n\n keys[keyId] = encryptedKey;\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to save encrypted API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает расшифрованный API ключ\n */\n static async getDecryptedKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n const encryptedKey = keys[keyId];\n\n if (!encryptedKey) {\n return null;\n }\n\n return await APIKeyEncryption.decrypt(encryptedKey);\n } catch (error) {\n console.error('Failed to get decrypted API key:', error);\n return null;\n }\n }\n\n /**\n * Удаляет API ключ\n */\n static async removeKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n delete keys[keyId];\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to remove API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает все зашифрованные ключи\n */\n private static async getAllEncryptedKeys(): Promise> {\n try {\n const result = await chrome.storage.local.get(['encryptedApiKeys']);\n return result.encryptedApiKeys || {};\n } catch (error) {\n console.error('Failed to get encrypted keys:', error);\n return {};\n }\n }\n\n /**\n * Получает все ID ключей (без расшифровки)\n */\n static async getAllKeyIds(): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return Object.keys(keys);\n } catch (error) {\n console.error('Failed to get key IDs:', error);\n return [];\n }\n }\n\n /**\n * Проверяет, существует ли ключ\n */\n static async keyExists(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return keyId in keys;\n } catch (error) {\n console.error('Failed to check key existence:', error);\n return false;\n }\n }\n}","import * as React from 'react';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface PluginSettings {\n basic_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n deep_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n api_keys: {\n default: string; // encrypted\n };\n}\n\nexport interface PluginPromptSettings {\n llm: string;\n custom_prompt: string;\n}\n\nconst STORAGE_KEY = 'plugin-ozon-analyzer-settings';\n\nconst DEFAULT_SETTINGS: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.',\n },\n en: {\n llm: '',\n custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.',\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.',\n },\n en: {\n llm: '',\n custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.',\n },\n },\n api_keys: {\n default: '',\n },\n};\n\nexport const usePluginSettings = () => {\n const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);\n const [isLoading, setIsLoading] = React.useState(true);\n\n React.useEffect(() => {\n loadSettings();\n }, []);\n\n const loadSettings = async () => {\n try {\n setIsLoading(true);\n const result = await chrome.storage.local.get([STORAGE_KEY]);\n const storedSettings = result[STORAGE_KEY];\n\n if (storedSettings) {\n // Расшифровываем API ключи\n const decryptedApiKeys = { ...storedSettings.api_keys };\n if (storedSettings.api_keys?.default) {\n decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || '';\n }\n\n setSettings({\n ...DEFAULT_SETTINGS,\n ...storedSettings,\n basic_analysis: {\n ru: {\n ...DEFAULT_SETTINGS.basic_analysis.ru,\n ...storedSettings.basic_analysis?.ru,\n },\n en: {\n ...DEFAULT_SETTINGS.basic_analysis.en,\n ...storedSettings.basic_analysis?.en,\n },\n },\n deep_analysis: {\n ru: {\n ...DEFAULT_SETTINGS.deep_analysis.ru,\n ...storedSettings.deep_analysis?.ru,\n },\n en: {\n ...DEFAULT_SETTINGS.deep_analysis.en,\n ...storedSettings.deep_analysis?.en,\n },\n },\n api_keys: decryptedApiKeys,\n });\n } else {\n setSettings(DEFAULT_SETTINGS);\n }\n } catch (error) {\n console.error('Failed to load plugin settings:', error);\n setSettings(DEFAULT_SETTINGS);\n } finally {\n setIsLoading(false);\n }\n };\n\n const saveSettings = async (newSettings: PluginSettings) => {\n try {\n // Шифруем API ключи перед сохранением\n const settingsToSave = { ...newSettings };\n if (newSettings.api_keys?.default) {\n await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default);\n } else {\n await APIKeyManager.removeKey('ozon-analyzer-default');\n }\n\n // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON\n settingsToSave.api_keys = { default: '' };\n\n await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave });\n setSettings(newSettings);\n } catch (error) {\n console.error('Failed to save plugin settings:', error);\n throw error;\n }\n };\n\n const updateBasicAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n basic_analysis: {\n ...settings.basic_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateDeepAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n deep_analysis: {\n ...settings.deep_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateAPIKey = async (key: string) => {\n const newSettings = {\n ...settings,\n api_keys: {\n default: key,\n },\n };\n await saveSettings(newSettings);\n };\n\n const getBasicAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.basic_analysis[language];\n };\n\n const getDeepAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.deep_analysis[language];\n };\n\n const getAPIKey = (): string => {\n return settings.api_keys?.default || '';\n };\n\n const resetToDefaults = async () => {\n await saveSettings(DEFAULT_SETTINGS);\n };\n\n return {\n settings,\n isLoading,\n updateBasicAnalysisSettings,\n updateDeepAnalysisSettings,\n updateAPIKey,\n getBasicAnalysisSettings,\n getDeepAnalysisSettings,\n getAPIKey,\n resetToDefaults,\n saveSettings,\n loadSettings,\n };\n};","import React, { useState, useEffect, useRef } from 'react';\nimport { usePluginSettings } from '../hooks/usePluginSettings';\nimport { AIKey } from '../hooks/useAIKeys';\nimport { APIKeyManager } from '../utils/encryption';\n\ninterface LLMSelectorProps {\n promptType: 'basic_analysis' | 'deep_analysis';\n language: 'ru' | 'en';\n globalAIKeys: AIKey[];\n defaultLLMCurl: string;\n hasDefaultLLM: boolean;\n onLLMChange: (llm: string, apiKey?: string) => void;\n}\n\nconst LLMSelector: React.FC = ({\n promptType,\n language,\n globalAIKeys,\n defaultLLMCurl,\n hasDefaultLLM,\n onLLMChange,\n}) => {\n const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings();\n const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl);\n const [apiKey, setApiKey] = useState('');\n const saveTimeoutRef = useRef();\n\n // Загружаем API-ключ при монтировании компонента\n useEffect(() => {\n const loadApiKey = async () => {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n const key = await APIKeyManager.getDecryptedKey(keyId) || '';\n setApiKey(key);\n };\n loadApiKey();\n }, [promptType, language]);\n\n // Загружаем LLM при изменении promptType/language\n useEffect(() => {\n const currentSettings = promptType === 'basic_analysis'\n ? settings.basic_analysis[language]\n : settings.deep_analysis[language];\n\n let initialLLM = '';\n\n if (currentSettings) {\n const savedLLM = currentSettings.llm;\n initialLLM = savedLLM || (hasDefaultLLM ? 'default' : '');\n } else {\n initialLLM = hasDefaultLLM ? 'default' : '';\n }\n\n setSelectedLLM(initialLLM);\n }, [promptType, language, settings, hasDefaultLLM]);\n\n // Сохраняем выбор LLM\n const handleLLMChange = async (newLLM: string) => {\n setSelectedLLM(newLLM);\n\n const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings;\n await updateFn(language, {\n llm: newLLM,\n custom_prompt: promptType === 'basic_analysis'\n ? settings.basic_analysis[language].custom_prompt\n : settings.deep_analysis[language].custom_prompt,\n });\n\n onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined);\n };\n\n // Сохраняем API ключ\n const handleApiKeyChange = (newApiKey: string) => {\n setApiKey(newApiKey);\n\n if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);\n\n saveTimeoutRef.current = setTimeout(async () => {\n try {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n await APIKeyManager.saveEncryptedKey(keyId, newApiKey);\n onLLMChange(selectedLLM, newApiKey);\n } catch (error) {\n console.error('Failed to save API key:', error);\n }\n }, 500);\n };\n\n // Получаем список опций для селекта\n const getLLMOptions = () => {\n const options = [\n { value: 'default', label: 'Default LLM' },\n ...globalAIKeys.map(key => ({\n value: key.id,\n label: key.name,\n })),\n ];\n return options;\n };\n\n return (\n
\n \n\n handleLLMChange(e.target.value)}\n placeholder=\"Выберите нейросеть\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px',\n marginBottom: '8px'\n }}\n >\n {getLLMOptions().map(option => (\n \n ))}\n \n\n {selectedLLM === 'default' && (\n
\n \n handleApiKeyChange(e.target.value)}\n placeholder=\"Введите API ключ\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n />\n
\n )}\n
\n );\n};\n\nexport default LLMSelector;","import * as React from 'react';\nimport { useTranslations } from './useTranslations';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface AIKey {\n id: string;\n name: string;\n key: string;\n status: 'configured' | 'not_configured' | 'testing';\n isFixed?: boolean;\n isFree?: boolean;\n}\n\nexport const useAIKeys = () => {\n const { t } = useTranslations('ru');\n const [aiKeys, setAiKeys] = React.useState([\n {\n id: 'gemini-flash-lite',\n name: 'Google Gemini Flash Lite - Базовый анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n {\n id: 'gemini-pro',\n name: 'Gemini 2.5 Pro - Глубокий анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n ]);\n const [customKeys, setCustomKeys] = React.useState([]);\n\n // Рефы для отложенного сохранения ключей\n const saveTimeoutsRef = React.useRef>({});\n\n // Load AI keys on mount and cleanup timeouts on unmount\n React.useEffect(() => {\n loadAIKeys();\n\n // Cleanup function\n return () => {\n // Очищаем все активные таймауты\n Object.values(saveTimeoutsRef.current).forEach(timeout => {\n clearTimeout(timeout);\n });\n saveTimeoutsRef.current = {};\n };\n }, []);\n\n const loadAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to load AI keys...');\n\n // Загружаем зашифрованные ключи\n const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro'];\n const fixedKeysPromises = fixedKeyIds.map(async (keyId) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(keyId);\n console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty');\n return { keyId, decryptedKey };\n });\n\n const fixedKeysResults = await Promise.all(fixedKeysPromises);\n console.log('[useAIKeys] Fixed keys results:', fixedKeysResults);\n\n setAiKeys(prev =>\n prev.map(key => {\n const result = fixedKeysResults.find(r => r.keyId === key.id);\n console.log(`[useAIKeys] Setting key ${key.id}:`, result?.decryptedKey ? 'configured' : 'not_configured');\n return {\n ...key,\n key: result?.decryptedKey || '',\n status: (result?.decryptedKey ? 'configured' : 'not_configured') as AIKey['status'],\n };\n }),\n );\n\n // Загружаем пользовательские ключи (метаданные)\n const result = await chrome.storage.local.get(['customKeys']);\n console.log('[useAIKeys] Custom keys metadata:', result.customKeys);\n if (result.customKeys) {\n // Для пользовательских ключей также используем шифрование\n const customKeysWithDecryption = await Promise.all(\n (result.customKeys as AIKey[]).map(async (key: AIKey) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(key.id);\n console.log(`[useAIKeys] Custom key ${key.id}:`, decryptedKey ? 'present' : 'empty');\n return {\n ...key,\n key: decryptedKey || '',\n status: (decryptedKey ? 'configured' : 'not_configured') as AIKey['status']\n };\n })\n );\n console.log('[useAIKeys] Setting custom keys:', customKeysWithDecryption);\n setCustomKeys(customKeysWithDecryption);\n } else {\n console.log('[useAIKeys] No custom keys found');\n setCustomKeys([]);\n }\n\n console.log('[useAIKeys] AI keys loaded successfully');\n } catch (error) {\n console.error('Failed to load AI keys:', error);\n // В случае ошибки шифрования показываем пустые ключи\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n key: '',\n status: 'not_configured' as AIKey['status'],\n })),\n );\n setCustomKeys([]);\n }\n };\n\n const saveAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to save AI keys...');\n console.log('[useAIKeys] Current aiKeys:', aiKeys);\n console.log('[useAIKeys] Current customKeys:', customKeys);\n\n // Сохраняем фиксированные ключи с шифрованием\n const saveFixedKeysPromises = aiKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving fixed key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing fixed key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveFixedKeysPromises);\n\n // Сохраняем пользовательские ключи с шифрованием\n const saveCustomKeysPromises = customKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving custom key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing custom key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveCustomKeysPromises);\n\n // Сохраняем метаданные пользовательских ключей (без самих ключей)\n const customKeysMetadata = customKeys.map(key => ({\n id: key.id,\n name: key.name,\n isFixed: false,\n isFree: false,\n // key и status не сохраняем в метаданных для безопасности\n }));\n\n console.log('[useAIKeys] Saving custom keys metadata:', customKeysMetadata);\n await chrome.storage.local.set({\n customKeys: customKeysMetadata,\n });\n\n // Обновляем статусы в состоянии\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status'],\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status']\n }))\n );\n\n console.log('[useAIKeys] AI keys saved successfully');\n alert(t('options.settings.aiKeys.messages.saved'));\n } catch (error) {\n console.error('Failed to save AI keys:', error);\n alert(t('options.settings.aiKeys.messages.saveError'));\n }\n };\n\n const testAIKeys = async () => {\n // Set testing status\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n // Simulate API testing with timeout\n setTimeout(() => {\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n alert(t('options.settings.aiKeys.messages.testComplete'));\n }, 2000);\n };\n\n const addCustomKey = () => {\n const newKey: AIKey = {\n id: `custom-${Date.now()}`,\n name: `Пользовательский ключ ${customKeys.length + 1}`,\n key: '',\n status: 'not_configured' as const,\n };\n setCustomKeys(prev => [...prev, newKey]);\n };\n\n const removeCustomKey = async (id: string) => {\n try {\n // Удаляем зашифрованный ключ\n await APIKeyManager.removeKey(id);\n\n // Удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n } catch (error) {\n console.error('Failed to remove custom key:', error);\n // Даже если удаление зашифрованного ключа не удалось, удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n }\n };\n\n const updateKey = (id: string, value: string, isCustom = false) => {\n console.log(`[useAIKeys] Updating key ${id} with value:`, value ? 'present' : 'empty');\n\n // Обновляем состояние немедленно для лучшего UX\n if (isCustom) {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n } else {\n setAiKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n }\n\n // Отменяем предыдущий таймаут для этого ключа\n if (saveTimeoutsRef.current[id]) {\n clearTimeout(saveTimeoutsRef.current[id]);\n }\n\n // Устанавливаем новый таймаут для отложенного сохранения\n saveTimeoutsRef.current[id] = setTimeout(async () => {\n try {\n console.log(`[useAIKeys] Auto-saving key ${id} after delay`);\n if (value) {\n await APIKeyManager.saveEncryptedKey(id, value);\n } else {\n await APIKeyManager.removeKey(id);\n }\n\n // Для пользовательских ключей также обновляем метаданные\n if (isCustom) {\n const result = await chrome.storage.local.get(['customKeys']);\n const customKeysMetadata = result.customKeys || [];\n const updatedMetadata = customKeysMetadata.map((key: any) =>\n key.id === id ? { ...key, name: key.name } : key\n );\n\n // Если ключ не существует в метаданных, добавляем его\n const existingKeyIndex = updatedMetadata.findIndex((key: any) => key.id === id);\n if (existingKeyIndex === -1 && value) {\n updatedMetadata.push({\n id,\n name: `Пользовательский ключ ${customKeysMetadata.length + 1}`,\n isFixed: false,\n isFree: false,\n });\n }\n\n await chrome.storage.local.set({\n customKeys: updatedMetadata.filter((key: any) => {\n // Убираем из метаданных ключи, которые были удалены\n return value || key.id !== id;\n }),\n });\n }\n\n console.log(`[useAIKeys] Key ${id} auto-saved successfully`);\n } catch (error) {\n console.error(`[useAIKeys] Failed to auto-save key ${id}:`, error);\n } finally {\n // Убираем таймаут из рефа\n delete saveTimeoutsRef.current[id];\n }\n }, 1000); // 1 секунда задержки перед сохранением\n };\n\n const updateCustomKeyName = (id: string, name: string) => {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n name,\n status: key.key ? 'configured' : 'not_configured'\n } : key)));\n };\n\n // Функция для получения текста статуса с поддержкой локализации\n const getStatusText = (status: string) => {\n switch (status) {\n case 'configured':\n return t('options.settings.aiKeys.status.configured');\n case 'not_configured':\n return t('options.settings.aiKeys.status.notConfigured');\n case 'testing':\n return t('options.settings.aiKeys.status.testing');\n default:\n return t('options.settings.aiKeys.status.notConfigured');\n }\n };\n\n const getStatusClass = (status: string) => {\n switch (status) {\n case 'configured':\n return 'status-configured';\n case 'not_configured':\n return 'status-not-configured';\n case 'testing':\n return 'status-testing';\n default:\n return '';\n }\n };\n\n // Функция для получения API ключа для использования в MCP-серверах\n const getAPIKeyForMCP = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.getDecryptedKey(keyId);\n } catch (error) {\n console.error('Failed to get API key for MCP:', error);\n return null;\n }\n };\n\n // Функция для проверки, настроен ли определенный ключ\n const isKeyConfigured = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.keyExists(keyId);\n } catch (error) {\n console.error('Failed to check if key is configured:', error);\n return false;\n }\n };\n\n return {\n aiKeys,\n customKeys,\n saveAIKeys,\n testAIKeys,\n addCustomKey,\n removeCustomKey,\n updateKey,\n updateCustomKeyName,\n getStatusText,\n getStatusClass,\n getAPIKeyForMCP,\n isKeyConfigured,\n };\n};\n","import { useTranslations } from '../hooks/useTranslations';\nimport type { Plugin } from '../hooks/usePlugins';\nimport type { PluginSettings } from '@extension/storage';\nimport ToggleButton from './ToggleButton';\nimport LocalErrorBoundary from './LocalErrorBoundary';\nimport LLMSelector from './LLMSelector';\nimport { useAIKeys, AIKey } from '../hooks/useAIKeys';\nimport { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings';\nimport { useState, useEffect } from 'react';\nimport type { ReactNode } from 'react';\n\n// Типы для структуры промптов\ninterface PromptData {\n [key: string]: any;\n}\n\ninterface LanguagePrompts {\n ru: PromptData;\n en: PromptData;\n}\n\ninterface PromptsStructure {\n basic_analysis: LanguagePrompts;\n deep_analysis: LanguagePrompts;\n}\n\nconst cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' ');\n\ninterface PluginDetailsProps {\n selectedPlugin: Plugin | null;\n locale?: 'en' | 'ru';\n onUpdateSetting?: (pluginId: string, setting: keyof PluginSettings, value: boolean) => Promise;\n}\n\ninterface CustomSetting {\n type: 'boolean' | 'select' | 'text' | 'number' | 'prompts';\n default: boolean | string | number | PromptsStructure;\n label: string | { ru: string; en: string };\n description?: string | { ru: string; en: string };\n values?: string[];\n labels?: Record;\n min?: number;\n max?: number;\n step?: number;\n}\n\n// Компонент для редактирования промптов\ninterface PromptsEditorProps {\n value: PromptsStructure;\n manifest: any; // manifest.json структура\n disabled: boolean;\n onSave: (value: PromptsStructure) => void;\n locale: 'en' | 'ru';\n t: (key: string) => string; // функция перевода\n globalAIKeys: AIKey[];\n pluginSettings: PluginSettingsType;\n}\n\nconst PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => {\n const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis');\n const [language, setLanguage] = useState<'ru' | 'en'>('ru');\n const [customPrompt, setCustomPrompt] = useState('');\n\n // Получить дефолтную LLM для конкретного промпта и языка\n const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return 'default';\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n const llmConfig = langPrompts.LLM?.default;\n\n if (!llmConfig) return 'default';\n\n // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro\n if (type === 'basic_analysis') {\n return 'gemini-flash-lite';\n } else if (type === 'deep_analysis') {\n return 'gemini-pro';\n }\n\n return 'default';\n } catch {\n return 'default';\n }\n };\n\n // Проверить наличие дефолтной LLM для конкретного промпта и языка\n const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return false;\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n return !!langPrompts.LLM?.default;\n } catch {\n return false;\n }\n };\n\n // Получаем оригинальный промпт из manifest\n const getOriginalPrompt = (): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return '';\n\n const typePrompts = promptsConfig[promptType] || {};\n const langPrompts = typePrompts[language] || {};\n const defaultPrompt = langPrompts.default || '';\n\n // Если defaultPrompt - это объект, преобразуем его\n if (typeof defaultPrompt === 'object') {\n return JSON.stringify(defaultPrompt, null, 2);\n }\n\n return defaultPrompt;\n } catch {\n return '';\n }\n };\n\n // Получаем кастомный промпт\n const getCustomPrompt = (): string => {\n try {\n const prompts = value || {};\n const typePrompts = (prompts as any)[promptType] || {};\n const langPrompts = (typePrompts as any)[language];\n\n // If stored as plain text, show as-is. Only stringify objects.\n if (typeof langPrompts === 'string') return langPrompts;\n if (langPrompts == null) return '';\n return JSON.stringify(langPrompts, null, 2);\n } catch {\n return '';\n }\n };\n\n // Загружаем кастомный промпт при изменении типа или языка\n useEffect(() => {\n setCustomPrompt(getCustomPrompt());\n }, [promptType, language, value]);\n\n const handleCopyToCustom = () => {\n setCustomPrompt(getOriginalPrompt());\n };\n\n const handleSave = () => {\n try {\n const newValue: any = { ...value };\n\n // Ensure container objects exist\n if (!newValue[promptType]) newValue[promptType] = { ru: '', en: '' };\n\n // Store verbatim text (plain string), no JSON requirement\n newValue[promptType][language] = customPrompt;\n\n onSave(newValue);\n } catch (error) {\n console.error('Failed to save custom prompt:', error);\n // Можно добавить уведомление об ошибке\n }\n };\n\n return (\n
\n
\n \n\n {/* Переключатели */}\n
\n
\n \n setPromptType(e.target.value as 'basic_analysis' | 'deep_analysis')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n\n
\n \n setLanguage(e.target.value as 'ru' | 'en')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n
\n\n {/* LLM Selector для текущего промпта и языка */}\n {\n console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey);\n // Сохраняем выбранную LLM в pluginSettings для передачи в mcp_server.py\n const updatedPluginSettings = { ...pluginSettings } as any;\n if (!updatedPluginSettings.selected_llms) {\n updatedPluginSettings.selected_llms = {};\n }\n if (!updatedPluginSettings.selected_llms[promptType]) {\n updatedPluginSettings.selected_llms[promptType] = {};\n }\n // Сохраняем только выбранную LLM (без api_key, так как он уже сохранен через APIKeyManager)\n updatedPluginSettings.selected_llms[promptType][language] = llm;\n // Обновляем pluginSettings через глобальный объект\n if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n }\n }}\n />\n\n {/* Textarea */}\n
\n
\n \n \n
\n\n
\n \n {t('options.plugins.prompts.copyToCustom')}\n \n
\n\n
\n \n setCustomPrompt(e.target.value)}\n disabled={disabled}\n style={{\n width: '100%',\n height: '300px',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '12px',\n fontFamily: 'monospace',\n resize: 'vertical'\n }}\n />\n
\n
\n\n {/* Кнопка сохранения */}\n
\n \n {t('options.plugins.prompts.save')}\n \n
\n
\n
\n );\n};\n\nconst PluginDetails = (props: PluginDetailsProps) => {\n const { selectedPlugin, locale = 'en', onUpdateSetting } = props;\n const { t } = useTranslations(locale);\n const { aiKeys } = useAIKeys();\n const { settings: pluginSettings } = usePluginSettings();\n const [isUpdating, setIsUpdating] = useState(null);\n const [customSettings, setCustomSettings] = useState | null>(null);\n\n // Хелперы для работы с локализацией\n const getLocalizedText = (text: string | { ru: string; en: string } | undefined): string => {\n if (!text) return '';\n if (typeof text === 'string') return text;\n return text[locale] || text.ru || text.en || '';\n };\n\n // Загружаем пользовательские настройки при выборе плагина\n useEffect(() => {\n if (selectedPlugin?.manifest?.options) {\n loadCustomSettings();\n }\n }, [selectedPlugin?.id]);\n\n // Передаем выбранные LLM в mcp_server.py через pyodide.globals\n useEffect(() => {\n if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n // Создаем структуру selected_llms из pluginSettings\n const selected_llms = {\n basic_analysis: {\n ru: pluginSettings.basic_analysis?.ru?.llm || 'default',\n en: pluginSettings.basic_analysis?.en?.llm || 'default',\n },\n deep_analysis: {\n ru: pluginSettings.deep_analysis?.ru?.llm || 'default',\n en: pluginSettings.deep_analysis?.en?.llm || 'default',\n }\n };\n \n // Обновляем pluginSettings в pyodide.globals\n const updatedPluginSettings = {\n ...(window as any).pyodide.globals.pluginSettings || {},\n selected_llms: selected_llms\n };\n \n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings);\n }\n }, [pluginSettings]);\n\n if (!selectedPlugin || typeof selectedPlugin !== 'object') {\n return (\n
\n

{t('options_plugins_details_title')}

\n

{t('options.plugins.details.selectPlugin')}

\n
\n );\n }\n\n const settings = selectedPlugin.settings || { enabled: true, autorun: false };\n const hostPermissions = selectedPlugin.manifest?.host_permissions || [];\n\n // Функции для работы с chrome.storage.local\n const loadCustomSettings = async () => {\n if (!selectedPlugin || !selectedPlugin.manifest?.options || customSettings !== null) return;\n\n try {\n // Проверяем доступность chrome.storage\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const optionKeys = Object.keys(selectedPlugin.manifest.options);\n const keys = optionKeys.map(key => `${selectedPlugin.id}_${key}`);\n\n const result = await chrome.storage.local.get(keys);\n const loadedSettings: Record = {};\n\n // Преобразуем ключи обратно и применяем значения\n Object.entries(result).forEach(([key, value]) => {\n const settingName = key.replace(`${selectedPlugin.id}_`, '');\n if (value !== undefined) {\n loadedSettings[settingName] = value;\n }\n });\n\n setCustomSettings(loadedSettings);\n }\n } catch (error) {\n console.warn('Failed to load custom settings from chrome.storage.local:', error);\n // Fallback: используем дефолтные значения из manifest\n }\n };\n\n const saveCustomSetting = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n try {\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const key = `${selectedPlugin.id}_${setting}`;\n await chrome.storage.local.set({ [key]: value });\n\n // Обновляем локальное состояние\n setCustomSettings(prev => ({\n ...prev,\n [setting]: value\n }));\n\n // Диагностика: проверяем правильность сохранения промптов\n if (setting === 'prompts') {\n await verifyPromptStorage();\n }\n } else {\n console.warn('chrome.storage.local is not available');\n }\n } catch (error) {\n console.error(`Failed to save setting ${setting} to chrome.storage.local:`, error);\n throw error; // Пробрасываем ошибку для обработки в handleSettingChange\n }\n };\n\n // Диагностическая функция для проверки сохранения промптов\n const verifyPromptStorage = async () => {\n if (!selectedPlugin) return;\n\n try {\n const key = `${selectedPlugin.id}_prompts`;\n const stored = await chrome.storage.local.get([key]);\n console.log('🔍 Диагностика промптов:');\n console.log(` Plugin ID: ${selectedPlugin.id}`);\n console.log(` Storage key: ${key}`);\n console.log(' Сохраненные промпты:', stored[key]);\n\n if (stored[key]) {\n const prompts = stored[key] as PromptsStructure;\n console.log(' Структура промптов:');\n console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`);\n console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`);\n console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`);\n console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`);\n }\n } catch (error) {\n console.error('Ошибка диагностики промптов:', error);\n }\n };\n\n\n // Хелпер для получения значения настройки с приоритетом: chrome.storage -> manifest\n const getCustomSettingValue = (settingName: string, defaultValue: boolean | string | number | PromptsStructure): boolean | string | number | PromptsStructure => {\n if (customSettings && customSettings[settingName] !== undefined) {\n return customSettings[settingName];\n }\n\n // Специальная обработка для промптов: преобразуем структуру из manifest в PromptsStructure\n if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) {\n const promptsConfig = defaultValue as any;\n const result: PromptsStructure = {\n basic_analysis: { ru: {}, en: {} },\n deep_analysis: { ru: {}, en: {} }\n };\n\n // Извлекаем default значения из структуры manifest\n if (promptsConfig.basic_analysis?.ru?.default) {\n result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default;\n }\n if (promptsConfig.basic_analysis?.en?.default) {\n result.basic_analysis.en = promptsConfig.basic_analysis.en.default;\n }\n if (promptsConfig.deep_analysis?.ru?.default) {\n result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default;\n }\n if (promptsConfig.deep_analysis?.en?.default) {\n result.deep_analysis.en = promptsConfig.deep_analysis.en.default;\n }\n\n return result;\n }\n\n return defaultValue;\n };\n\n const renderCustomSetting = (key: string, config: CustomSetting): ReactNode | null => {\n const value = getCustomSettingValue(key, config.default);\n const disabled = isUpdating === key || !(settings.enabled ?? true);\n const localizedLabel = getLocalizedText(config.label);\n const localizedDescription = getLocalizedText(config.description);\n\n // Специальная обработка для промптов\n if (key === 'prompts') {\n return (\n
\n handleSettingChange(key, newValue)}\n locale={locale}\n t={t}\n globalAIKeys={aiKeys}\n pluginSettings={pluginSettings}\n />\n
\n );\n }\n\n if (config.type === 'boolean') {\n return (\n
\n handleSettingChange(key, val)}\n label={\n <>\n {localizedLabel}\n {localizedDescription && (\n \n i\n \n )}\n \n }\n />\n
\n );\n }\n\n if (config.type === 'select') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'text') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'number') {\n const handleNumberChange = (e: React.ChangeEvent) => {\n const numValue = parseFloat(e.target.value);\n if (isNaN(numValue)) return;\n\n // Валидация диапазона\n let validatedValue = numValue;\n if (config.min !== undefined && validatedValue < config.min) {\n validatedValue = config.min;\n }\n if (config.max !== undefined && validatedValue > config.max) {\n validatedValue = config.max;\n }\n\n handleSettingChange(key, validatedValue);\n };\n\n return (\n
\n \n
\n );\n }\n\n return null;\n };\n\n const handleSettingChange = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n // Проверяем, является ли настройка пользовательской\n const options = selectedPlugin.manifest?.options;\n if (options && options[setting as keyof typeof options]) {\n // Ленивая загрузка: загружаем настройки только при первом взаимодействии\n if (customSettings === null) {\n await loadCustomSettings();\n }\n\n try {\n setIsUpdating(setting);\n await saveCustomSetting(setting, value);\n } catch (error) {\n console.error(`Failed to update custom setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n } else {\n // Стандартные настройки (enabled/autorun) используем через callback\n if (onUpdateSetting) {\n try {\n setIsUpdating(setting);\n await onUpdateSetting(selectedPlugin.id, setting as keyof PluginSettings, value as boolean);\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n }\n }\n };\n\n // Precompute custom settings elements to satisfy TypeScript\n const optionEntries = Object.entries(selectedPlugin.manifest?.options ?? {}) as [string, CustomSetting][];\n const customSettingElements: ReactNode[] = optionEntries\n .map(([key, config]) => renderCustomSetting(key, config))\n .filter((item): item is ReactNode => item !== null);\n\n // Render helper to avoid union/unknown in JSX for plugin settings section\n const PluginSettingsSection = () => (\n
\n

Настройки плагина

\n
\n handleSettingChange('enabled', val)}\n label={\n <>\n Включен\n \n i\n \n \n }\n />\n
\n
\n handleSettingChange('autorun', val)}\n label={\n <>\n Автоматический запуск\n \n i\n \n \n }\n />\n
\n
\n );\n\n return (\n \n
\n

{selectedPlugin.name}

\n
\n
\n
\n

\n Версия: v{selectedPlugin.version}\n

\n

\n Статус:\n \n {settings.enabled ? 'Активен' : 'Неактивен'}\n \n

\n

\n Автор: {selectedPlugin.manifest?.author || 'Не указан'}\n

\n

\n Последнее обновление: {selectedPlugin.manifest?.last_updated || 'Неизвестно'}\n

\n
\n\n
\n

Описание

\n

{selectedPlugin.description}

\n
\n\n {/* Сайты/домены, на которых работает плагин */}\n {hostPermissions.length > 0 && (\n
\n

Сайты/домены

\n
    \n {hostPermissions.map((host: string, idx: number) => (\n
  • {host}
  • \n ))}\n
\n
\n )}\n\n {Array.isArray(selectedPlugin.manifest?.permissions) ? (\n
\n

Разрешения

\n
    \n {(selectedPlugin.manifest?.permissions ?? []).map((permission: string, idx: number) => (\n
  • {permission}
  • \n ))}\n
\n
\n ) : null}\n\n \n\n {/* Пользовательские настройки */}\n {(selectedPlugin.manifest?.options && Object.keys(selectedPlugin.manifest.options).length > 0) ? (\n
\n

Дополнительные настройки

\n {customSettingElements}\n
\n ) : null}\n\n\n
\n
\n
\n );\n};\n\nexport { PluginDetails };\n\nexport default PluginDetails;\n","import '../../../options/src/Options.css'; // Импорт стилей из options для идентичности\nimport type React from 'react';\nimport OptionsPluginDetails from '../../../options/src/components/PluginDetails';\nimport type { PluginSettings } from '@extension/storage';\n\ninterface PluginDetailsProps {\n plugin: Plugin;\n onUpdateSetting?: (pluginId: string, setting: string, value: boolean) => Promise;\n}\n\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\nexport const PluginDetails: React.FC = ({ plugin, onUpdateSetting }) => {\n // Адаптируем onUpdateSetting для совместимости с OptionsPluginDetails\n const adaptedOnUpdateSetting = onUpdateSetting ? async (pluginId: string, setting: keyof PluginSettings, value: boolean): Promise => {\n try {\n await onUpdateSetting(pluginId, setting as string, value);\n return true; // Успешно\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n return false; // Неудача\n }\n } : undefined;\n\n const adaptedProps = {\n selectedPlugin: {\n ...plugin,\n description: plugin.description || 'Описание не указано',\n icon: plugin.icon || '',\n manifest: plugin.manifest || {} as any\n },\n locale: 'ru' as const,\n onUpdateSetting: adaptedOnUpdateSetting\n };\n\n return ;\n};\n","import type { ExcludeValuesFromBaseArrayType } from './types.js';\n\nexport const excludeValuesFromBaseArray = (\n baseArray: B,\n excludeArray: E,\n) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType;\n\nexport const sleep = async (time: number) => new Promise(r => setTimeout(r, time));\n\n/**\n * Унифицированное получение pageKey из URL страницы.\n * Удаляет search/hash, возвращает нормализованный URL или 'unknown-page'.\n */\nexport const getPageKey = function (currentTabUrl: string | null): string {\n if (!currentTabUrl) return 'unknown-page';\n try {\n const url = new URL(currentTabUrl);\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return currentTabUrl;\n }\n};\n","import { useState, useEffect, useRef, useCallback } from 'react';\n\ninterface UseLazyChatSyncOptions {\n pluginId: string;\n pageKey: string;\n debounceMs?: number; // Задержка перед синхронизацией (по умолчанию 1000ms)\n}\n\ninterface UseLazyChatSyncReturn {\n message: string;\n setMessage: (text: string) => void;\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n loadDraft: () => Promise;\n clearDraft: () => Promise;\n draftText: string;\n}\n\nexport const useLazyChatSync = ({\n pluginId,\n pageKey,\n debounceMs = 1000,\n}: UseLazyChatSyncOptions): UseLazyChatSyncReturn => {\n const [message, setMessageState] = useState('');\n const [isDraftSaved, setIsDraftSaved] = useState(false);\n const [isDraftLoading, setIsDraftLoading] = useState(false);\n const [draftError, setDraftError] = useState(null);\n const [draftText, setDraftText] = useState('');\n\n const debounceRef = useRef(null);\n const lastSavedText = useRef('');\n\n // Функция для сохранения черновика\n const saveDraft = useCallback(\n async (text: string) => {\n if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился\n console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: text,\n });\n lastSavedText.current = text;\n setIsDraftSaved(true);\n setDraftError(null);\n setDraftText(text);\n console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text });\n } catch (error) {\n console.error('[useLazyChatSync] Error saving draft:', error);\n setDraftError('Ошибка сохранения черновика');\n setIsDraftSaved(false);\n }\n },\n [pluginId, pageKey],\n );\n\n // Функция для загрузки черновика\n const loadDraft = useCallback(async () => {\n setIsDraftLoading(true);\n setDraftError(null);\n console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey });\n try {\n const response = await chrome.runtime.sendMessage({\n type: 'GET_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n });\n if (response?.draftText) {\n setMessageState(response.draftText);\n lastSavedText.current = response.draftText;\n setIsDraftSaved(true);\n setDraftText(response.draftText);\n console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText });\n } else {\n setMessageState('');\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftText('');\n console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey });\n }\n } catch (error) {\n console.error('[useLazyChatSync] Error loading draft:', error);\n setDraftError('Ошибка загрузки черновика');\n } finally {\n setIsDraftLoading(false);\n }\n }, [pluginId, pageKey]);\n\n // Функция для очистки черновика\n const clearDraft = useCallback(async () => {\n console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: '',\n });\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftError(null);\n setDraftText('');\n setMessageState('');\n console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey });\n } catch (error) {\n console.error('[useLazyChatSync] Error clearing draft:', error);\n setDraftError('Ошибка очистки черновика');\n }\n }, [pluginId, pageKey]);\n\n // Обновленная функция setMessage с ленивой синхронизацией\n const setMessage = useCallback(\n (text: string) => {\n setMessageState(text);\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (text.length === 0) {\n clearDraft();\n } else {\n debounceRef.current = setTimeout(() => {\n saveDraft(text);\n }, debounceMs);\n }\n console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text });\n },\n [debounceMs, saveDraft, clearDraft, pluginId, pageKey],\n );\n\n // Очистка таймера при размонтировании\n useEffect(\n () => () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n },\n [],\n );\n\n // Автоматическая загрузка черновика при монтировании\n useEffect(() => {\n loadDraft();\n }, [loadDraft]);\n\n // При смене pageKey всегда загружаем draft, не сбрасываем message вручную\n useEffect(() => {\n console.log('[useLazyChatSync] pageKey изменился:', pageKey);\n setIsDraftSaved(false);\n setIsDraftLoading(false);\n setDraftError(null);\n lastSavedText.current = '';\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n loadDraft();\n }, [pageKey, loadDraft]);\n\n return {\n message,\n setMessage,\n isDraftSaved,\n isDraftLoading,\n draftError,\n loadDraft,\n clearDraft,\n draftText,\n };\n};\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","/**\n * Storage area type for persisting and exchanging data.\n * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview\n */\nexport var StorageEnum;\n(function (StorageEnum) {\n /**\n * Persist data locally against browser restarts. Will be deleted by uninstalling the extension.\n * @default\n */\n StorageEnum[\"Local\"] = \"local\";\n /**\n * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply.\n */\n StorageEnum[\"Sync\"] = \"sync\";\n /**\n * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a\n * json schema for company wide config.\n */\n StorageEnum[\"Managed\"] = \"managed\";\n /**\n * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and\n * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access.\n * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session)\n */\n StorageEnum[\"Session\"] = \"session\";\n})(StorageEnum || (StorageEnum = {}));\n/**\n * Global access level requirement for the {@link StorageEnum.Session} Storage Area.\n * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel)\n */\nexport var SessionAccessLevelEnum;\n(function (SessionAccessLevelEnum) {\n /**\n * Storage can only be accessed by Extension pages (not Content scripts).\n * @default\n */\n SessionAccessLevelEnum[\"ExtensionPagesOnly\"] = \"TRUSTED_CONTEXTS\";\n /**\n * Storage can be accessed by both Extension pages and Content scripts.\n */\n SessionAccessLevelEnum[\"ExtensionPagesAndContentScripts\"] = \"TRUSTED_AND_UNTRUSTED_CONTEXTS\";\n})(SessionAccessLevelEnum || (SessionAccessLevelEnum = {}));\n","import { SessionAccessLevelEnum, StorageEnum } from './enums.js';\n/**\n * Chrome reference error while running `processTailwindFeatures` in tailwindcss.\n * To avoid this, we need to check if globalThis.chrome is available and add fallback logic.\n */\nconst chrome = globalThis.chrome;\n/**\n * Sets or updates an arbitrary cache with a new value or the result of an update function.\n */\nconst updateCache = async (valueOrUpdate, cache) => {\n // Type guard to check if our value or update is a function\n const isFunction = (value) => typeof value === 'function';\n // Type guard to check in case of a function if it's a Promise\n const returnsPromise = (func) => \n // Use ReturnType to infer the return type of the function and check if it's a Promise\n func instanceof Promise;\n if (isFunction(valueOrUpdate)) {\n // Check if the function returns a Promise\n if (returnsPromise(valueOrUpdate)) {\n return valueOrUpdate(cache);\n }\n else {\n return valueOrUpdate(cache);\n }\n }\n else {\n return valueOrUpdate;\n }\n};\n/**\n * If one session storage needs access from content scripts, we need to enable it globally.\n * @default false\n */\nlet globalSessionAccessLevelFlag = false;\n/**\n * Checks if the storage permission is granted in the manifest.json.\n */\nconst checkStoragePermission = (storageEnum) => {\n if (!chrome) {\n return;\n }\n if (!chrome.storage[storageEnum]) {\n throw new Error(`\"storage\" permission in manifest.ts: \"storage ${storageEnum}\" isn't defined`);\n }\n};\n/**\n * Creates a storage area for persisting and exchanging data.\n */\nexport const createStorage = (key, fallback, config) => {\n let cache = null;\n let initialCache = false;\n let listeners = [];\n const storageEnum = config?.storageEnum ?? StorageEnum.Local;\n const liveUpdate = config?.liveUpdate ?? false;\n const serialize = config?.serialization?.serialize ?? ((v) => v);\n const deserialize = config?.serialization?.deserialize ?? (v => v);\n // Set global session storage access level for StoryType.Session, only when not already done but needed.\n if (globalSessionAccessLevelFlag === false &&\n storageEnum === StorageEnum.Session &&\n config?.sessionAccessForContentScripts === true) {\n checkStoragePermission(storageEnum);\n chrome?.storage[storageEnum]\n .setAccessLevel({\n accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts,\n })\n .catch(error => {\n console.error(error);\n console.error('Please call .setAccessLevel() into different context, like a background script.');\n });\n globalSessionAccessLevelFlag = true;\n }\n // Register life cycle methods\n const get = async () => {\n checkStoragePermission(storageEnum);\n const value = await chrome?.storage[storageEnum].get([key]);\n if (!value) {\n return fallback;\n }\n return deserialize(value[key]) ?? fallback;\n };\n const set = async (valueOrUpdate) => {\n if (!initialCache) {\n cache = await get();\n }\n cache = await updateCache(valueOrUpdate, cache);\n await chrome?.storage[storageEnum].set({ [key]: serialize(cache) });\n _emitChange();\n };\n const subscribe = (listener) => {\n listeners = [...listeners, listener];\n return () => {\n listeners = listeners.filter(l => l !== listener);\n };\n };\n const getSnapshot = () => cache;\n const _emitChange = () => {\n listeners.forEach(listener => listener());\n };\n // Listener for live updates from the browser\n const _updateFromStorageOnChanged = async (changes) => {\n // Check if the key we are listening for is in the changes object\n if (changes[key] === undefined)\n return;\n const valueOrUpdate = deserialize(changes[key].newValue);\n if (cache === valueOrUpdate)\n return;\n cache = await updateCache(valueOrUpdate, cache);\n _emitChange();\n };\n get().then(data => {\n cache = data;\n initialCache = true;\n _emitChange();\n });\n // Register listener for live updates for our storage area\n if (liveUpdate) {\n chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);\n }\n return {\n get,\n set,\n getSnapshot,\n subscribe,\n };\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('theme-storage-key', {\n theme: 'system',\n isLight: getSystemTheme(),\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\n// Функция для определения системной темы\nfunction getSystemTheme() {\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: light)').matches;\n }\n return true; // По умолчанию светлая тема\n}\nexport const exampleThemeStorage = {\n ...storage,\n toggle: async () => {\n await storage.set(currentState => {\n let newTheme;\n switch (currentState.theme) {\n case 'light':\n newTheme = 'dark';\n break;\n case 'dark':\n newTheme = 'system';\n break;\n case 'system':\n default:\n newTheme = 'light';\n break;\n }\n const isLight = newTheme === 'system' ? getSystemTheme() : newTheme === 'light';\n return {\n theme: newTheme,\n isLight,\n };\n });\n },\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('chat-alignment-storage-key', {\n alignment: 'left',\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\nexport const exampleChatAlignmentStorage = {\n ...storage,\n setAlignment: async (alignment) => {\n await storage.set({ alignment });\n },\n getAlignment: async () => {\n const state = await storage.get();\n return state.alignment;\n },\n};\n","/**\n * PluginControlPanel.tsx - Панель управления плагином с чатом\n *\n * ИСПРАВЛЕНИЕ ПРОБЛЕМЫ С ПОЛУЧЕНИЕМ ОТВЕТА GET_PLUGIN_CHAT:\n * ========================================================\n *\n * Проблема: Background отправлял ответ через sendResponse() callback, но компонент\n * ожидал ответ через chrome.runtime.sendMessage() с типом 'GET_PLUGIN_CHAT_RESPONSE'.\n *\n * Решение: Изменен механизм коммуникации на использование Promise-based подхода\n * с помощью sendMessageToBackgroundAsync(), который правильно работает с sendResponse().\n *\n * Теперь:\n * 1. Компонент отправляет GET_PLUGIN_CHAT через chrome.runtime.sendMessage()\n * 2. Background получает сообщение и отвечает через sendResponse()\n * 3. Компонент получает ответ через Promise и обрабатывает его\n *\n * Диагностика:\n * - Логи sendMessageToBackgroundAsync покажут отправку и получение ответа\n * - Логи loadChat покажут обработку ответа\n * - Логи processChatResponse покажут разбор данных чата\n */\n\nimport { DraftStatus } from './DraftStatus';\nimport { PluginDetails } from './PluginDetails';\nimport { getPageKey } from '../../../../packages/shared/lib/utils/helpers';\nimport { useLazyChatSync } from '../hooks/useLazyChatSync';\nimport { saveAs } from 'file-saver';\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport './PluginControlPanel.css';\nimport type React from 'react';\nimport { exampleChatAlignmentStorage, type ChatAlignment } from '@extension/storage';\n\n// Определение типа Plugin для PluginControlPanel\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Новый тип для сообщений чата\ninterface ChatMessage {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: number;\n}\n\ninterface PluginControlPanelProps {\n plugin: Plugin;\n currentView: PanelView;\n isRunning: boolean;\n isPaused: boolean;\n currentTabUrl: string | null;\n onStart: () => void;\n onPause: () => void;\n onStop: () => void;\n onClose: () => void;\n}\n\nexport type PanelView = 'chat' | 'details';\n\nexport const PluginControlPanel: React.FC = ({\n plugin,\n currentView,\n isRunning,\n isPaused,\n currentTabUrl,\n onStart,\n onPause,\n onStop,\n onClose,\n}) => {\n // Состояние для активной вкладки в панели управления\n const [activeTab, setActiveTab] = useState('chat');\n // Состояние для текущего pageKey с динамическим обновлением\n const [currentPageKey, setCurrentPageKey] = useState(getPageKey(currentTabUrl));\n\n const [chatTextAlign, setChatTextAlign] = useState('left');\n\n // useEffect для обновления pageKey при изменении currentTabUrl\n useEffect(() => {\n const newPageKey = getPageKey(currentTabUrl);\n console.log('[PluginControlPanel] currentTabUrl изменился:', {\n oldPageKey: currentPageKey,\n newPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n setCurrentPageKey(newPageKey);\n }, [currentTabUrl]);\n\n useEffect(() => {\n const loadAlignment = async () => {\n const alignment = await exampleChatAlignmentStorage.getAlignment();\n setChatTextAlign(alignment);\n };\n\n loadAlignment();\n\n const unsubscribe = exampleChatAlignmentStorage.subscribe(() => {\n loadAlignment();\n });\n\n return unsubscribe;\n }, []);\n // Используем хук для ленивой синхронизации\n const { message, setMessage, isDraftSaved, isDraftLoading, draftError, loadDraft, clearDraft, draftText } =\n useLazyChatSync({\n pluginId: plugin.id,\n pageKey: currentPageKey, // <-- Теперь динамический pageKey\n debounceMs: 1000, // 1 секунда задержки\n });\n\n const [messages, setMessages] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n const [inputHeight, setInputHeight] = useState(60); // Начальная высота поля ввода\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef(null);\n const textareaRef = useRef(null);\n\n useEffect(() => {\n if (!isRunning) {\n // Удалить все вызовы setStopped(...)\n }\n }, [isRunning]);\n\n const handleStart = () => {\n // Удалить все вызовы setStopped(...)\n onStart();\n };\n\n const pluginName =\n plugin.name || (typeof plugin.manifest?.name === 'string' ? plugin.manifest.name : '') || plugin.id;\n\n // Получаем ключ чата для текущего плагина и страницы\n const pluginId = plugin.id;\n\n // Вспомогательная функция для отправки сообщений в background с ожиданием ответа\n const sendMessageToBackgroundAsync = useCallback(async (message: any): Promise => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n try {\n const response = await chrome.runtime.sendMessage(messageWithId);\n return response;\n } catch (error) {\n console.error('[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:', error);\n throw error;\n }\n }, []);\n\n // Вспомогательная функция для отправки сообщений в background без ожидания ответа (для обратной совместимости)\n const sendMessageToBackground = useCallback((message: any): void => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n chrome.runtime.sendMessage(messageWithId);\n }, []);\n\n // Функция для тестирования обработки сообщений с проблемными данными\n const testMessageProcessing = useCallback(() => {\n console.log('[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными');\n\n // Тест 1: Сообщение с объектом вместо строки в text\n const testMessageWithObject = {\n messages: [{\n id: 'test_obj_1',\n text: { content: 'Это объект вместо строки', type: 'object' }, // Объект вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 2: Сообщение с null в text\n const testMessageWithNull = {\n messages: [{\n id: 'test_null_1',\n text: null, // null вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 3: Сообщение с undefined в text\n const testMessageWithUndefined = {\n messages: [{\n id: 'test_undef_1',\n text: undefined, // undefined вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Объявляем processChatResponse локально для избежания проблем с temporal dead zone\n const localProcessChatResponse = (response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(msg.timestamp || Date.now() + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: msg.timestamp || Date.now(),\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n };\n\n try {\n console.log('[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text');\n localProcessChatResponse(testMessageWithObject);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 1 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 2: Обработка сообщения с null в text');\n localProcessChatResponse(testMessageWithNull);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 2 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text');\n localProcessChatResponse(testMessageWithUndefined);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 3 провалился:', error);\n }\n\n console.log('[PluginControlPanel] ✅ Тестирование обработки сообщений завершено');\n }, []); // Убрали processChatResponse из зависимостей\n\n // Вспомогательная функция для обработки ответа чата\n const processChatResponse = useCallback((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n\n console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${index}:`, textContent);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n\n console.log(`[PluginControlPanel] TextContent after JSON parse for message ${index}:`, textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n console.log(`[PluginControlPanel] Final converted text for message ${index}:`, convertedMsg.text);\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n }, []);\n\n // Загрузка истории чата при монтировании или смене плагина/страницы\n const loadChat = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n console.log('[PluginControlPanel] ===== НАЧАЛО loadChat =====', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString(),\n isRunning,\n isPaused\n });\n\n try {\n console.log('[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT');\n const response = await sendMessageToBackgroundAsync({\n type: 'GET_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n console.log('[PluginControlPanel] loadChat - получен ответ от background:', response);\n console.log('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:', {\n loading,\n messagesCount: messages.length,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку при получении ответа\n console.log('[PluginControlPanel] loadChat - loading сброшен в false');\n\n if (response?.error) {\n console.error('[PluginControlPanel] loadChat - ошибка в ответе:', response.error);\n setError(`Ошибка загрузки чата: ${response.error}`);\n setMessages([]);\n } else {\n console.log('[PluginControlPanel] loadChat - обрабатываем успешный ответ');\n // Используем анонимную функцию вместо прямого вызова processChatResponse\n // чтобы избежать проблемы с temporal dead zone\n ((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n \n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n \n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распаршен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n }\n \n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n })(response);\n console.log('[PluginControlPanel] loadChat: чат успешно загружен');\n }\n\n } catch (error) {\n console.error('[PluginControlPanel] loadChat - ошибка при получении ответа:', error);\n console.error('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:', {\n loading,\n messagesCount: messages.length,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString()\n });\n\n // Детальное логирование ошибки с трассировкой стека\n console.error('[PluginControlPanel] loadChat - ERROR DETAILS:', {\n error,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : 'No stack trace',\n errorName: error instanceof Error ? error.name : 'Unknown error type',\n timestamp: new Date().toISOString(),\n pluginId,\n pageKey: currentPageKey\n });\n\n setLoading(false);\n console.log('[PluginControlPanel] loadChat - loading сброшен в false в catch блоке');\n\n // Улучшенная обработка ошибок с проверкой типа\n let errorMessage = 'Ошибка связи с background';\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Специальная обработка для TypeError с substring\n if (error.name === 'TypeError' && error.message.includes('substring')) {\n errorMessage += ' (ошибка обработки текста - проверьте тип данных)';\n console.error('[PluginControlPanel] CRITICAL: substring error detected:', {\n originalError: error,\n stack: error.stack,\n context: { pluginId, pageKey: currentPageKey }\n });\n }\n } else {\n errorMessage += `: ${String(error)}`;\n }\n\n setError(errorMessage);\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====');\n }, [pluginId, currentPageKey, sendMessageToBackgroundAsync, currentTabUrl, isRunning, isPaused]);\n\n // Добавить useEffect для вызова loadChat при монтировании и смене pluginId/pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n\n // Вызываем асинхронную функцию без await, так как useEffect не может быть async\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:', error);\n });\n }, [loadChat]);\n\n // useEffect для перезагрузки чата при смене pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик');\n // Перезагружаем чат для новой страницы\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] Ошибка при перезагрузке чата:', error);\n });\n // Перезагружаем черновик для новой страницы\n loadDraft();\n }, [currentPageKey, loadChat, loadDraft]);\n\n // Событийная синхронизация чата между вкладками и обработка результатов сохранения сообщений\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений', {\n pluginId,\n pageKey: currentPageKey,\n timestamp: new Date().toISOString()\n });\n\n const handleChatUpdate = (event: { type: string; pluginId: string; pageKey: string; messages?: ChatMessage[]; messageId?: string; response?: any; success?: boolean; error?: string; message?: any; timestamp?: number }) => {\n console.log('[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====', {\n type: event?.type,\n pluginId: event?.pluginId,\n pageKey: event?.pageKey,\n messageId: event?.messageId,\n hasResponse: !!event?.response,\n responseType: event?.response ? typeof event.response : 'none',\n timestamp: new Date().toISOString()\n });\n\n // Обработка обновлений чата от других компонентов/вкладок\n if (event?.type === 'PLUGIN_CHAT_UPDATED' && event.pluginId === pluginId && event.pageKey === currentPageKey) {\n console.log('[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные');\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:', {\n loading,\n messagesCount: messages.length,\n currentPageKey,\n pluginId,\n timestamp: new Date().toISOString()\n });\n setLoading(false); // Гарантированный сброс loading перед загрузкой чата\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat');\n // Запрашиваем актуальные данные чата асинхронно\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:', error);\n });\n }\n\n // NOTE: GET_PLUGIN_CHAT_RESPONSE больше не обрабатывается здесь,\n // поскольку ответы на GET_PLUGIN_CHAT теперь обрабатываются через Promise в loadChat()\n\n // Логируем все сообщения, которые приходят, но не обрабатываются\n if (event?.type !== 'PLUGIN_CHAT_UPDATED' && event?.type !== 'GET_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:', {\n type: event?.type,\n fullEvent: event,\n timestamp: new Date().toISOString()\n });\n }\n\n // Обработка результатов сохранения сообщений\n if (event?.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:', event);\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения', event.error);\n setError(`Ошибка сохранения сообщения: ${event.error}`);\n }\n }\n\n // Обработка результатов удаления чата\n if (event?.type === 'DELETE_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат удаления чата:', event);\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:', {\n loading,\n messagesCount: messages.length,\n eventSuccess: event.success,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE');\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: чат успешно удален');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка удаления чата', event.error);\n setError(`Ошибка удаления чата: ${event.error}`);\n }\n }\n\n // === PYODIDE MESSAGE HANDLER ===\n if (event?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', event.message);\n\n if (event.message?.content) {\n try {\n // Строгая проверка типа content для Pyodide сообщений\n let content = event.message.content;\n let messageTimestamp = event.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] PYODIDE content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] PYODIDE content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из PYODIDE content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: event.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:', pyodideError, event);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:', event.message);\n }\n }\n };\n\n // Слушатель для обработки результатов операций с чатом\n const handleChatOperationResult = (message: any) => {\n if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения', message);\n\n if (message.success) {\n console.log('[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено');\n // Не нужно ничего делать дополнительно - обновление придет через PLUGIN_CHAT_UPDATED\n } else {\n console.error('[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения', message.error);\n setError(`Ошибка сохранения сообщения: ${message.error}`);\n }\n }\n };\n\n chrome.runtime.onMessage.addListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы');\n\n return () => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений');\n chrome.runtime.onMessage.removeListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n };\n }, [pluginId, currentPageKey, sendMessageToBackground, loadChat]);\n\n // Восстановление черновика при возврате на вкладку 'Чат'\n useEffect(() => {\n if (currentView === 'chat') {\n loadDraft(); // Явно загружаем черновик при возврате на вкладку чата\n }\n }, [currentView, loadDraft]);\n\n // Логирование каждого рендера и ключевых параметров\n useEffect(() => {\n console.log('[PluginControlPanel] === РЕНДЕР ===', {\n pluginId,\n pageKey: currentPageKey,\n draftText,\n message,\n currentView,\n isRunning,\n isPaused,\n });\n });\n\n // Глобальный обработчик ошибок для ловли проблем с substring\n useEffect(() => {\n const originalConsoleError = console.error;\n console.error = (...args) => {\n // Перехватываем ошибки substring\n const errorMessage = args.join(' ');\n if (errorMessage.includes('substring') && errorMessage.includes('is not a function')) {\n console.error('[PluginControlPanel] 🔴 CRITICAL: substring error detected!');\n console.error('[PluginControlPanel] Error details:', args);\n console.error('[PluginControlPanel] Stack trace:', new Error().stack);\n // Не блокируем оригинальную обработку ошибки\n }\n originalConsoleError.apply(console, args);\n };\n\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring активирован');\n\n return () => {\n console.error = originalConsoleError;\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован');\n };\n }, []);\n\n // Слушатель для Pyodide сообщений через custom events\n useEffect(() => {\n console.log('[PluginControlPanel] Настройка слушателя для pyodide messages');\n\n const handlePyodideCustomEvent = (event: any) => {\n const data = event.detail;\n console.log('[PluginControlPanel] Получен Pyodide custom event:', data);\n\n if (data?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', data.message);\n\n if (data.message?.content) {\n try {\n // Строгая проверка типа content\n let content = data.message.content;\n let messageTimestamp = data.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] Pyodide content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] Pyodide content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из Pyodide content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Pyodide content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: data.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки Pyodide сообщения:', pyodideError, data);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] Pyodide сообщение без content:', data.message);\n }\n }\n };\n\n window.addEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован');\n\n return () => {\n window.removeEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events удален');\n };\n }, []);\n\n // --- Синхронизация message с draftText после загрузки черновика ---\n useEffect(() => {\n if (typeof draftText === 'string') {\n setMessage(draftText);\n console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText);\n }\n }, [draftText, setMessage]);\n\n const handleSendMessage = (): void => {\n console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message });\n if (!message.trim()) return;\n\n const newMessage: ChatMessage = {\n id: Date.now().toString(),\n text: message.trim(),\n isUser: true,\n timestamp: Date.now(),\n };\n\n // Очищаем сообщение через хук\n setMessage('');\n setError(null); // Сбрасываем предыдущие ошибки\n\n console.log('[PluginControlPanel] handleSendMessage: отправка сообщения в background');\n\n sendMessageToBackground({\n type: 'SAVE_PLUGIN_CHAT_MESSAGE',\n pluginId,\n pageKey: currentPageKey,\n message: {\n role: 'user',\n content: newMessage.text,\n timestamp: newMessage.timestamp,\n },\n });\n\n // Очищаем черновик сразу после отправки\n clearDraft();\n };\n\n // Обработка изменения размера разделителя\n useEffect(() => {\n const handleResizeMove = (event: MouseEvent): void => {\n if (!isResizing) return;\n\n const container = document.querySelector('.chat-view') as HTMLElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n const newHeight = containerRect.bottom - event.clientY;\n const minHeight = 100; // Минимальная высота чата\n const maxHeight = containerRect.height - 80; // Максимальная высота чата\n\n if (newHeight >= minHeight && newHeight <= maxHeight) {\n setInputHeight(containerRect.height - newHeight);\n }\n };\n\n const handleResizeEnd = (): void => {\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n if (isResizing) {\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n }\n\n return () => {\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isResizing]);\n\n // Автоскролл к последнему сообщению\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[messages] - состояние messages обновлено:', {\n messagesCount: messages.length,\n firstMessage: messages[0] ? {\n id: messages[0].id,\n text: typeof messages[0].text === 'string' ? messages[0].text.substring(0, 50) : String(messages[0].text || '').substring(0, 50),\n isUser: messages[0].isUser,\n timestamp: messages[0].timestamp,\n textType: typeof messages[0].text\n } : null,\n // Безопасная обработка всех сообщений с проверкой типов\n allMessages: messages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 30) : String(m.text || '').substring(0, 30);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (msgError) {\n console.warn('[PluginControlPanel] Error in message logging:', msgError, m);\n return { id: m.id, text: '[LOGGING ERROR]', isUser: m.isUser, textType: typeof m.text };\n }\n }),\n timestamp: new Date().toISOString()\n });\n }, [messages]);\n\n // Фокус на поле ввода при открытии чата\n useEffect(() => {\n if (currentView === 'chat') {\n setTimeout(() => textareaRef.current?.focus(), 100);\n }\n }, [currentView]);\n\n const handleTextareaChange = (event: React.ChangeEvent): void => {\n setMessage(event.target.value); // Используем хук вместо setMessage\n // Автоматическое изменение высоты\n const textarea = event.target;\n textarea.style.height = 'auto';\n const newHeight = Math.min(Math.max(textarea.scrollHeight, 60), 200); // Минимум 60px, максимум 200px\n textarea.style.height = `${newHeight}px`;\n setInputHeight(newHeight);\n };\n\n const handleKeyPress = (event: React.KeyboardEvent): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n // Очистка чата (удаление всей истории)\n const handleClearChat = (): void => {\n setLoading(true);\n setError(null);\n\n sendMessageToBackground({\n type: 'DELETE_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n // Очищаем локальное состояние сразу\n setMessages([]);\n clearDraft(); // Очищаем черновик\n };\n\n // Экспорт чата в JSON\n const handleExportChat = (): void => {\n const data = JSON.stringify(messages, null, 2);\n const blob = new Blob([data], { type: 'application/json' });\n saveAs(blob, `plugin-chat-${pluginId}.json`);\n };\n\n return (\n
\n
\n
\n {\n const firstChar = typeof pluginName === 'string' && pluginName.length > 0 ? pluginName.charAt(0) : 'P';\n (event.currentTarget as HTMLImageElement).src =\n `data:image/svg+xml;utf8,${firstChar}`;\n }}\n />\n

{pluginName}

\n
\n
\n \n {isRunning ? '⏹️' : '▶️'}\n \n \n ⏸️\n \n \n ⏹️\n \n \n ✕\n \n
\n
\n
\n setActiveTab('chat')}\n >\n Чат\n \n setActiveTab('details')}\n >\n Детали\n \n
\n
\n {activeTab === 'chat' && (\n
\n
\n

Чат

\n
\n \n \n \n 🧪 Тест\n \n
\n
\n
\n {loading &&
Загрузка сообщений...
}\n {error &&
{error}
}\n {!loading && !error && messages.length === 0 && (\n
\n

Нет сообщений

\n

Напишите первое сообщение!

\n
\n )}\n {/* Отображение сообщений чата */}\n
\n {messages.map((msg, idx) => {\n console.log('[PluginControlPanel] render message:', idx, msg);\n\n // Парсинг JSON в тексте сообщения перед рендерингом\n let displayText = msg.text;\n let displayTimestamp = msg.timestamp;\n\n console.log('[PluginControlPanel] Raw message text before parsing for message', idx, ':', msg.text);\n\n try {\n const parsed = JSON.parse(displayText);\n if (typeof parsed === 'object' && parsed !== null && 'content' in parsed) {\n console.log('[PluginControlPanel] Парсинг JSON в рендере:', parsed);\n let content = parsed.content;\n if (typeof content === 'object') {\n displayText = JSON.stringify(content);\n } else {\n displayText = String(content || '');\n }\n if (parsed.timestamp && typeof parsed.timestamp === 'number') {\n displayTimestamp = parsed.timestamp;\n }\n } else if (typeof parsed === 'string') {\n // Если JSON содержит просто строку\n displayText = parsed;\n } else if (typeof parsed === 'object' && parsed !== null) {\n // Если JSON содержит объект без поля content, берем первое строковое поле\n const stringFields = Object.values(parsed).filter(val => typeof val === 'string');\n if (stringFields.length > 0) {\n displayText = String(stringFields[0]);\n } else {\n displayText = JSON.stringify(parsed);\n }\n }\n } catch (parseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Текст не является JSON, рендерим как есть');\n }\n\n console.log('[PluginControlPanel] Display text after parsing for message', idx, ':', displayText);\n\n return (\n \n
\n {displayText}\n \n {new Date(displayTimestamp).toLocaleTimeString()}\n \n
\n
\n );\n })}\n
\n
\n
\n
\n \n \n 📤\n \n \n
\n
\n )}\n {activeTab === 'details' && (\n \n )}\n
\n
\n );\n};","import React, { useState, useEffect, useCallback } from 'react';\nimport './ToastNotifications.css';\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n message: string;\n type: ToastType;\n duration: number;\n timestamp: number;\n}\n\ninterface ToastNotificationsProps {\n toasts: Toast[];\n onRemove: (id: string) => void;\n}\n\nexport const ToastNotifications: React.FC = ({ toasts, onRemove }) => {\n return (\n
\n {toasts.map((toast) => (\n \n ))}\n
\n );\n};\n\ninterface ToastItemProps {\n toast: Toast;\n onRemove: (id: string) => void;\n}\n\nconst ToastItem: React.FC = ({ toast, onRemove }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [isHiding, setIsHiding] = useState(false);\n\n useEffect(() => {\n // Animation in\n requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n // Auto remove\n if (toast.duration > 0) {\n const timer = setTimeout(() => {\n handleRemove();\n }, toast.duration);\n\n return () => clearTimeout(timer);\n }\n\n return undefined; // Explicitly return undefined when no timer is set\n }, [toast.duration]);\n\n const handleRemove = useCallback(() => {\n setIsHiding(true);\n setTimeout(() => {\n onRemove(toast.id);\n }, 300); // Animation duration\n }, [toast.id, onRemove]);\n\n return (\n
\n
\n {toast.message}\n \n
\n
\n );\n};\n\n// Toast manager hook\nexport function useToastManager() {\n const [toasts, setToasts] = useState([]);\n\n const addToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {\n const id = `toast-${Date.now()}-${Math.random()}`;\n const newToast: Toast = {\n id,\n message,\n type,\n duration,\n timestamp: Date.now()\n };\n\n setToasts(prev => {\n const updated = [...prev, newToast];\n // Limit to 3 toasts\n return updated.slice(-3);\n });\n\n return id;\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts(prev => prev.filter(toast => toast.id !== id));\n }, []);\n\n const clearAll = useCallback(() => {\n setToasts([]);\n }, []);\n\n return {\n toasts,\n addToast,\n removeToast,\n clearAll\n };\n}\n\n// Convenience functions\nexport const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {\n // This will be used with the toast manager\n console.log(`[Toast] ${type}: ${message}`);\n};\n\nexport const showSuccessToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'success', duration);\n};\n\nexport const showErrorToast = (message: string, duration: number = 5000) => {\n return showToast(message, 'error', duration);\n};\n\nexport const showWarningToast = (message: string, duration: number = 4000) => {\n return showToast(message, 'warning', duration);\n};\n\nexport const showInfoToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'info', duration);\n}; ","import React from 'react';\n\ninterface ThemeSwitcherProps {\n theme: 'light' | 'dark' | 'system';\n isLight: boolean;\n onToggle: () => void;\n isInSidebar?: boolean; // Контекст использования: sidebar или options\n}\n\nconst ThemeSwitcher: React.FC = ({ theme, isLight, onToggle, isInSidebar = false }) => {\n const getIcon = () => {\n switch (theme) {\n case 'light':\n return '🌙'; // Moon - to switch to dark\n case 'dark':\n return '💻'; // System icon - to switch to system\n case 'system':\n return '☀️'; // Sun - to switch to light\n default:\n return '🌙';\n }\n };\n\n const getTitle = () => {\n switch (theme) {\n case 'light':\n return 'Переключить на темную тему';\n case 'dark':\n return 'Переключить на системную тему';\n case 'system':\n return 'Переключить на светлую тему';\n default:\n return 'Переключить тему';\n }\n };\n\n const buttonStyle: React.CSSProperties = {\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: '50%',\n width: '40px',\n height: '40px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '20px',\n // marginTop только для Options (не в sidebar)\n ...(isInSidebar ? {} : { marginTop: '20px' })\n };\n\n return (\n \n );\n};\n\nexport default ThemeSwitcher;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t {\n const classMap = createClassMap(config);\n const {\n conflictingClassGroups,\n conflictingClassGroupModifiers\n } = config;\n const getClassGroupId = className => {\n const classParts = className.split(CLASS_PART_SEPARATOR);\n // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.\n if (classParts[0] === '' && classParts.length !== 1) {\n classParts.shift();\n }\n return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);\n };\n const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {\n const conflicts = conflictingClassGroups[classGroupId] || [];\n if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {\n return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];\n }\n return conflicts;\n };\n return {\n getClassGroupId,\n getConflictingClassGroupIds\n };\n};\nconst getGroupRecursive = (classParts, classPartObject) => {\n if (classParts.length === 0) {\n return classPartObject.classGroupId;\n }\n const currentClassPart = classParts[0];\n const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);\n const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;\n if (classGroupFromNextClassPart) {\n return classGroupFromNextClassPart;\n }\n if (classPartObject.validators.length === 0) {\n return undefined;\n }\n const classRest = classParts.join(CLASS_PART_SEPARATOR);\n return classPartObject.validators.find(({\n validator\n }) => validator(classRest))?.classGroupId;\n};\nconst arbitraryPropertyRegex = /^\\[(.+)\\]$/;\nconst getGroupIdForArbitraryProperty = className => {\n if (arbitraryPropertyRegex.test(className)) {\n const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];\n const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(':'));\n if (property) {\n // I use two dots here because one dot is used as prefix for class groups in plugins\n return 'arbitrary..' + property;\n }\n }\n};\n/**\n * Exported for testing only\n */\nconst createClassMap = config => {\n const {\n theme,\n classGroups\n } = config;\n const classMap = {\n nextPart: new Map(),\n validators: []\n };\n for (const classGroupId in classGroups) {\n processClassesRecursively(classGroups[classGroupId], classMap, classGroupId, theme);\n }\n return classMap;\n};\nconst processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {\n classGroup.forEach(classDefinition => {\n if (typeof classDefinition === 'string') {\n const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);\n classPartObjectToEdit.classGroupId = classGroupId;\n return;\n }\n if (typeof classDefinition === 'function') {\n if (isThemeGetter(classDefinition)) {\n processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);\n return;\n }\n classPartObject.validators.push({\n validator: classDefinition,\n classGroupId\n });\n return;\n }\n Object.entries(classDefinition).forEach(([key, classGroup]) => {\n processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);\n });\n });\n};\nconst getPart = (classPartObject, path) => {\n let currentClassPartObject = classPartObject;\n path.split(CLASS_PART_SEPARATOR).forEach(pathPart => {\n if (!currentClassPartObject.nextPart.has(pathPart)) {\n currentClassPartObject.nextPart.set(pathPart, {\n nextPart: new Map(),\n validators: []\n });\n }\n currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);\n });\n return currentClassPartObject;\n};\nconst isThemeGetter = func => func.isThemeGetter;\n\n// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance\nconst createLruCache = maxCacheSize => {\n if (maxCacheSize < 1) {\n return {\n get: () => undefined,\n set: () => {}\n };\n }\n let cacheSize = 0;\n let cache = new Map();\n let previousCache = new Map();\n const update = (key, value) => {\n cache.set(key, value);\n cacheSize++;\n if (cacheSize > maxCacheSize) {\n cacheSize = 0;\n previousCache = cache;\n cache = new Map();\n }\n };\n return {\n get(key) {\n let value = cache.get(key);\n if (value !== undefined) {\n return value;\n }\n if ((value = previousCache.get(key)) !== undefined) {\n update(key, value);\n return value;\n }\n },\n set(key, value) {\n if (cache.has(key)) {\n cache.set(key, value);\n } else {\n update(key, value);\n }\n }\n };\n};\nconst IMPORTANT_MODIFIER = '!';\nconst MODIFIER_SEPARATOR = ':';\nconst MODIFIER_SEPARATOR_LENGTH = MODIFIER_SEPARATOR.length;\nconst createParseClassName = config => {\n const {\n prefix,\n experimentalParseClassName\n } = config;\n /**\n * Parse class name into parts.\n *\n * Inspired by `splitAtTopLevelOnly` used in Tailwind CSS\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js\n */\n let parseClassName = className => {\n const modifiers = [];\n let bracketDepth = 0;\n let parenDepth = 0;\n let modifierStart = 0;\n let postfixModifierPosition;\n for (let index = 0; index < className.length; index++) {\n let currentCharacter = className[index];\n if (bracketDepth === 0 && parenDepth === 0) {\n if (currentCharacter === MODIFIER_SEPARATOR) {\n modifiers.push(className.slice(modifierStart, index));\n modifierStart = index + MODIFIER_SEPARATOR_LENGTH;\n continue;\n }\n if (currentCharacter === '/') {\n postfixModifierPosition = index;\n continue;\n }\n }\n if (currentCharacter === '[') {\n bracketDepth++;\n } else if (currentCharacter === ']') {\n bracketDepth--;\n } else if (currentCharacter === '(') {\n parenDepth++;\n } else if (currentCharacter === ')') {\n parenDepth--;\n }\n }\n const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);\n const baseClassName = stripImportantModifier(baseClassNameWithImportantModifier);\n const hasImportantModifier = baseClassName !== baseClassNameWithImportantModifier;\n const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;\n return {\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n };\n };\n if (prefix) {\n const fullPrefix = prefix + MODIFIER_SEPARATOR;\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.substring(fullPrefix.length)) : {\n isExternal: true,\n modifiers: [],\n hasImportantModifier: false,\n baseClassName: className,\n maybePostfixModifierPosition: undefined\n };\n }\n if (experimentalParseClassName) {\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => experimentalParseClassName({\n className,\n parseClassName: parseClassNameOriginal\n });\n }\n return parseClassName;\n};\nconst stripImportantModifier = baseClassName => {\n if (baseClassName.endsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(0, baseClassName.length - 1);\n }\n /**\n * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.\n * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864\n */\n if (baseClassName.startsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(1);\n }\n return baseClassName;\n};\n\n/**\n * Sorts modifiers according to following schema:\n * - Predefined modifiers are sorted alphabetically\n * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it\n */\nconst createSortModifiers = config => {\n const orderSensitiveModifiers = Object.fromEntries(config.orderSensitiveModifiers.map(modifier => [modifier, true]));\n const sortModifiers = modifiers => {\n if (modifiers.length <= 1) {\n return modifiers;\n }\n const sortedModifiers = [];\n let unsortedModifiers = [];\n modifiers.forEach(modifier => {\n const isPositionSensitive = modifier[0] === '[' || orderSensitiveModifiers[modifier];\n if (isPositionSensitive) {\n sortedModifiers.push(...unsortedModifiers.sort(), modifier);\n unsortedModifiers = [];\n } else {\n unsortedModifiers.push(modifier);\n }\n });\n sortedModifiers.push(...unsortedModifiers.sort());\n return sortedModifiers;\n };\n return sortModifiers;\n};\nconst createConfigUtils = config => ({\n cache: createLruCache(config.cacheSize),\n parseClassName: createParseClassName(config),\n sortModifiers: createSortModifiers(config),\n ...createClassGroupUtils(config)\n});\nconst SPLIT_CLASSES_REGEX = /\\s+/;\nconst mergeClassList = (classList, configUtils) => {\n const {\n parseClassName,\n getClassGroupId,\n getConflictingClassGroupIds,\n sortModifiers\n } = configUtils;\n /**\n * Set of classGroupIds in following format:\n * `{importantModifier}{variantModifiers}{classGroupId}`\n * @example 'float'\n * @example 'hover:focus:bg-color'\n * @example 'md:!pr'\n */\n const classGroupsInConflict = [];\n const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);\n let result = '';\n for (let index = classNames.length - 1; index >= 0; index -= 1) {\n const originalClassName = classNames[index];\n const {\n isExternal,\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n } = parseClassName(originalClassName);\n if (isExternal) {\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n let hasPostfixModifier = !!maybePostfixModifierPosition;\n let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);\n if (!classGroupId) {\n if (!hasPostfixModifier) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n classGroupId = getClassGroupId(baseClassName);\n if (!classGroupId) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n hasPostfixModifier = false;\n }\n const variantModifier = sortModifiers(modifiers).join(':');\n const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;\n const classId = modifierId + classGroupId;\n if (classGroupsInConflict.includes(classId)) {\n // Tailwind class omitted due to conflict\n continue;\n }\n classGroupsInConflict.push(classId);\n const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);\n for (let i = 0; i < conflictGroups.length; ++i) {\n const group = conflictGroups[i];\n classGroupsInConflict.push(modifierId + group);\n }\n // Tailwind class not in conflict\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n }\n return result;\n};\n\n/**\n * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.\n *\n * Specifically:\n * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js\n * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts\n *\n * Original code has MIT license: Copyright (c) Luke Edwards (lukeed.com)\n */\nfunction twJoin() {\n let index = 0;\n let argument;\n let resolvedValue;\n let string = '';\n while (index < arguments.length) {\n if (argument = arguments[index++]) {\n if (resolvedValue = toValue(argument)) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n}\nconst toValue = mix => {\n if (typeof mix === 'string') {\n return mix;\n }\n let resolvedValue;\n let string = '';\n for (let k = 0; k < mix.length; k++) {\n if (mix[k]) {\n if (resolvedValue = toValue(mix[k])) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n};\nfunction createTailwindMerge(createConfigFirst, ...createConfigRest) {\n let configUtils;\n let cacheGet;\n let cacheSet;\n let functionToCall = initTailwindMerge;\n function initTailwindMerge(classList) {\n const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());\n configUtils = createConfigUtils(config);\n cacheGet = configUtils.cache.get;\n cacheSet = configUtils.cache.set;\n functionToCall = tailwindMerge;\n return tailwindMerge(classList);\n }\n function tailwindMerge(classList) {\n const cachedResult = cacheGet(classList);\n if (cachedResult) {\n return cachedResult;\n }\n const result = mergeClassList(classList, configUtils);\n cacheSet(classList, result);\n return result;\n }\n return function callTailwindMerge() {\n return functionToCall(twJoin.apply(null, arguments));\n };\n}\nconst fromTheme = key => {\n const themeGetter = theme => theme[key] || [];\n themeGetter.isThemeGetter = true;\n return themeGetter;\n};\nconst arbitraryValueRegex = /^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i;\nconst arbitraryVariableRegex = /^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i;\nconst fractionRegex = /^\\d+\\/\\d+$/;\nconst tshirtUnitRegex = /^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/;\nconst lengthUnitRegex = /\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/;\nconst colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/;\n// Shadow always begins with x and y offset separated by underscore optionally prepended by inset\nconst shadowRegex = /^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/;\nconst imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/;\nconst isFraction = value => fractionRegex.test(value);\nconst isNumber = value => !!value && !Number.isNaN(Number(value));\nconst isInteger = value => !!value && Number.isInteger(Number(value));\nconst isPercent = value => value.endsWith('%') && isNumber(value.slice(0, -1));\nconst isTshirtSize = value => tshirtUnitRegex.test(value);\nconst isAny = () => true;\nconst isLengthOnly = value =>\n// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.\n// For example, `hsl(0 0% 0%)` would be classified as a length without this check.\n// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.\nlengthUnitRegex.test(value) && !colorFunctionRegex.test(value);\nconst isNever = () => false;\nconst isShadow = value => shadowRegex.test(value);\nconst isImage = value => imageRegex.test(value);\nconst isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);\nconst isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);\nconst isArbitraryValue = value => arbitraryValueRegex.test(value);\nconst isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);\nconst isArbitraryNumber = value => getIsArbitraryValue(value, isLabelNumber, isNumber);\nconst isArbitraryPosition = value => getIsArbitraryValue(value, isLabelPosition, isNever);\nconst isArbitraryImage = value => getIsArbitraryValue(value, isLabelImage, isImage);\nconst isArbitraryShadow = value => getIsArbitraryValue(value, isLabelShadow, isShadow);\nconst isArbitraryVariable = value => arbitraryVariableRegex.test(value);\nconst isArbitraryVariableLength = value => getIsArbitraryVariable(value, isLabelLength);\nconst isArbitraryVariableFamilyName = value => getIsArbitraryVariable(value, isLabelFamilyName);\nconst isArbitraryVariablePosition = value => getIsArbitraryVariable(value, isLabelPosition);\nconst isArbitraryVariableSize = value => getIsArbitraryVariable(value, isLabelSize);\nconst isArbitraryVariableImage = value => getIsArbitraryVariable(value, isLabelImage);\nconst isArbitraryVariableShadow = value => getIsArbitraryVariable(value, isLabelShadow, true);\n// Helpers\nconst getIsArbitraryValue = (value, testLabel, testValue) => {\n const result = arbitraryValueRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return testValue(result[2]);\n }\n return false;\n};\nconst getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {\n const result = arbitraryVariableRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return shouldMatchNoLabel;\n }\n return false;\n};\n// Labels\nconst isLabelPosition = label => label === 'position' || label === 'percentage';\nconst isLabelImage = label => label === 'image' || label === 'url';\nconst isLabelSize = label => label === 'length' || label === 'size' || label === 'bg-size';\nconst isLabelLength = label => label === 'length';\nconst isLabelNumber = label => label === 'number';\nconst isLabelFamilyName = label => label === 'family-name';\nconst isLabelShadow = label => label === 'shadow';\nconst validators = /*#__PURE__*/Object.defineProperty({\n __proto__: null,\n isAny,\n isAnyNonArbitrary,\n isArbitraryImage,\n isArbitraryLength,\n isArbitraryNumber,\n isArbitraryPosition,\n isArbitraryShadow,\n isArbitrarySize,\n isArbitraryValue,\n isArbitraryVariable,\n isArbitraryVariableFamilyName,\n isArbitraryVariableImage,\n isArbitraryVariableLength,\n isArbitraryVariablePosition,\n isArbitraryVariableShadow,\n isArbitraryVariableSize,\n isFraction,\n isInteger,\n isNumber,\n isPercent,\n isTshirtSize\n}, Symbol.toStringTag, {\n value: 'Module'\n});\nconst getDefaultConfig = () => {\n /**\n * Theme getters for theme variable namespaces\n * @see https://tailwindcss.com/docs/theme#theme-variable-namespaces\n */\n /***/\n const themeColor = fromTheme('color');\n const themeFont = fromTheme('font');\n const themeText = fromTheme('text');\n const themeFontWeight = fromTheme('font-weight');\n const themeTracking = fromTheme('tracking');\n const themeLeading = fromTheme('leading');\n const themeBreakpoint = fromTheme('breakpoint');\n const themeContainer = fromTheme('container');\n const themeSpacing = fromTheme('spacing');\n const themeRadius = fromTheme('radius');\n const themeShadow = fromTheme('shadow');\n const themeInsetShadow = fromTheme('inset-shadow');\n const themeTextShadow = fromTheme('text-shadow');\n const themeDropShadow = fromTheme('drop-shadow');\n const themeBlur = fromTheme('blur');\n const themePerspective = fromTheme('perspective');\n const themeAspect = fromTheme('aspect');\n const themeEase = fromTheme('ease');\n const themeAnimate = fromTheme('animate');\n /**\n * Helpers to avoid repeating the same scales\n *\n * We use functions that create a new array every time they're called instead of static arrays.\n * This ensures that users who modify any scale by mutating the array (e.g. with `array.push(element)`) don't accidentally mutate arrays in other parts of the config.\n */\n /***/\n const scaleBreak = () => ['auto', 'avoid', 'all', 'avoid-page', 'page', 'left', 'right', 'column'];\n const scalePosition = () => ['center', 'top', 'bottom', 'left', 'right', 'top-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-top', 'top-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-top', 'bottom-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-bottom', 'bottom-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-bottom'];\n const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];\n const scaleOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'];\n const scaleOverscroll = () => ['auto', 'contain', 'none'];\n const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];\n const scaleInset = () => [isFraction, 'full', 'auto', ...scaleUnambiguousSpacing()];\n const scaleGridTemplateColsRows = () => [isInteger, 'none', 'subgrid', isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartAndEnd = () => ['auto', {\n span: ['full', isInteger, isArbitraryVariable, isArbitraryValue]\n }, isInteger, isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartOrEnd = () => [isInteger, 'auto', isArbitraryVariable, isArbitraryValue];\n const scaleGridAutoColsRows = () => ['auto', 'min', 'max', 'fr', isArbitraryVariable, isArbitraryValue];\n const scaleAlignPrimaryAxis = () => ['start', 'end', 'center', 'between', 'around', 'evenly', 'stretch', 'baseline', 'center-safe', 'end-safe'];\n const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];\n const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];\n const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];\n const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];\n const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {\n position: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleBgRepeat = () => ['no-repeat', {\n repeat: ['', 'x', 'y', 'space', 'round']\n }];\n const scaleBgSize = () => ['auto', 'cover', 'contain', isArbitraryVariableSize, isArbitrarySize, {\n size: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];\n const scaleRadius = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', 'full', themeRadius, isArbitraryVariable, isArbitraryValue];\n const scaleBorderWidth = () => ['', isNumber, isArbitraryVariableLength, isArbitraryLength];\n const scaleLineStyle = () => ['solid', 'dashed', 'dotted', 'double'];\n const scaleBlendMode = () => ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];\n const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];\n const scaleBlur = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeBlur, isArbitraryVariable, isArbitraryValue];\n const scaleRotate = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleScale = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleTranslate = () => [isFraction, 'full', ...scaleUnambiguousSpacing()];\n return {\n cacheSize: 500,\n theme: {\n animate: ['spin', 'ping', 'pulse', 'bounce'],\n aspect: ['video'],\n blur: [isTshirtSize],\n breakpoint: [isTshirtSize],\n color: [isAny],\n container: [isTshirtSize],\n 'drop-shadow': [isTshirtSize],\n ease: ['in', 'out', 'in-out'],\n font: [isAnyNonArbitrary],\n 'font-weight': ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black'],\n 'inset-shadow': [isTshirtSize],\n leading: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'],\n perspective: ['dramatic', 'near', 'normal', 'midrange', 'distant', 'none'],\n radius: [isTshirtSize],\n shadow: [isTshirtSize],\n spacing: ['px', isNumber],\n text: [isTshirtSize],\n 'text-shadow': [isTshirtSize],\n tracking: ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest']\n },\n classGroups: {\n // --------------\n // --- Layout ---\n // --------------\n /**\n * Aspect Ratio\n * @see https://tailwindcss.com/docs/aspect-ratio\n */\n aspect: [{\n aspect: ['auto', 'square', isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]\n }],\n /**\n * Container\n * @see https://tailwindcss.com/docs/container\n * @deprecated since Tailwind CSS v4.0.0\n */\n container: ['container'],\n /**\n * Columns\n * @see https://tailwindcss.com/docs/columns\n */\n columns: [{\n columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]\n }],\n /**\n * Break After\n * @see https://tailwindcss.com/docs/break-after\n */\n 'break-after': [{\n 'break-after': scaleBreak()\n }],\n /**\n * Break Before\n * @see https://tailwindcss.com/docs/break-before\n */\n 'break-before': [{\n 'break-before': scaleBreak()\n }],\n /**\n * Break Inside\n * @see https://tailwindcss.com/docs/break-inside\n */\n 'break-inside': [{\n 'break-inside': ['auto', 'avoid', 'avoid-page', 'avoid-column']\n }],\n /**\n * Box Decoration Break\n * @see https://tailwindcss.com/docs/box-decoration-break\n */\n 'box-decoration': [{\n 'box-decoration': ['slice', 'clone']\n }],\n /**\n * Box Sizing\n * @see https://tailwindcss.com/docs/box-sizing\n */\n box: [{\n box: ['border', 'content']\n }],\n /**\n * Display\n * @see https://tailwindcss.com/docs/display\n */\n display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],\n /**\n * Screen Reader Only\n * @see https://tailwindcss.com/docs/display#screen-reader-only\n */\n sr: ['sr-only', 'not-sr-only'],\n /**\n * Floats\n * @see https://tailwindcss.com/docs/float\n */\n float: [{\n float: ['right', 'left', 'none', 'start', 'end']\n }],\n /**\n * Clear\n * @see https://tailwindcss.com/docs/clear\n */\n clear: [{\n clear: ['left', 'right', 'both', 'none', 'start', 'end']\n }],\n /**\n * Isolation\n * @see https://tailwindcss.com/docs/isolation\n */\n isolation: ['isolate', 'isolation-auto'],\n /**\n * Object Fit\n * @see https://tailwindcss.com/docs/object-fit\n */\n 'object-fit': [{\n object: ['contain', 'cover', 'fill', 'none', 'scale-down']\n }],\n /**\n * Object Position\n * @see https://tailwindcss.com/docs/object-position\n */\n 'object-position': [{\n object: scalePositionWithArbitrary()\n }],\n /**\n * Overflow\n * @see https://tailwindcss.com/docs/overflow\n */\n overflow: [{\n overflow: scaleOverflow()\n }],\n /**\n * Overflow X\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-x': [{\n 'overflow-x': scaleOverflow()\n }],\n /**\n * Overflow Y\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-y': [{\n 'overflow-y': scaleOverflow()\n }],\n /**\n * Overscroll Behavior\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n overscroll: [{\n overscroll: scaleOverscroll()\n }],\n /**\n * Overscroll Behavior X\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-x': [{\n 'overscroll-x': scaleOverscroll()\n }],\n /**\n * Overscroll Behavior Y\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-y': [{\n 'overscroll-y': scaleOverscroll()\n }],\n /**\n * Position\n * @see https://tailwindcss.com/docs/position\n */\n position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],\n /**\n * Top / Right / Bottom / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n inset: [{\n inset: scaleInset()\n }],\n /**\n * Right / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-x': [{\n 'inset-x': scaleInset()\n }],\n /**\n * Top / Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-y': [{\n 'inset-y': scaleInset()\n }],\n /**\n * Start\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n start: [{\n start: scaleInset()\n }],\n /**\n * End\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n end: [{\n end: scaleInset()\n }],\n /**\n * Top\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n top: [{\n top: scaleInset()\n }],\n /**\n * Right\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n right: [{\n right: scaleInset()\n }],\n /**\n * Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n bottom: [{\n bottom: scaleInset()\n }],\n /**\n * Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n left: [{\n left: scaleInset()\n }],\n /**\n * Visibility\n * @see https://tailwindcss.com/docs/visibility\n */\n visibility: ['visible', 'invisible', 'collapse'],\n /**\n * Z-Index\n * @see https://tailwindcss.com/docs/z-index\n */\n z: [{\n z: [isInteger, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------------\n // --- Flexbox and Grid ---\n // ------------------------\n /**\n * Flex Basis\n * @see https://tailwindcss.com/docs/flex-basis\n */\n basis: [{\n basis: [isFraction, 'full', 'auto', themeContainer, ...scaleUnambiguousSpacing()]\n }],\n /**\n * Flex Direction\n * @see https://tailwindcss.com/docs/flex-direction\n */\n 'flex-direction': [{\n flex: ['row', 'row-reverse', 'col', 'col-reverse']\n }],\n /**\n * Flex Wrap\n * @see https://tailwindcss.com/docs/flex-wrap\n */\n 'flex-wrap': [{\n flex: ['nowrap', 'wrap', 'wrap-reverse']\n }],\n /**\n * Flex\n * @see https://tailwindcss.com/docs/flex\n */\n flex: [{\n flex: [isNumber, isFraction, 'auto', 'initial', 'none', isArbitraryValue]\n }],\n /**\n * Flex Grow\n * @see https://tailwindcss.com/docs/flex-grow\n */\n grow: [{\n grow: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Flex Shrink\n * @see https://tailwindcss.com/docs/flex-shrink\n */\n shrink: [{\n shrink: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Order\n * @see https://tailwindcss.com/docs/order\n */\n order: [{\n order: [isInteger, 'first', 'last', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Grid Template Columns\n * @see https://tailwindcss.com/docs/grid-template-columns\n */\n 'grid-cols': [{\n 'grid-cols': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Column Start / End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start-end': [{\n col: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Column Start\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start': [{\n 'col-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Column End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-end': [{\n 'col-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Template Rows\n * @see https://tailwindcss.com/docs/grid-template-rows\n */\n 'grid-rows': [{\n 'grid-rows': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Row Start / End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start-end': [{\n row: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Row Start\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start': [{\n 'row-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Row End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-end': [{\n 'row-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Auto Flow\n * @see https://tailwindcss.com/docs/grid-auto-flow\n */\n 'grid-flow': [{\n 'grid-flow': ['row', 'col', 'dense', 'row-dense', 'col-dense']\n }],\n /**\n * Grid Auto Columns\n * @see https://tailwindcss.com/docs/grid-auto-columns\n */\n 'auto-cols': [{\n 'auto-cols': scaleGridAutoColsRows()\n }],\n /**\n * Grid Auto Rows\n * @see https://tailwindcss.com/docs/grid-auto-rows\n */\n 'auto-rows': [{\n 'auto-rows': scaleGridAutoColsRows()\n }],\n /**\n * Gap\n * @see https://tailwindcss.com/docs/gap\n */\n gap: [{\n gap: scaleUnambiguousSpacing()\n }],\n /**\n * Gap X\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-x': [{\n 'gap-x': scaleUnambiguousSpacing()\n }],\n /**\n * Gap Y\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-y': [{\n 'gap-y': scaleUnambiguousSpacing()\n }],\n /**\n * Justify Content\n * @see https://tailwindcss.com/docs/justify-content\n */\n 'justify-content': [{\n justify: [...scaleAlignPrimaryAxis(), 'normal']\n }],\n /**\n * Justify Items\n * @see https://tailwindcss.com/docs/justify-items\n */\n 'justify-items': [{\n 'justify-items': [...scaleAlignSecondaryAxis(), 'normal']\n }],\n /**\n * Justify Self\n * @see https://tailwindcss.com/docs/justify-self\n */\n 'justify-self': [{\n 'justify-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n /**\n * Align Content\n * @see https://tailwindcss.com/docs/align-content\n */\n 'align-content': [{\n content: ['normal', ...scaleAlignPrimaryAxis()]\n }],\n /**\n * Align Items\n * @see https://tailwindcss.com/docs/align-items\n */\n 'align-items': [{\n items: [...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Align Self\n * @see https://tailwindcss.com/docs/align-self\n */\n 'align-self': [{\n self: ['auto', ...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Place Content\n * @see https://tailwindcss.com/docs/place-content\n */\n 'place-content': [{\n 'place-content': scaleAlignPrimaryAxis()\n }],\n /**\n * Place Items\n * @see https://tailwindcss.com/docs/place-items\n */\n 'place-items': [{\n 'place-items': [...scaleAlignSecondaryAxis(), 'baseline']\n }],\n /**\n * Place Self\n * @see https://tailwindcss.com/docs/place-self\n */\n 'place-self': [{\n 'place-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n // Spacing\n /**\n * Padding\n * @see https://tailwindcss.com/docs/padding\n */\n p: [{\n p: scaleUnambiguousSpacing()\n }],\n /**\n * Padding X\n * @see https://tailwindcss.com/docs/padding\n */\n px: [{\n px: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Y\n * @see https://tailwindcss.com/docs/padding\n */\n py: [{\n py: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Start\n * @see https://tailwindcss.com/docs/padding\n */\n ps: [{\n ps: scaleUnambiguousSpacing()\n }],\n /**\n * Padding End\n * @see https://tailwindcss.com/docs/padding\n */\n pe: [{\n pe: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Top\n * @see https://tailwindcss.com/docs/padding\n */\n pt: [{\n pt: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Right\n * @see https://tailwindcss.com/docs/padding\n */\n pr: [{\n pr: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Bottom\n * @see https://tailwindcss.com/docs/padding\n */\n pb: [{\n pb: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Left\n * @see https://tailwindcss.com/docs/padding\n */\n pl: [{\n pl: scaleUnambiguousSpacing()\n }],\n /**\n * Margin\n * @see https://tailwindcss.com/docs/margin\n */\n m: [{\n m: scaleMargin()\n }],\n /**\n * Margin X\n * @see https://tailwindcss.com/docs/margin\n */\n mx: [{\n mx: scaleMargin()\n }],\n /**\n * Margin Y\n * @see https://tailwindcss.com/docs/margin\n */\n my: [{\n my: scaleMargin()\n }],\n /**\n * Margin Start\n * @see https://tailwindcss.com/docs/margin\n */\n ms: [{\n ms: scaleMargin()\n }],\n /**\n * Margin End\n * @see https://tailwindcss.com/docs/margin\n */\n me: [{\n me: scaleMargin()\n }],\n /**\n * Margin Top\n * @see https://tailwindcss.com/docs/margin\n */\n mt: [{\n mt: scaleMargin()\n }],\n /**\n * Margin Right\n * @see https://tailwindcss.com/docs/margin\n */\n mr: [{\n mr: scaleMargin()\n }],\n /**\n * Margin Bottom\n * @see https://tailwindcss.com/docs/margin\n */\n mb: [{\n mb: scaleMargin()\n }],\n /**\n * Margin Left\n * @see https://tailwindcss.com/docs/margin\n */\n ml: [{\n ml: scaleMargin()\n }],\n /**\n * Space Between X\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x': [{\n 'space-x': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between X Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x-reverse': ['space-x-reverse'],\n /**\n * Space Between Y\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y': [{\n 'space-y': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between Y Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y-reverse': ['space-y-reverse'],\n // --------------\n // --- Sizing ---\n // --------------\n /**\n * Size\n * @see https://tailwindcss.com/docs/width#setting-both-width-and-height\n */\n size: [{\n size: scaleSizing()\n }],\n /**\n * Width\n * @see https://tailwindcss.com/docs/width\n */\n w: [{\n w: [themeContainer, 'screen', ...scaleSizing()]\n }],\n /**\n * Min-Width\n * @see https://tailwindcss.com/docs/min-width\n */\n 'min-w': [{\n 'min-w': [themeContainer, 'screen', /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'none', ...scaleSizing()]\n }],\n /**\n * Max-Width\n * @see https://tailwindcss.com/docs/max-width\n */\n 'max-w': [{\n 'max-w': [themeContainer, 'screen', 'none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'prose', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n {\n screen: [themeBreakpoint]\n }, ...scaleSizing()]\n }],\n /**\n * Height\n * @see https://tailwindcss.com/docs/height\n */\n h: [{\n h: ['screen', 'lh', ...scaleSizing()]\n }],\n /**\n * Min-Height\n * @see https://tailwindcss.com/docs/min-height\n */\n 'min-h': [{\n 'min-h': ['screen', 'lh', 'none', ...scaleSizing()]\n }],\n /**\n * Max-Height\n * @see https://tailwindcss.com/docs/max-height\n */\n 'max-h': [{\n 'max-h': ['screen', 'lh', ...scaleSizing()]\n }],\n // ------------------\n // --- Typography ---\n // ------------------\n /**\n * Font Size\n * @see https://tailwindcss.com/docs/font-size\n */\n 'font-size': [{\n text: ['base', themeText, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Font Smoothing\n * @see https://tailwindcss.com/docs/font-smoothing\n */\n 'font-smoothing': ['antialiased', 'subpixel-antialiased'],\n /**\n * Font Style\n * @see https://tailwindcss.com/docs/font-style\n */\n 'font-style': ['italic', 'not-italic'],\n /**\n * Font Weight\n * @see https://tailwindcss.com/docs/font-weight\n */\n 'font-weight': [{\n font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Font Stretch\n * @see https://tailwindcss.com/docs/font-stretch\n */\n 'font-stretch': [{\n 'font-stretch': ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', isPercent, isArbitraryValue]\n }],\n /**\n * Font Family\n * @see https://tailwindcss.com/docs/font-family\n */\n 'font-family': [{\n font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont]\n }],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-normal': ['normal-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-ordinal': ['ordinal'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-slashed-zero': ['slashed-zero'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-figure': ['lining-nums', 'oldstyle-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-spacing': ['proportional-nums', 'tabular-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-fraction': ['diagonal-fractions', 'stacked-fractions'],\n /**\n * Letter Spacing\n * @see https://tailwindcss.com/docs/letter-spacing\n */\n tracking: [{\n tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Line Clamp\n * @see https://tailwindcss.com/docs/line-clamp\n */\n 'line-clamp': [{\n 'line-clamp': [isNumber, 'none', isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Line Height\n * @see https://tailwindcss.com/docs/line-height\n */\n leading: [{\n leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n themeLeading, ...scaleUnambiguousSpacing()]\n }],\n /**\n * List Style Image\n * @see https://tailwindcss.com/docs/list-style-image\n */\n 'list-image': [{\n 'list-image': ['none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * List Style Position\n * @see https://tailwindcss.com/docs/list-style-position\n */\n 'list-style-position': [{\n list: ['inside', 'outside']\n }],\n /**\n * List Style Type\n * @see https://tailwindcss.com/docs/list-style-type\n */\n 'list-style-type': [{\n list: ['disc', 'decimal', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Alignment\n * @see https://tailwindcss.com/docs/text-align\n */\n 'text-alignment': [{\n text: ['left', 'center', 'right', 'justify', 'start', 'end']\n }],\n /**\n * Placeholder Color\n * @deprecated since Tailwind CSS v3.0.0\n * @see https://v3.tailwindcss.com/docs/placeholder-color\n */\n 'placeholder-color': [{\n placeholder: scaleColor()\n }],\n /**\n * Text Color\n * @see https://tailwindcss.com/docs/text-color\n */\n 'text-color': [{\n text: scaleColor()\n }],\n /**\n * Text Decoration\n * @see https://tailwindcss.com/docs/text-decoration\n */\n 'text-decoration': ['underline', 'overline', 'line-through', 'no-underline'],\n /**\n * Text Decoration Style\n * @see https://tailwindcss.com/docs/text-decoration-style\n */\n 'text-decoration-style': [{\n decoration: [...scaleLineStyle(), 'wavy']\n }],\n /**\n * Text Decoration Thickness\n * @see https://tailwindcss.com/docs/text-decoration-thickness\n */\n 'text-decoration-thickness': [{\n decoration: [isNumber, 'from-font', 'auto', isArbitraryVariable, isArbitraryLength]\n }],\n /**\n * Text Decoration Color\n * @see https://tailwindcss.com/docs/text-decoration-color\n */\n 'text-decoration-color': [{\n decoration: scaleColor()\n }],\n /**\n * Text Underline Offset\n * @see https://tailwindcss.com/docs/text-underline-offset\n */\n 'underline-offset': [{\n 'underline-offset': [isNumber, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Transform\n * @see https://tailwindcss.com/docs/text-transform\n */\n 'text-transform': ['uppercase', 'lowercase', 'capitalize', 'normal-case'],\n /**\n * Text Overflow\n * @see https://tailwindcss.com/docs/text-overflow\n */\n 'text-overflow': ['truncate', 'text-ellipsis', 'text-clip'],\n /**\n * Text Wrap\n * @see https://tailwindcss.com/docs/text-wrap\n */\n 'text-wrap': [{\n text: ['wrap', 'nowrap', 'balance', 'pretty']\n }],\n /**\n * Text Indent\n * @see https://tailwindcss.com/docs/text-indent\n */\n indent: [{\n indent: scaleUnambiguousSpacing()\n }],\n /**\n * Vertical Alignment\n * @see https://tailwindcss.com/docs/vertical-align\n */\n 'vertical-align': [{\n align: ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Whitespace\n * @see https://tailwindcss.com/docs/whitespace\n */\n whitespace: [{\n whitespace: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces']\n }],\n /**\n * Word Break\n * @see https://tailwindcss.com/docs/word-break\n */\n break: [{\n break: ['normal', 'words', 'all', 'keep']\n }],\n /**\n * Overflow Wrap\n * @see https://tailwindcss.com/docs/overflow-wrap\n */\n wrap: [{\n wrap: ['break-word', 'anywhere', 'normal']\n }],\n /**\n * Hyphens\n * @see https://tailwindcss.com/docs/hyphens\n */\n hyphens: [{\n hyphens: ['none', 'manual', 'auto']\n }],\n /**\n * Content\n * @see https://tailwindcss.com/docs/content\n */\n content: [{\n content: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // -------------------\n // --- Backgrounds ---\n // -------------------\n /**\n * Background Attachment\n * @see https://tailwindcss.com/docs/background-attachment\n */\n 'bg-attachment': [{\n bg: ['fixed', 'local', 'scroll']\n }],\n /**\n * Background Clip\n * @see https://tailwindcss.com/docs/background-clip\n */\n 'bg-clip': [{\n 'bg-clip': ['border', 'padding', 'content', 'text']\n }],\n /**\n * Background Origin\n * @see https://tailwindcss.com/docs/background-origin\n */\n 'bg-origin': [{\n 'bg-origin': ['border', 'padding', 'content']\n }],\n /**\n * Background Position\n * @see https://tailwindcss.com/docs/background-position\n */\n 'bg-position': [{\n bg: scaleBgPosition()\n }],\n /**\n * Background Repeat\n * @see https://tailwindcss.com/docs/background-repeat\n */\n 'bg-repeat': [{\n bg: scaleBgRepeat()\n }],\n /**\n * Background Size\n * @see https://tailwindcss.com/docs/background-size\n */\n 'bg-size': [{\n bg: scaleBgSize()\n }],\n /**\n * Background Image\n * @see https://tailwindcss.com/docs/background-image\n */\n 'bg-image': [{\n bg: ['none', {\n linear: [{\n to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']\n }, isInteger, isArbitraryVariable, isArbitraryValue],\n radial: ['', isArbitraryVariable, isArbitraryValue],\n conic: [isInteger, isArbitraryVariable, isArbitraryValue]\n }, isArbitraryVariableImage, isArbitraryImage]\n }],\n /**\n * Background Color\n * @see https://tailwindcss.com/docs/background-color\n */\n 'bg-color': [{\n bg: scaleColor()\n }],\n /**\n * Gradient Color Stops From Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from-pos': [{\n from: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops Via Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via-pos': [{\n via: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops To Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to-pos': [{\n to: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops From\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from': [{\n from: scaleColor()\n }],\n /**\n * Gradient Color Stops Via\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via': [{\n via: scaleColor()\n }],\n /**\n * Gradient Color Stops To\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to': [{\n to: scaleColor()\n }],\n // ---------------\n // --- Borders ---\n // ---------------\n /**\n * Border Radius\n * @see https://tailwindcss.com/docs/border-radius\n */\n rounded: [{\n rounded: scaleRadius()\n }],\n /**\n * Border Radius Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-s': [{\n 'rounded-s': scaleRadius()\n }],\n /**\n * Border Radius End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-e': [{\n 'rounded-e': scaleRadius()\n }],\n /**\n * Border Radius Top\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-t': [{\n 'rounded-t': scaleRadius()\n }],\n /**\n * Border Radius Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-r': [{\n 'rounded-r': scaleRadius()\n }],\n /**\n * Border Radius Bottom\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-b': [{\n 'rounded-b': scaleRadius()\n }],\n /**\n * Border Radius Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-l': [{\n 'rounded-l': scaleRadius()\n }],\n /**\n * Border Radius Start Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ss': [{\n 'rounded-ss': scaleRadius()\n }],\n /**\n * Border Radius Start End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-se': [{\n 'rounded-se': scaleRadius()\n }],\n /**\n * Border Radius End End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ee': [{\n 'rounded-ee': scaleRadius()\n }],\n /**\n * Border Radius End Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-es': [{\n 'rounded-es': scaleRadius()\n }],\n /**\n * Border Radius Top Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tl': [{\n 'rounded-tl': scaleRadius()\n }],\n /**\n * Border Radius Top Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tr': [{\n 'rounded-tr': scaleRadius()\n }],\n /**\n * Border Radius Bottom Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-br': [{\n 'rounded-br': scaleRadius()\n }],\n /**\n * Border Radius Bottom Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-bl': [{\n 'rounded-bl': scaleRadius()\n }],\n /**\n * Border Width\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w': [{\n border: scaleBorderWidth()\n }],\n /**\n * Border Width X\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-x': [{\n 'border-x': scaleBorderWidth()\n }],\n /**\n * Border Width Y\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-y': [{\n 'border-y': scaleBorderWidth()\n }],\n /**\n * Border Width Start\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-s': [{\n 'border-s': scaleBorderWidth()\n }],\n /**\n * Border Width End\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-e': [{\n 'border-e': scaleBorderWidth()\n }],\n /**\n * Border Width Top\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-t': [{\n 'border-t': scaleBorderWidth()\n }],\n /**\n * Border Width Right\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-r': [{\n 'border-r': scaleBorderWidth()\n }],\n /**\n * Border Width Bottom\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-b': [{\n 'border-b': scaleBorderWidth()\n }],\n /**\n * Border Width Left\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-l': [{\n 'border-l': scaleBorderWidth()\n }],\n /**\n * Divide Width X\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x': [{\n 'divide-x': scaleBorderWidth()\n }],\n /**\n * Divide Width X Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x-reverse': ['divide-x-reverse'],\n /**\n * Divide Width Y\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y': [{\n 'divide-y': scaleBorderWidth()\n }],\n /**\n * Divide Width Y Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y-reverse': ['divide-y-reverse'],\n /**\n * Border Style\n * @see https://tailwindcss.com/docs/border-style\n */\n 'border-style': [{\n border: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Divide Style\n * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style\n */\n 'divide-style': [{\n divide: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Border Color\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color': [{\n border: scaleColor()\n }],\n /**\n * Border Color X\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-x': [{\n 'border-x': scaleColor()\n }],\n /**\n * Border Color Y\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-y': [{\n 'border-y': scaleColor()\n }],\n /**\n * Border Color S\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-s': [{\n 'border-s': scaleColor()\n }],\n /**\n * Border Color E\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-e': [{\n 'border-e': scaleColor()\n }],\n /**\n * Border Color Top\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-t': [{\n 'border-t': scaleColor()\n }],\n /**\n * Border Color Right\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-r': [{\n 'border-r': scaleColor()\n }],\n /**\n * Border Color Bottom\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-b': [{\n 'border-b': scaleColor()\n }],\n /**\n * Border Color Left\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-l': [{\n 'border-l': scaleColor()\n }],\n /**\n * Divide Color\n * @see https://tailwindcss.com/docs/divide-color\n */\n 'divide-color': [{\n divide: scaleColor()\n }],\n /**\n * Outline Style\n * @see https://tailwindcss.com/docs/outline-style\n */\n 'outline-style': [{\n outline: [...scaleLineStyle(), 'none', 'hidden']\n }],\n /**\n * Outline Offset\n * @see https://tailwindcss.com/docs/outline-offset\n */\n 'outline-offset': [{\n 'outline-offset': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Outline Width\n * @see https://tailwindcss.com/docs/outline-width\n */\n 'outline-w': [{\n outline: ['', isNumber, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Outline Color\n * @see https://tailwindcss.com/docs/outline-color\n */\n 'outline-color': [{\n outline: scaleColor()\n }],\n // ---------------\n // --- Effects ---\n // ---------------\n /**\n * Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow\n */\n shadow: [{\n shadow: [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color\n */\n 'shadow-color': [{\n shadow: scaleColor()\n }],\n /**\n * Inset Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow\n */\n 'inset-shadow': [{\n 'inset-shadow': ['none', themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Inset Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color\n */\n 'inset-shadow-color': [{\n 'inset-shadow': scaleColor()\n }],\n /**\n * Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring\n */\n 'ring-w': [{\n ring: scaleBorderWidth()\n }],\n /**\n * Ring Width Inset\n * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-w-inset': ['ring-inset'],\n /**\n * Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color\n */\n 'ring-color': [{\n ring: scaleColor()\n }],\n /**\n * Ring Offset Width\n * @see https://v3.tailwindcss.com/docs/ring-offset-width\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-w': [{\n 'ring-offset': [isNumber, isArbitraryLength]\n }],\n /**\n * Ring Offset Color\n * @see https://v3.tailwindcss.com/docs/ring-offset-color\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-color': [{\n 'ring-offset': scaleColor()\n }],\n /**\n * Inset Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring\n */\n 'inset-ring-w': [{\n 'inset-ring': scaleBorderWidth()\n }],\n /**\n * Inset Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color\n */\n 'inset-ring-color': [{\n 'inset-ring': scaleColor()\n }],\n /**\n * Text Shadow\n * @see https://tailwindcss.com/docs/text-shadow\n */\n 'text-shadow': [{\n 'text-shadow': ['none', themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Text Shadow Color\n * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color\n */\n 'text-shadow-color': [{\n 'text-shadow': scaleColor()\n }],\n /**\n * Opacity\n * @see https://tailwindcss.com/docs/opacity\n */\n opacity: [{\n opacity: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Mix Blend Mode\n * @see https://tailwindcss.com/docs/mix-blend-mode\n */\n 'mix-blend': [{\n 'mix-blend': [...scaleBlendMode(), 'plus-darker', 'plus-lighter']\n }],\n /**\n * Background Blend Mode\n * @see https://tailwindcss.com/docs/background-blend-mode\n */\n 'bg-blend': [{\n 'bg-blend': scaleBlendMode()\n }],\n /**\n * Mask Clip\n * @see https://tailwindcss.com/docs/mask-clip\n */\n 'mask-clip': [{\n 'mask-clip': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }, 'mask-no-clip'],\n /**\n * Mask Composite\n * @see https://tailwindcss.com/docs/mask-composite\n */\n 'mask-composite': [{\n mask: ['add', 'subtract', 'intersect', 'exclude']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image-linear-pos': [{\n 'mask-linear': [isNumber]\n }],\n 'mask-image-linear-from-pos': [{\n 'mask-linear-from': scaleMaskImagePosition()\n }],\n 'mask-image-linear-to-pos': [{\n 'mask-linear-to': scaleMaskImagePosition()\n }],\n 'mask-image-linear-from-color': [{\n 'mask-linear-from': scaleColor()\n }],\n 'mask-image-linear-to-color': [{\n 'mask-linear-to': scaleColor()\n }],\n 'mask-image-t-from-pos': [{\n 'mask-t-from': scaleMaskImagePosition()\n }],\n 'mask-image-t-to-pos': [{\n 'mask-t-to': scaleMaskImagePosition()\n }],\n 'mask-image-t-from-color': [{\n 'mask-t-from': scaleColor()\n }],\n 'mask-image-t-to-color': [{\n 'mask-t-to': scaleColor()\n }],\n 'mask-image-r-from-pos': [{\n 'mask-r-from': scaleMaskImagePosition()\n }],\n 'mask-image-r-to-pos': [{\n 'mask-r-to': scaleMaskImagePosition()\n }],\n 'mask-image-r-from-color': [{\n 'mask-r-from': scaleColor()\n }],\n 'mask-image-r-to-color': [{\n 'mask-r-to': scaleColor()\n }],\n 'mask-image-b-from-pos': [{\n 'mask-b-from': scaleMaskImagePosition()\n }],\n 'mask-image-b-to-pos': [{\n 'mask-b-to': scaleMaskImagePosition()\n }],\n 'mask-image-b-from-color': [{\n 'mask-b-from': scaleColor()\n }],\n 'mask-image-b-to-color': [{\n 'mask-b-to': scaleColor()\n }],\n 'mask-image-l-from-pos': [{\n 'mask-l-from': scaleMaskImagePosition()\n }],\n 'mask-image-l-to-pos': [{\n 'mask-l-to': scaleMaskImagePosition()\n }],\n 'mask-image-l-from-color': [{\n 'mask-l-from': scaleColor()\n }],\n 'mask-image-l-to-color': [{\n 'mask-l-to': scaleColor()\n }],\n 'mask-image-x-from-pos': [{\n 'mask-x-from': scaleMaskImagePosition()\n }],\n 'mask-image-x-to-pos': [{\n 'mask-x-to': scaleMaskImagePosition()\n }],\n 'mask-image-x-from-color': [{\n 'mask-x-from': scaleColor()\n }],\n 'mask-image-x-to-color': [{\n 'mask-x-to': scaleColor()\n }],\n 'mask-image-y-from-pos': [{\n 'mask-y-from': scaleMaskImagePosition()\n }],\n 'mask-image-y-to-pos': [{\n 'mask-y-to': scaleMaskImagePosition()\n }],\n 'mask-image-y-from-color': [{\n 'mask-y-from': scaleColor()\n }],\n 'mask-image-y-to-color': [{\n 'mask-y-to': scaleColor()\n }],\n 'mask-image-radial': [{\n 'mask-radial': [isArbitraryVariable, isArbitraryValue]\n }],\n 'mask-image-radial-from-pos': [{\n 'mask-radial-from': scaleMaskImagePosition()\n }],\n 'mask-image-radial-to-pos': [{\n 'mask-radial-to': scaleMaskImagePosition()\n }],\n 'mask-image-radial-from-color': [{\n 'mask-radial-from': scaleColor()\n }],\n 'mask-image-radial-to-color': [{\n 'mask-radial-to': scaleColor()\n }],\n 'mask-image-radial-shape': [{\n 'mask-radial': ['circle', 'ellipse']\n }],\n 'mask-image-radial-size': [{\n 'mask-radial': [{\n closest: ['side', 'corner'],\n farthest: ['side', 'corner']\n }]\n }],\n 'mask-image-radial-pos': [{\n 'mask-radial-at': scalePosition()\n }],\n 'mask-image-conic-pos': [{\n 'mask-conic': [isNumber]\n }],\n 'mask-image-conic-from-pos': [{\n 'mask-conic-from': scaleMaskImagePosition()\n }],\n 'mask-image-conic-to-pos': [{\n 'mask-conic-to': scaleMaskImagePosition()\n }],\n 'mask-image-conic-from-color': [{\n 'mask-conic-from': scaleColor()\n }],\n 'mask-image-conic-to-color': [{\n 'mask-conic-to': scaleColor()\n }],\n /**\n * Mask Mode\n * @see https://tailwindcss.com/docs/mask-mode\n */\n 'mask-mode': [{\n mask: ['alpha', 'luminance', 'match']\n }],\n /**\n * Mask Origin\n * @see https://tailwindcss.com/docs/mask-origin\n */\n 'mask-origin': [{\n 'mask-origin': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }],\n /**\n * Mask Position\n * @see https://tailwindcss.com/docs/mask-position\n */\n 'mask-position': [{\n mask: scaleBgPosition()\n }],\n /**\n * Mask Repeat\n * @see https://tailwindcss.com/docs/mask-repeat\n */\n 'mask-repeat': [{\n mask: scaleBgRepeat()\n }],\n /**\n * Mask Size\n * @see https://tailwindcss.com/docs/mask-size\n */\n 'mask-size': [{\n mask: scaleBgSize()\n }],\n /**\n * Mask Type\n * @see https://tailwindcss.com/docs/mask-type\n */\n 'mask-type': [{\n 'mask-type': ['alpha', 'luminance']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image': [{\n mask: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // ---------------\n // --- Filters ---\n // ---------------\n /**\n * Filter\n * @see https://tailwindcss.com/docs/filter\n */\n filter: [{\n filter: [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Blur\n * @see https://tailwindcss.com/docs/blur\n */\n blur: [{\n blur: scaleBlur()\n }],\n /**\n * Brightness\n * @see https://tailwindcss.com/docs/brightness\n */\n brightness: [{\n brightness: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Contrast\n * @see https://tailwindcss.com/docs/contrast\n */\n contrast: [{\n contrast: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Drop Shadow\n * @see https://tailwindcss.com/docs/drop-shadow\n */\n 'drop-shadow': [{\n 'drop-shadow': [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeDropShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Drop Shadow Color\n * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color\n */\n 'drop-shadow-color': [{\n 'drop-shadow': scaleColor()\n }],\n /**\n * Grayscale\n * @see https://tailwindcss.com/docs/grayscale\n */\n grayscale: [{\n grayscale: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Hue Rotate\n * @see https://tailwindcss.com/docs/hue-rotate\n */\n 'hue-rotate': [{\n 'hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Invert\n * @see https://tailwindcss.com/docs/invert\n */\n invert: [{\n invert: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Saturate\n * @see https://tailwindcss.com/docs/saturate\n */\n saturate: [{\n saturate: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Sepia\n * @see https://tailwindcss.com/docs/sepia\n */\n sepia: [{\n sepia: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Filter\n * @see https://tailwindcss.com/docs/backdrop-filter\n */\n 'backdrop-filter': [{\n 'backdrop-filter': [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Blur\n * @see https://tailwindcss.com/docs/backdrop-blur\n */\n 'backdrop-blur': [{\n 'backdrop-blur': scaleBlur()\n }],\n /**\n * Backdrop Brightness\n * @see https://tailwindcss.com/docs/backdrop-brightness\n */\n 'backdrop-brightness': [{\n 'backdrop-brightness': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Contrast\n * @see https://tailwindcss.com/docs/backdrop-contrast\n */\n 'backdrop-contrast': [{\n 'backdrop-contrast': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Grayscale\n * @see https://tailwindcss.com/docs/backdrop-grayscale\n */\n 'backdrop-grayscale': [{\n 'backdrop-grayscale': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Hue Rotate\n * @see https://tailwindcss.com/docs/backdrop-hue-rotate\n */\n 'backdrop-hue-rotate': [{\n 'backdrop-hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Invert\n * @see https://tailwindcss.com/docs/backdrop-invert\n */\n 'backdrop-invert': [{\n 'backdrop-invert': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Opacity\n * @see https://tailwindcss.com/docs/backdrop-opacity\n */\n 'backdrop-opacity': [{\n 'backdrop-opacity': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Saturate\n * @see https://tailwindcss.com/docs/backdrop-saturate\n */\n 'backdrop-saturate': [{\n 'backdrop-saturate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Sepia\n * @see https://tailwindcss.com/docs/backdrop-sepia\n */\n 'backdrop-sepia': [{\n 'backdrop-sepia': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n // --------------\n // --- Tables ---\n // --------------\n /**\n * Border Collapse\n * @see https://tailwindcss.com/docs/border-collapse\n */\n 'border-collapse': [{\n border: ['collapse', 'separate']\n }],\n /**\n * Border Spacing\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing': [{\n 'border-spacing': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing X\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-x': [{\n 'border-spacing-x': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing Y\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-y': [{\n 'border-spacing-y': scaleUnambiguousSpacing()\n }],\n /**\n * Table Layout\n * @see https://tailwindcss.com/docs/table-layout\n */\n 'table-layout': [{\n table: ['auto', 'fixed']\n }],\n /**\n * Caption Side\n * @see https://tailwindcss.com/docs/caption-side\n */\n caption: [{\n caption: ['top', 'bottom']\n }],\n // ---------------------------------\n // --- Transitions and Animation ---\n // ---------------------------------\n /**\n * Transition Property\n * @see https://tailwindcss.com/docs/transition-property\n */\n transition: [{\n transition: ['', 'all', 'colors', 'opacity', 'shadow', 'transform', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Behavior\n * @see https://tailwindcss.com/docs/transition-behavior\n */\n 'transition-behavior': [{\n transition: ['normal', 'discrete']\n }],\n /**\n * Transition Duration\n * @see https://tailwindcss.com/docs/transition-duration\n */\n duration: [{\n duration: [isNumber, 'initial', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Timing Function\n * @see https://tailwindcss.com/docs/transition-timing-function\n */\n ease: [{\n ease: ['linear', 'initial', themeEase, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Delay\n * @see https://tailwindcss.com/docs/transition-delay\n */\n delay: [{\n delay: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Animation\n * @see https://tailwindcss.com/docs/animation\n */\n animate: [{\n animate: ['none', themeAnimate, isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------\n // --- Transforms ---\n // ------------------\n /**\n * Backface Visibility\n * @see https://tailwindcss.com/docs/backface-visibility\n */\n backface: [{\n backface: ['hidden', 'visible']\n }],\n /**\n * Perspective\n * @see https://tailwindcss.com/docs/perspective\n */\n perspective: [{\n perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Perspective Origin\n * @see https://tailwindcss.com/docs/perspective-origin\n */\n 'perspective-origin': [{\n 'perspective-origin': scalePositionWithArbitrary()\n }],\n /**\n * Rotate\n * @see https://tailwindcss.com/docs/rotate\n */\n rotate: [{\n rotate: scaleRotate()\n }],\n /**\n * Rotate X\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-x': [{\n 'rotate-x': scaleRotate()\n }],\n /**\n * Rotate Y\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-y': [{\n 'rotate-y': scaleRotate()\n }],\n /**\n * Rotate Z\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-z': [{\n 'rotate-z': scaleRotate()\n }],\n /**\n * Scale\n * @see https://tailwindcss.com/docs/scale\n */\n scale: [{\n scale: scaleScale()\n }],\n /**\n * Scale X\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-x': [{\n 'scale-x': scaleScale()\n }],\n /**\n * Scale Y\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-y': [{\n 'scale-y': scaleScale()\n }],\n /**\n * Scale Z\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-z': [{\n 'scale-z': scaleScale()\n }],\n /**\n * Scale 3D\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-3d': ['scale-3d'],\n /**\n * Skew\n * @see https://tailwindcss.com/docs/skew\n */\n skew: [{\n skew: scaleSkew()\n }],\n /**\n * Skew X\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-x': [{\n 'skew-x': scaleSkew()\n }],\n /**\n * Skew Y\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-y': [{\n 'skew-y': scaleSkew()\n }],\n /**\n * Transform\n * @see https://tailwindcss.com/docs/transform\n */\n transform: [{\n transform: [isArbitraryVariable, isArbitraryValue, '', 'none', 'gpu', 'cpu']\n }],\n /**\n * Transform Origin\n * @see https://tailwindcss.com/docs/transform-origin\n */\n 'transform-origin': [{\n origin: scalePositionWithArbitrary()\n }],\n /**\n * Transform Style\n * @see https://tailwindcss.com/docs/transform-style\n */\n 'transform-style': [{\n transform: ['3d', 'flat']\n }],\n /**\n * Translate\n * @see https://tailwindcss.com/docs/translate\n */\n translate: [{\n translate: scaleTranslate()\n }],\n /**\n * Translate X\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-x': [{\n 'translate-x': scaleTranslate()\n }],\n /**\n * Translate Y\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-y': [{\n 'translate-y': scaleTranslate()\n }],\n /**\n * Translate Z\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-z': [{\n 'translate-z': scaleTranslate()\n }],\n /**\n * Translate None\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-none': ['translate-none'],\n // ---------------------\n // --- Interactivity ---\n // ---------------------\n /**\n * Accent Color\n * @see https://tailwindcss.com/docs/accent-color\n */\n accent: [{\n accent: scaleColor()\n }],\n /**\n * Appearance\n * @see https://tailwindcss.com/docs/appearance\n */\n appearance: [{\n appearance: ['none', 'auto']\n }],\n /**\n * Caret Color\n * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities\n */\n 'caret-color': [{\n caret: scaleColor()\n }],\n /**\n * Color Scheme\n * @see https://tailwindcss.com/docs/color-scheme\n */\n 'color-scheme': [{\n scheme: ['normal', 'dark', 'light', 'light-dark', 'only-dark', 'only-light']\n }],\n /**\n * Cursor\n * @see https://tailwindcss.com/docs/cursor\n */\n cursor: [{\n cursor: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'help', 'not-allowed', 'none', 'context-menu', 'progress', 'cell', 'crosshair', 'vertical-text', 'alias', 'copy', 'no-drop', 'grab', 'grabbing', 'all-scroll', 'col-resize', 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Field Sizing\n * @see https://tailwindcss.com/docs/field-sizing\n */\n 'field-sizing': [{\n 'field-sizing': ['fixed', 'content']\n }],\n /**\n * Pointer Events\n * @see https://tailwindcss.com/docs/pointer-events\n */\n 'pointer-events': [{\n 'pointer-events': ['auto', 'none']\n }],\n /**\n * Resize\n * @see https://tailwindcss.com/docs/resize\n */\n resize: [{\n resize: ['none', '', 'y', 'x']\n }],\n /**\n * Scroll Behavior\n * @see https://tailwindcss.com/docs/scroll-behavior\n */\n 'scroll-behavior': [{\n scroll: ['auto', 'smooth']\n }],\n /**\n * Scroll Margin\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-m': [{\n 'scroll-m': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin X\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mx': [{\n 'scroll-mx': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Y\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-my': [{\n 'scroll-my': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Start\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ms': [{\n 'scroll-ms': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin End\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-me': [{\n 'scroll-me': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Top\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mt': [{\n 'scroll-mt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Right\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mr': [{\n 'scroll-mr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Bottom\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mb': [{\n 'scroll-mb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Left\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ml': [{\n 'scroll-ml': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-p': [{\n 'scroll-p': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding X\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-px': [{\n 'scroll-px': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Y\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-py': [{\n 'scroll-py': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Start\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-ps': [{\n 'scroll-ps': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding End\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pe': [{\n 'scroll-pe': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Top\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pt': [{\n 'scroll-pt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Right\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pr': [{\n 'scroll-pr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Bottom\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pb': [{\n 'scroll-pb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Left\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pl': [{\n 'scroll-pl': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Snap Align\n * @see https://tailwindcss.com/docs/scroll-snap-align\n */\n 'snap-align': [{\n snap: ['start', 'end', 'center', 'align-none']\n }],\n /**\n * Scroll Snap Stop\n * @see https://tailwindcss.com/docs/scroll-snap-stop\n */\n 'snap-stop': [{\n snap: ['normal', 'always']\n }],\n /**\n * Scroll Snap Type\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-type': [{\n snap: ['none', 'x', 'y', 'both']\n }],\n /**\n * Scroll Snap Type Strictness\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-strictness': [{\n snap: ['mandatory', 'proximity']\n }],\n /**\n * Touch Action\n * @see https://tailwindcss.com/docs/touch-action\n */\n touch: [{\n touch: ['auto', 'none', 'manipulation']\n }],\n /**\n * Touch Action X\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-x': [{\n 'touch-pan': ['x', 'left', 'right']\n }],\n /**\n * Touch Action Y\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-y': [{\n 'touch-pan': ['y', 'up', 'down']\n }],\n /**\n * Touch Action Pinch Zoom\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-pz': ['touch-pinch-zoom'],\n /**\n * User Select\n * @see https://tailwindcss.com/docs/user-select\n */\n select: [{\n select: ['none', 'text', 'all', 'auto']\n }],\n /**\n * Will Change\n * @see https://tailwindcss.com/docs/will-change\n */\n 'will-change': [{\n 'will-change': ['auto', 'scroll', 'contents', 'transform', isArbitraryVariable, isArbitraryValue]\n }],\n // -----------\n // --- SVG ---\n // -----------\n /**\n * Fill\n * @see https://tailwindcss.com/docs/fill\n */\n fill: [{\n fill: ['none', ...scaleColor()]\n }],\n /**\n * Stroke Width\n * @see https://tailwindcss.com/docs/stroke-width\n */\n 'stroke-w': [{\n stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]\n }],\n /**\n * Stroke\n * @see https://tailwindcss.com/docs/stroke\n */\n stroke: [{\n stroke: ['none', ...scaleColor()]\n }],\n // ---------------------\n // --- Accessibility ---\n // ---------------------\n /**\n * Forced Color Adjust\n * @see https://tailwindcss.com/docs/forced-color-adjust\n */\n 'forced-color-adjust': [{\n 'forced-color-adjust': ['auto', 'none']\n }]\n },\n conflictingClassGroups: {\n overflow: ['overflow-x', 'overflow-y'],\n overscroll: ['overscroll-x', 'overscroll-y'],\n inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],\n 'inset-x': ['right', 'left'],\n 'inset-y': ['top', 'bottom'],\n flex: ['basis', 'grow', 'shrink'],\n gap: ['gap-x', 'gap-y'],\n p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],\n px: ['pr', 'pl'],\n py: ['pt', 'pb'],\n m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],\n mx: ['mr', 'ml'],\n my: ['mt', 'mb'],\n size: ['w', 'h'],\n 'font-size': ['leading'],\n 'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],\n 'fvn-ordinal': ['fvn-normal'],\n 'fvn-slashed-zero': ['fvn-normal'],\n 'fvn-figure': ['fvn-normal'],\n 'fvn-spacing': ['fvn-normal'],\n 'fvn-fraction': ['fvn-normal'],\n 'line-clamp': ['display', 'overflow'],\n rounded: ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],\n 'rounded-s': ['rounded-ss', 'rounded-es'],\n 'rounded-e': ['rounded-se', 'rounded-ee'],\n 'rounded-t': ['rounded-tl', 'rounded-tr'],\n 'rounded-r': ['rounded-tr', 'rounded-br'],\n 'rounded-b': ['rounded-br', 'rounded-bl'],\n 'rounded-l': ['rounded-tl', 'rounded-bl'],\n 'border-spacing': ['border-spacing-x', 'border-spacing-y'],\n 'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],\n 'border-w-x': ['border-w-r', 'border-w-l'],\n 'border-w-y': ['border-w-t', 'border-w-b'],\n 'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],\n 'border-color-x': ['border-color-r', 'border-color-l'],\n 'border-color-y': ['border-color-t', 'border-color-b'],\n translate: ['translate-x', 'translate-y', 'translate-none'],\n 'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],\n 'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],\n 'scroll-mx': ['scroll-mr', 'scroll-ml'],\n 'scroll-my': ['scroll-mt', 'scroll-mb'],\n 'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],\n 'scroll-px': ['scroll-pr', 'scroll-pl'],\n 'scroll-py': ['scroll-pt', 'scroll-pb'],\n touch: ['touch-x', 'touch-y', 'touch-pz'],\n 'touch-x': ['touch'],\n 'touch-y': ['touch'],\n 'touch-pz': ['touch']\n },\n conflictingClassGroupModifiers: {\n 'font-size': ['leading']\n },\n orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']\n };\n};\n\n/**\n * @param baseConfig Config where other config will be merged into. This object will be mutated.\n * @param configExtension Partial config to merge into the `baseConfig`.\n */\nconst mergeConfigs = (baseConfig, {\n cacheSize,\n prefix,\n experimentalParseClassName,\n extend = {},\n override = {}\n}) => {\n overrideProperty(baseConfig, 'cacheSize', cacheSize);\n overrideProperty(baseConfig, 'prefix', prefix);\n overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName);\n overrideConfigProperties(baseConfig.theme, override.theme);\n overrideConfigProperties(baseConfig.classGroups, override.classGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroupModifiers, override.conflictingClassGroupModifiers);\n overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers);\n mergeConfigProperties(baseConfig.theme, extend.theme);\n mergeConfigProperties(baseConfig.classGroups, extend.classGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroupModifiers, extend.conflictingClassGroupModifiers);\n mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers');\n return baseConfig;\n};\nconst overrideProperty = (baseObject, overrideKey, overrideValue) => {\n if (overrideValue !== undefined) {\n baseObject[overrideKey] = overrideValue;\n }\n};\nconst overrideConfigProperties = (baseObject, overrideObject) => {\n if (overrideObject) {\n for (const key in overrideObject) {\n overrideProperty(baseObject, key, overrideObject[key]);\n }\n }\n};\nconst mergeConfigProperties = (baseObject, mergeObject) => {\n if (mergeObject) {\n for (const key in mergeObject) {\n mergeArrayProperties(baseObject, mergeObject, key);\n }\n }\n};\nconst mergeArrayProperties = (baseObject, mergeObject, key) => {\n const mergeValue = mergeObject[key];\n if (mergeValue !== undefined) {\n baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue;\n }\n};\nconst extendTailwindMerge = (configExtension, ...createConfig) => typeof configExtension === 'function' ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig) : createTailwindMerge(() => mergeConfigs(getDefaultConfig(), configExtension), ...createConfig);\nconst twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);\nexport { createTailwindMerge, extendTailwindMerge, fromTheme, getDefaultConfig, mergeConfigs, twJoin, twMerge, validators };\n//# sourceMappingURL=bundle-mjs.mjs.map\n","import { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nexport const cn = (...inputs) => twMerge(clsx(inputs));\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","/**\n * @license React\n * scheduler.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nfunction push(heap, node) {\n var index = heap.length;\n heap.push(node);\n a: for (; 0 < index; ) {\n var parentIndex = (index - 1) >>> 1,\n parent = heap[parentIndex];\n if (0 < compare(parent, node))\n (heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);\n else break a;\n }\n}\nfunction peek(heap) {\n return 0 === heap.length ? null : heap[0];\n}\nfunction pop(heap) {\n if (0 === heap.length) return null;\n var first = heap[0],\n last = heap.pop();\n if (last !== first) {\n heap[0] = last;\n a: for (\n var index = 0, length = heap.length, halfLength = length >>> 1;\n index < halfLength;\n\n ) {\n var leftIndex = 2 * (index + 1) - 1,\n left = heap[leftIndex],\n rightIndex = leftIndex + 1,\n right = heap[rightIndex];\n if (0 > compare(left, last))\n rightIndex < length && 0 > compare(right, left)\n ? ((heap[index] = right),\n (heap[rightIndex] = last),\n (index = rightIndex))\n : ((heap[index] = left),\n (heap[leftIndex] = last),\n (index = leftIndex));\n else if (rightIndex < length && 0 > compare(right, last))\n (heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);\n else break a;\n }\n }\n return first;\n}\nfunction compare(a, b) {\n var diff = a.sortIndex - b.sortIndex;\n return 0 !== diff ? diff : a.id - b.id;\n}\nexports.unstable_now = void 0;\nif (\"object\" === typeof performance && \"function\" === typeof performance.now) {\n var localPerformance = performance;\n exports.unstable_now = function () {\n return localPerformance.now();\n };\n} else {\n var localDate = Date,\n initialTime = localDate.now();\n exports.unstable_now = function () {\n return localDate.now() - initialTime;\n };\n}\nvar taskQueue = [],\n timerQueue = [],\n taskIdCounter = 1,\n currentTask = null,\n currentPriorityLevel = 3,\n isPerformingWork = !1,\n isHostCallbackScheduled = !1,\n isHostTimeoutScheduled = !1,\n needsPaint = !1,\n localSetTimeout = \"function\" === typeof setTimeout ? setTimeout : null,\n localClearTimeout = \"function\" === typeof clearTimeout ? clearTimeout : null,\n localSetImmediate = \"undefined\" !== typeof setImmediate ? setImmediate : null;\nfunction advanceTimers(currentTime) {\n for (var timer = peek(timerQueue); null !== timer; ) {\n if (null === timer.callback) pop(timerQueue);\n else if (timer.startTime <= currentTime)\n pop(timerQueue),\n (timer.sortIndex = timer.expirationTime),\n push(taskQueue, timer);\n else break;\n timer = peek(timerQueue);\n }\n}\nfunction handleTimeout(currentTime) {\n isHostTimeoutScheduled = !1;\n advanceTimers(currentTime);\n if (!isHostCallbackScheduled)\n if (null !== peek(taskQueue))\n (isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);\n }\n}\nvar isMessageLoopRunning = !1,\n taskTimeoutID = -1,\n frameInterval = 5,\n startTime = -1;\nfunction shouldYieldToHost() {\n return needsPaint\n ? !0\n : exports.unstable_now() - startTime < frameInterval\n ? !1\n : !0;\n}\nfunction performWorkUntilDeadline() {\n needsPaint = !1;\n if (isMessageLoopRunning) {\n var currentTime = exports.unstable_now();\n startTime = currentTime;\n var hasMoreWork = !0;\n try {\n a: {\n isHostCallbackScheduled = !1;\n isHostTimeoutScheduled &&\n ((isHostTimeoutScheduled = !1),\n localClearTimeout(taskTimeoutID),\n (taskTimeoutID = -1));\n isPerformingWork = !0;\n var previousPriorityLevel = currentPriorityLevel;\n try {\n b: {\n advanceTimers(currentTime);\n for (\n currentTask = peek(taskQueue);\n null !== currentTask &&\n !(\n currentTask.expirationTime > currentTime && shouldYieldToHost()\n );\n\n ) {\n var callback = currentTask.callback;\n if (\"function\" === typeof callback) {\n currentTask.callback = null;\n currentPriorityLevel = currentTask.priorityLevel;\n var continuationCallback = callback(\n currentTask.expirationTime <= currentTime\n );\n currentTime = exports.unstable_now();\n if (\"function\" === typeof continuationCallback) {\n currentTask.callback = continuationCallback;\n advanceTimers(currentTime);\n hasMoreWork = !0;\n break b;\n }\n currentTask === peek(taskQueue) && pop(taskQueue);\n advanceTimers(currentTime);\n } else pop(taskQueue);\n currentTask = peek(taskQueue);\n }\n if (null !== currentTask) hasMoreWork = !0;\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(\n handleTimeout,\n firstTimer.startTime - currentTime\n );\n hasMoreWork = !1;\n }\n }\n break a;\n } finally {\n (currentTask = null),\n (currentPriorityLevel = previousPriorityLevel),\n (isPerformingWork = !1);\n }\n hasMoreWork = void 0;\n }\n } finally {\n hasMoreWork\n ? schedulePerformWorkUntilDeadline()\n : (isMessageLoopRunning = !1);\n }\n }\n}\nvar schedulePerformWorkUntilDeadline;\nif (\"function\" === typeof localSetImmediate)\n schedulePerformWorkUntilDeadline = function () {\n localSetImmediate(performWorkUntilDeadline);\n };\nelse if (\"undefined\" !== typeof MessageChannel) {\n var channel = new MessageChannel(),\n port = channel.port2;\n channel.port1.onmessage = performWorkUntilDeadline;\n schedulePerformWorkUntilDeadline = function () {\n port.postMessage(null);\n };\n} else\n schedulePerformWorkUntilDeadline = function () {\n localSetTimeout(performWorkUntilDeadline, 0);\n };\nfunction requestHostTimeout(callback, ms) {\n taskTimeoutID = localSetTimeout(function () {\n callback(exports.unstable_now());\n }, ms);\n}\nexports.unstable_IdlePriority = 5;\nexports.unstable_ImmediatePriority = 1;\nexports.unstable_LowPriority = 4;\nexports.unstable_NormalPriority = 3;\nexports.unstable_Profiling = null;\nexports.unstable_UserBlockingPriority = 2;\nexports.unstable_cancelCallback = function (task) {\n task.callback = null;\n};\nexports.unstable_forceFrameRate = function (fps) {\n 0 > fps || 125 < fps\n ? console.error(\n \"forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported\"\n )\n : (frameInterval = 0 < fps ? Math.floor(1e3 / fps) : 5);\n};\nexports.unstable_getCurrentPriorityLevel = function () {\n return currentPriorityLevel;\n};\nexports.unstable_next = function (eventHandler) {\n switch (currentPriorityLevel) {\n case 1:\n case 2:\n case 3:\n var priorityLevel = 3;\n break;\n default:\n priorityLevel = currentPriorityLevel;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_requestPaint = function () {\n needsPaint = !0;\n};\nexports.unstable_runWithPriority = function (priorityLevel, eventHandler) {\n switch (priorityLevel) {\n case 1:\n case 2:\n case 3:\n case 4:\n case 5:\n break;\n default:\n priorityLevel = 3;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_scheduleCallback = function (\n priorityLevel,\n callback,\n options\n) {\n var currentTime = exports.unstable_now();\n \"object\" === typeof options && null !== options\n ? ((options = options.delay),\n (options =\n \"number\" === typeof options && 0 < options\n ? currentTime + options\n : currentTime))\n : (options = currentTime);\n switch (priorityLevel) {\n case 1:\n var timeout = -1;\n break;\n case 2:\n timeout = 250;\n break;\n case 5:\n timeout = 1073741823;\n break;\n case 4:\n timeout = 1e4;\n break;\n default:\n timeout = 5e3;\n }\n timeout = options + timeout;\n priorityLevel = {\n id: taskIdCounter++,\n callback: callback,\n priorityLevel: priorityLevel,\n startTime: options,\n expirationTime: timeout,\n sortIndex: -1\n };\n options > currentTime\n ? ((priorityLevel.sortIndex = options),\n push(timerQueue, priorityLevel),\n null === peek(taskQueue) &&\n priorityLevel === peek(timerQueue) &&\n (isHostTimeoutScheduled\n ? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))\n : (isHostTimeoutScheduled = !0),\n requestHostTimeout(handleTimeout, options - currentTime)))\n : ((priorityLevel.sortIndex = timeout),\n push(taskQueue, priorityLevel),\n isHostCallbackScheduled ||\n isPerformingWork ||\n ((isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));\n return priorityLevel;\n};\nexports.unstable_shouldYield = shouldYieldToHost;\nexports.unstable_wrapCallback = function (callback) {\n var parentPriorityLevel = currentPriorityLevel;\n return function () {\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = parentPriorityLevel;\n try {\n return callback.apply(this, arguments);\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n };\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction noop() {}\nvar Internals = {\n d: {\n f: noop,\n r: function () {\n throw Error(formatProdErrorMessage(522));\n },\n D: noop,\n C: noop,\n L: noop,\n m: noop,\n X: noop,\n S: noop,\n M: noop\n },\n p: 0,\n findDOMNode: null\n },\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\");\nfunction createPortal$1(children, containerInfo, implementation) {\n var key =\n 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;\n return {\n $$typeof: REACT_PORTAL_TYPE,\n key: null == key ? null : \"\" + key,\n children: children,\n containerInfo: containerInfo,\n implementation: implementation\n };\n}\nvar ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\nfunction getCrossOriginStringAs(as, input) {\n if (\"font\" === as) return \"\";\n if (\"string\" === typeof input)\n return \"use-credentials\" === input ? input : \"\";\n}\nexports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n Internals;\nexports.createPortal = function (children, container) {\n var key =\n 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;\n if (\n !container ||\n (1 !== container.nodeType &&\n 9 !== container.nodeType &&\n 11 !== container.nodeType)\n )\n throw Error(formatProdErrorMessage(299));\n return createPortal$1(children, container, null, key);\n};\nexports.flushSync = function (fn) {\n var previousTransition = ReactSharedInternals.T,\n previousUpdatePriority = Internals.p;\n try {\n if (((ReactSharedInternals.T = null), (Internals.p = 2), fn)) return fn();\n } finally {\n (ReactSharedInternals.T = previousTransition),\n (Internals.p = previousUpdatePriority),\n Internals.d.f();\n }\n};\nexports.preconnect = function (href, options) {\n \"string\" === typeof href &&\n (options\n ? ((options = options.crossOrigin),\n (options =\n \"string\" === typeof options\n ? \"use-credentials\" === options\n ? options\n : \"\"\n : void 0))\n : (options = null),\n Internals.d.C(href, options));\n};\nexports.prefetchDNS = function (href) {\n \"string\" === typeof href && Internals.d.D(href);\n};\nexports.preinit = function (href, options) {\n if (\"string\" === typeof href && options && \"string\" === typeof options.as) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin),\n integrity =\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n fetchPriority =\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0;\n \"style\" === as\n ? Internals.d.S(\n href,\n \"string\" === typeof options.precedence ? options.precedence : void 0,\n {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority\n }\n )\n : \"script\" === as &&\n Internals.d.X(href, {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n};\nexports.preinitModule = function (href, options) {\n if (\"string\" === typeof href)\n if (\"object\" === typeof options && null !== options) {\n if (null == options.as || \"script\" === options.as) {\n var crossOrigin = getCrossOriginStringAs(\n options.as,\n options.crossOrigin\n );\n Internals.d.M(href, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n } else null == options && Internals.d.M(href);\n};\nexports.preload = function (href, options) {\n if (\n \"string\" === typeof href &&\n \"object\" === typeof options &&\n null !== options &&\n \"string\" === typeof options.as\n ) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);\n Internals.d.L(href, as, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0,\n type: \"string\" === typeof options.type ? options.type : void 0,\n fetchPriority:\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0,\n referrerPolicy:\n \"string\" === typeof options.referrerPolicy\n ? options.referrerPolicy\n : void 0,\n imageSrcSet:\n \"string\" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,\n imageSizes:\n \"string\" === typeof options.imageSizes ? options.imageSizes : void 0,\n media: \"string\" === typeof options.media ? options.media : void 0\n });\n }\n};\nexports.preloadModule = function (href, options) {\n if (\"string\" === typeof href)\n if (options) {\n var crossOrigin = getCrossOriginStringAs(options.as, options.crossOrigin);\n Internals.d.m(href, {\n as:\n \"string\" === typeof options.as && \"script\" !== options.as\n ? options.as\n : void 0,\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0\n });\n } else Internals.d.m(href);\n};\nexports.requestFormReset = function (form) {\n Internals.d.r(form);\n};\nexports.unstable_batchedUpdates = function (fn, a) {\n return fn(a);\n};\nexports.useFormState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useFormState(action, initialState, permalink);\n};\nexports.useFormStatus = function () {\n return ReactSharedInternals.H.useHostTransitionStatus();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nfunction checkDCE() {\n /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\n if (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' ||\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function'\n ) {\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // This branch is unreachable because this function is only called\n // in production, but the condition is true only in development.\n // Therefore if the branch is still here, dead code elimination wasn't\n // properly applied.\n // Don't change the message. React DevTools relies on it. Also make sure\n // this message doesn't occur elsewhere in this function, or it will cause\n // a false positive.\n throw new Error('^_^');\n }\n try {\n // Verify that the code above has been dead code eliminated (DCE'd).\n __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);\n } catch (err) {\n // DevTools shouldn't crash React, no matter what.\n // We should still report in case we break this code.\n console.error(err);\n }\n}\n\nif (process.env.NODE_ENV === 'production') {\n // DCE check should happen before ReactDOM bundle executes so that\n // DevTools can report bad minification during injection.\n checkDCE();\n module.exports = require('./cjs/react-dom.production.js');\n} else {\n module.exports = require('./cjs/react-dom.development.js');\n}\n","/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n\"use strict\";\nvar Scheduler = require(\"scheduler\"),\n React = require(\"react\"),\n ReactDOM = require(\"react-dom\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction isValidContainer(node) {\n return !(\n !node ||\n (1 !== node.nodeType && 9 !== node.nodeType && 11 !== node.nodeType)\n );\n}\nfunction getNearestMountedFiber(fiber) {\n var node = fiber,\n nearestMounted = fiber;\n if (fiber.alternate) for (; node.return; ) node = node.return;\n else {\n fiber = node;\n do\n (node = fiber),\n 0 !== (node.flags & 4098) && (nearestMounted = node.return),\n (fiber = node.return);\n while (fiber);\n }\n return 3 === node.tag ? nearestMounted : null;\n}\nfunction getSuspenseInstanceFromFiber(fiber) {\n if (13 === fiber.tag) {\n var suspenseState = fiber.memoizedState;\n null === suspenseState &&\n ((fiber = fiber.alternate),\n null !== fiber && (suspenseState = fiber.memoizedState));\n if (null !== suspenseState) return suspenseState.dehydrated;\n }\n return null;\n}\nfunction getActivityInstanceFromFiber(fiber) {\n if (31 === fiber.tag) {\n var activityState = fiber.memoizedState;\n null === activityState &&\n ((fiber = fiber.alternate),\n null !== fiber && (activityState = fiber.memoizedState));\n if (null !== activityState) return activityState.dehydrated;\n }\n return null;\n}\nfunction assertIsMounted(fiber) {\n if (getNearestMountedFiber(fiber) !== fiber)\n throw Error(formatProdErrorMessage(188));\n}\nfunction findCurrentFiberUsingSlowPath(fiber) {\n var alternate = fiber.alternate;\n if (!alternate) {\n alternate = getNearestMountedFiber(fiber);\n if (null === alternate) throw Error(formatProdErrorMessage(188));\n return alternate !== fiber ? null : fiber;\n }\n for (var a = fiber, b = alternate; ; ) {\n var parentA = a.return;\n if (null === parentA) break;\n var parentB = parentA.alternate;\n if (null === parentB) {\n b = parentA.return;\n if (null !== b) {\n a = b;\n continue;\n }\n break;\n }\n if (parentA.child === parentB.child) {\n for (parentB = parentA.child; parentB; ) {\n if (parentB === a) return assertIsMounted(parentA), fiber;\n if (parentB === b) return assertIsMounted(parentA), alternate;\n parentB = parentB.sibling;\n }\n throw Error(formatProdErrorMessage(188));\n }\n if (a.return !== b.return) (a = parentA), (b = parentB);\n else {\n for (var didFindChild = !1, child$0 = parentA.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentA;\n b = parentB;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentA;\n a = parentB;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) {\n for (child$0 = parentB.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentB;\n b = parentA;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentB;\n a = parentA;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) throw Error(formatProdErrorMessage(189));\n }\n }\n if (a.alternate !== b) throw Error(formatProdErrorMessage(190));\n }\n if (3 !== a.tag) throw Error(formatProdErrorMessage(188));\n return a.stateNode.current === a ? fiber : alternate;\n}\nfunction findCurrentHostFiberImpl(node) {\n var tag = node.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return node;\n for (node = node.child; null !== node; ) {\n tag = findCurrentHostFiberImpl(node);\n if (null !== tag) return tag;\n node = node.sibling;\n }\n return null;\n}\nvar assign = Object.assign,\n REACT_LEGACY_ELEMENT_TYPE = Symbol.for(\"react.element\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\nSymbol.for(\"react.scope\");\nvar REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\");\nSymbol.for(\"react.legacy_hidden\");\nSymbol.for(\"react.tracing_marker\");\nvar REACT_MEMO_CACHE_SENTINEL = Symbol.for(\"react.memo_cache_sentinel\");\nSymbol.for(\"react.view_transition\");\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\");\nfunction getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (type.$$typeof) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n}\nvar isArrayImpl = Array.isArray,\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n ReactDOMSharedInternals =\n ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n sharedNotPendingObject = {\n pending: !1,\n data: null,\n method: null,\n action: null\n },\n valueStack = [],\n index = -1;\nfunction createCursor(defaultValue) {\n return { current: defaultValue };\n}\nfunction pop(cursor) {\n 0 > index ||\n ((cursor.current = valueStack[index]), (valueStack[index] = null), index--);\n}\nfunction push(cursor, value) {\n index++;\n valueStack[index] = cursor.current;\n cursor.current = value;\n}\nvar contextStackCursor = createCursor(null),\n contextFiberStackCursor = createCursor(null),\n rootInstanceStackCursor = createCursor(null),\n hostTransitionProviderCursor = createCursor(null);\nfunction pushHostContainer(fiber, nextRootInstance) {\n push(rootInstanceStackCursor, nextRootInstance);\n push(contextFiberStackCursor, fiber);\n push(contextStackCursor, null);\n switch (nextRootInstance.nodeType) {\n case 9:\n case 11:\n fiber = (fiber = nextRootInstance.documentElement)\n ? (fiber = fiber.namespaceURI)\n ? getOwnHostContext(fiber)\n : 0\n : 0;\n break;\n default:\n if (\n ((fiber = nextRootInstance.tagName),\n (nextRootInstance = nextRootInstance.namespaceURI))\n )\n (nextRootInstance = getOwnHostContext(nextRootInstance)),\n (fiber = getChildHostContextProd(nextRootInstance, fiber));\n else\n switch (fiber) {\n case \"svg\":\n fiber = 1;\n break;\n case \"math\":\n fiber = 2;\n break;\n default:\n fiber = 0;\n }\n }\n pop(contextStackCursor);\n push(contextStackCursor, fiber);\n}\nfunction popHostContainer() {\n pop(contextStackCursor);\n pop(contextFiberStackCursor);\n pop(rootInstanceStackCursor);\n}\nfunction pushHostContext(fiber) {\n null !== fiber.memoizedState && push(hostTransitionProviderCursor, fiber);\n var context = contextStackCursor.current;\n var JSCompiler_inline_result = getChildHostContextProd(context, fiber.type);\n context !== JSCompiler_inline_result &&\n (push(contextFiberStackCursor, fiber),\n push(contextStackCursor, JSCompiler_inline_result));\n}\nfunction popHostContext(fiber) {\n contextFiberStackCursor.current === fiber &&\n (pop(contextStackCursor), pop(contextFiberStackCursor));\n hostTransitionProviderCursor.current === fiber &&\n (pop(hostTransitionProviderCursor),\n (HostTransitionContext._currentValue = sharedNotPendingObject));\n}\nvar prefix, suffix;\nfunction describeBuiltInComponentFrame(name) {\n if (void 0 === prefix)\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = (match && match[1]) || \"\";\n suffix =\n -1 < x.stack.indexOf(\"\\n at\")\n ? \" ()\"\n : -1 < x.stack.indexOf(\"@\")\n ? \"@unknown:0:0\"\n : \"\";\n }\n return \"\\n\" + prefix + name + suffix;\n}\nvar reentry = !1;\nfunction describeNativeComponentFrame(fn, construct) {\n if (!fn || reentry) return \"\";\n reentry = !0;\n var previousPrepareStackTrace = Error.prepareStackTrace;\n Error.prepareStackTrace = void 0;\n try {\n var RunInRootFrame = {\n DetermineComponentFrameRoot: function () {\n try {\n if (construct) {\n var Fake = function () {\n throw Error();\n };\n Object.defineProperty(Fake.prototype, \"props\", {\n set: function () {\n throw Error();\n }\n });\n if (\"object\" === typeof Reflect && Reflect.construct) {\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n var control = x;\n }\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x$1) {\n control = x$1;\n }\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x$2) {\n control = x$2;\n }\n (Fake = fn()) &&\n \"function\" === typeof Fake.catch &&\n Fake.catch(function () {});\n }\n } catch (sample) {\n if (sample && control && \"string\" === typeof sample.stack)\n return [sample.stack, control.stack];\n }\n return [null, null];\n }\n };\n RunInRootFrame.DetermineComponentFrameRoot.displayName =\n \"DetermineComponentFrameRoot\";\n var namePropDescriptor = Object.getOwnPropertyDescriptor(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\"\n );\n namePropDescriptor &&\n namePropDescriptor.configurable &&\n Object.defineProperty(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\",\n { value: \"DetermineComponentFrameRoot\" }\n );\n var _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(),\n sampleStack = _RunInRootFrame$Deter[0],\n controlStack = _RunInRootFrame$Deter[1];\n if (sampleStack && controlStack) {\n var sampleLines = sampleStack.split(\"\\n\"),\n controlLines = controlStack.split(\"\\n\");\n for (\n namePropDescriptor = RunInRootFrame = 0;\n RunInRootFrame < sampleLines.length &&\n !sampleLines[RunInRootFrame].includes(\"DetermineComponentFrameRoot\");\n\n )\n RunInRootFrame++;\n for (\n ;\n namePropDescriptor < controlLines.length &&\n !controlLines[namePropDescriptor].includes(\n \"DetermineComponentFrameRoot\"\n );\n\n )\n namePropDescriptor++;\n if (\n RunInRootFrame === sampleLines.length ||\n namePropDescriptor === controlLines.length\n )\n for (\n RunInRootFrame = sampleLines.length - 1,\n namePropDescriptor = controlLines.length - 1;\n 1 <= RunInRootFrame &&\n 0 <= namePropDescriptor &&\n sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor];\n\n )\n namePropDescriptor--;\n for (\n ;\n 1 <= RunInRootFrame && 0 <= namePropDescriptor;\n RunInRootFrame--, namePropDescriptor--\n )\n if (sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {\n if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {\n do\n if (\n (RunInRootFrame--,\n namePropDescriptor--,\n 0 > namePropDescriptor ||\n sampleLines[RunInRootFrame] !==\n controlLines[namePropDescriptor])\n ) {\n var frame =\n \"\\n\" +\n sampleLines[RunInRootFrame].replace(\" at new \", \" at \");\n fn.displayName &&\n frame.includes(\"\") &&\n (frame = frame.replace(\"\", fn.displayName));\n return frame;\n }\n while (1 <= RunInRootFrame && 0 <= namePropDescriptor);\n }\n break;\n }\n }\n } finally {\n (reentry = !1), (Error.prepareStackTrace = previousPrepareStackTrace);\n }\n return (previousPrepareStackTrace = fn ? fn.displayName || fn.name : \"\")\n ? describeBuiltInComponentFrame(previousPrepareStackTrace)\n : \"\";\n}\nfunction describeFiber(fiber, childFiber) {\n switch (fiber.tag) {\n case 26:\n case 27:\n case 5:\n return describeBuiltInComponentFrame(fiber.type);\n case 16:\n return describeBuiltInComponentFrame(\"Lazy\");\n case 13:\n return fiber.child !== childFiber && null !== childFiber\n ? describeBuiltInComponentFrame(\"Suspense Fallback\")\n : describeBuiltInComponentFrame(\"Suspense\");\n case 19:\n return describeBuiltInComponentFrame(\"SuspenseList\");\n case 0:\n case 15:\n return describeNativeComponentFrame(fiber.type, !1);\n case 11:\n return describeNativeComponentFrame(fiber.type.render, !1);\n case 1:\n return describeNativeComponentFrame(fiber.type, !0);\n case 31:\n return describeBuiltInComponentFrame(\"Activity\");\n default:\n return \"\";\n }\n}\nfunction getStackByFiberInDevAndProd(workInProgress) {\n try {\n var info = \"\",\n previous = null;\n do\n (info += describeFiber(workInProgress, previous)),\n (previous = workInProgress),\n (workInProgress = workInProgress.return);\n while (workInProgress);\n return info;\n } catch (x) {\n return \"\\nError generating stack: \" + x.message + \"\\n\" + x.stack;\n }\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty,\n scheduleCallback$3 = Scheduler.unstable_scheduleCallback,\n cancelCallback$1 = Scheduler.unstable_cancelCallback,\n shouldYield = Scheduler.unstable_shouldYield,\n requestPaint = Scheduler.unstable_requestPaint,\n now = Scheduler.unstable_now,\n getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel,\n ImmediatePriority = Scheduler.unstable_ImmediatePriority,\n UserBlockingPriority = Scheduler.unstable_UserBlockingPriority,\n NormalPriority$1 = Scheduler.unstable_NormalPriority,\n LowPriority = Scheduler.unstable_LowPriority,\n IdlePriority = Scheduler.unstable_IdlePriority,\n log$1 = Scheduler.log,\n unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue,\n rendererID = null,\n injectedHook = null;\nfunction setIsStrictModeForDevtools(newIsStrictMode) {\n \"function\" === typeof log$1 && unstable_setDisableYieldValue(newIsStrictMode);\n if (injectedHook && \"function\" === typeof injectedHook.setStrictMode)\n try {\n injectedHook.setStrictMode(rendererID, newIsStrictMode);\n } catch (err) {}\n}\nvar clz32 = Math.clz32 ? Math.clz32 : clz32Fallback,\n log = Math.log,\n LN2 = Math.LN2;\nfunction clz32Fallback(x) {\n x >>>= 0;\n return 0 === x ? 32 : (31 - ((log(x) / LN2) | 0)) | 0;\n}\nvar nextTransitionUpdateLane = 256,\n nextTransitionDeferredLane = 262144,\n nextRetryLane = 4194304;\nfunction getHighestPriorityLanes(lanes) {\n var pendingSyncLanes = lanes & 42;\n if (0 !== pendingSyncLanes) return pendingSyncLanes;\n switch (lanes & -lanes) {\n case 1:\n return 1;\n case 2:\n return 2;\n case 4:\n return 4;\n case 8:\n return 8;\n case 16:\n return 16;\n case 32:\n return 32;\n case 64:\n return 64;\n case 128:\n return 128;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n return lanes & 261888;\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return lanes & 3932160;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return lanes & 62914560;\n case 67108864:\n return 67108864;\n case 134217728:\n return 134217728;\n case 268435456:\n return 268435456;\n case 536870912:\n return 536870912;\n case 1073741824:\n return 0;\n default:\n return lanes;\n }\n}\nfunction getNextLanes(root, wipLanes, rootHasPendingCommit) {\n var pendingLanes = root.pendingLanes;\n if (0 === pendingLanes) return 0;\n var nextLanes = 0,\n suspendedLanes = root.suspendedLanes,\n pingedLanes = root.pingedLanes;\n root = root.warmLanes;\n var nonIdlePendingLanes = pendingLanes & 134217727;\n 0 !== nonIdlePendingLanes\n ? ((pendingLanes = nonIdlePendingLanes & ~suspendedLanes),\n 0 !== pendingLanes\n ? (nextLanes = getHighestPriorityLanes(pendingLanes))\n : ((pingedLanes &= nonIdlePendingLanes),\n 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = nonIdlePendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit)))))\n : ((nonIdlePendingLanes = pendingLanes & ~suspendedLanes),\n 0 !== nonIdlePendingLanes\n ? (nextLanes = getHighestPriorityLanes(nonIdlePendingLanes))\n : 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = pendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit))));\n return 0 === nextLanes\n ? 0\n : 0 !== wipLanes &&\n wipLanes !== nextLanes &&\n 0 === (wipLanes & suspendedLanes) &&\n ((suspendedLanes = nextLanes & -nextLanes),\n (rootHasPendingCommit = wipLanes & -wipLanes),\n suspendedLanes >= rootHasPendingCommit ||\n (32 === suspendedLanes && 0 !== (rootHasPendingCommit & 4194048)))\n ? wipLanes\n : nextLanes;\n}\nfunction checkIfRootIsPrerendering(root, renderLanes) {\n return (\n 0 ===\n (root.pendingLanes &\n ~(root.suspendedLanes & ~root.pingedLanes) &\n renderLanes)\n );\n}\nfunction computeExpirationTime(lane, currentTime) {\n switch (lane) {\n case 1:\n case 2:\n case 4:\n case 8:\n case 64:\n return currentTime + 250;\n case 16:\n case 32:\n case 128:\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return currentTime + 5e3;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return -1;\n case 67108864:\n case 134217728:\n case 268435456:\n case 536870912:\n case 1073741824:\n return -1;\n default:\n return -1;\n }\n}\nfunction claimNextRetryLane() {\n var lane = nextRetryLane;\n nextRetryLane <<= 1;\n 0 === (nextRetryLane & 62914560) && (nextRetryLane = 4194304);\n return lane;\n}\nfunction createLaneMap(initial) {\n for (var laneMap = [], i = 0; 31 > i; i++) laneMap.push(initial);\n return laneMap;\n}\nfunction markRootUpdated$1(root, updateLane) {\n root.pendingLanes |= updateLane;\n 268435456 !== updateLane &&\n ((root.suspendedLanes = 0), (root.pingedLanes = 0), (root.warmLanes = 0));\n}\nfunction markRootFinished(\n root,\n finishedLanes,\n remainingLanes,\n spawnedLane,\n updatedLanes,\n suspendedRetryLanes\n) {\n var previouslyPendingLanes = root.pendingLanes;\n root.pendingLanes = remainingLanes;\n root.suspendedLanes = 0;\n root.pingedLanes = 0;\n root.warmLanes = 0;\n root.expiredLanes &= remainingLanes;\n root.entangledLanes &= remainingLanes;\n root.errorRecoveryDisabledLanes &= remainingLanes;\n root.shellSuspendCounter = 0;\n var entanglements = root.entanglements,\n expirationTimes = root.expirationTimes,\n hiddenUpdates = root.hiddenUpdates;\n for (\n remainingLanes = previouslyPendingLanes & ~remainingLanes;\n 0 < remainingLanes;\n\n ) {\n var index$7 = 31 - clz32(remainingLanes),\n lane = 1 << index$7;\n entanglements[index$7] = 0;\n expirationTimes[index$7] = -1;\n var hiddenUpdatesForLane = hiddenUpdates[index$7];\n if (null !== hiddenUpdatesForLane)\n for (\n hiddenUpdates[index$7] = null, index$7 = 0;\n index$7 < hiddenUpdatesForLane.length;\n index$7++\n ) {\n var update = hiddenUpdatesForLane[index$7];\n null !== update && (update.lane &= -536870913);\n }\n remainingLanes &= ~lane;\n }\n 0 !== spawnedLane && markSpawnedDeferredLane(root, spawnedLane, 0);\n 0 !== suspendedRetryLanes &&\n 0 === updatedLanes &&\n 0 !== root.tag &&\n (root.suspendedLanes |=\n suspendedRetryLanes & ~(previouslyPendingLanes & ~finishedLanes));\n}\nfunction markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {\n root.pendingLanes |= spawnedLane;\n root.suspendedLanes &= ~spawnedLane;\n var spawnedLaneIndex = 31 - clz32(spawnedLane);\n root.entangledLanes |= spawnedLane;\n root.entanglements[spawnedLaneIndex] =\n root.entanglements[spawnedLaneIndex] |\n 1073741824 |\n (entangledLanes & 261930);\n}\nfunction markRootEntangled(root, entangledLanes) {\n var rootEntangledLanes = (root.entangledLanes |= entangledLanes);\n for (root = root.entanglements; rootEntangledLanes; ) {\n var index$8 = 31 - clz32(rootEntangledLanes),\n lane = 1 << index$8;\n (lane & entangledLanes) | (root[index$8] & entangledLanes) &&\n (root[index$8] |= entangledLanes);\n rootEntangledLanes &= ~lane;\n }\n}\nfunction getBumpedLaneForHydration(root, renderLanes) {\n var renderLane = renderLanes & -renderLanes;\n renderLane =\n 0 !== (renderLane & 42) ? 1 : getBumpedLaneForHydrationByLane(renderLane);\n return 0 !== (renderLane & (root.suspendedLanes | renderLanes))\n ? 0\n : renderLane;\n}\nfunction getBumpedLaneForHydrationByLane(lane) {\n switch (lane) {\n case 2:\n lane = 1;\n break;\n case 8:\n lane = 4;\n break;\n case 32:\n lane = 16;\n break;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n lane = 128;\n break;\n case 268435456:\n lane = 134217728;\n break;\n default:\n lane = 0;\n }\n return lane;\n}\nfunction lanesToEventPriority(lanes) {\n lanes &= -lanes;\n return 2 < lanes\n ? 8 < lanes\n ? 0 !== (lanes & 134217727)\n ? 32\n : 268435456\n : 8\n : 2;\n}\nfunction resolveUpdatePriority() {\n var updatePriority = ReactDOMSharedInternals.p;\n if (0 !== updatePriority) return updatePriority;\n updatePriority = window.event;\n return void 0 === updatePriority ? 32 : getEventPriority(updatePriority.type);\n}\nfunction runWithPriority(priority, fn) {\n var previousPriority = ReactDOMSharedInternals.p;\n try {\n return (ReactDOMSharedInternals.p = priority), fn();\n } finally {\n ReactDOMSharedInternals.p = previousPriority;\n }\n}\nvar randomKey = Math.random().toString(36).slice(2),\n internalInstanceKey = \"__reactFiber$\" + randomKey,\n internalPropsKey = \"__reactProps$\" + randomKey,\n internalContainerInstanceKey = \"__reactContainer$\" + randomKey,\n internalEventHandlersKey = \"__reactEvents$\" + randomKey,\n internalEventHandlerListenersKey = \"__reactListeners$\" + randomKey,\n internalEventHandlesSetKey = \"__reactHandles$\" + randomKey,\n internalRootNodeResourcesKey = \"__reactResources$\" + randomKey,\n internalHoistableMarker = \"__reactMarker$\" + randomKey;\nfunction detachDeletedInstance(node) {\n delete node[internalInstanceKey];\n delete node[internalPropsKey];\n delete node[internalEventHandlersKey];\n delete node[internalEventHandlerListenersKey];\n delete node[internalEventHandlesSetKey];\n}\nfunction getClosestInstanceFromNode(targetNode) {\n var targetInst = targetNode[internalInstanceKey];\n if (targetInst) return targetInst;\n for (var parentNode = targetNode.parentNode; parentNode; ) {\n if (\n (targetInst =\n parentNode[internalContainerInstanceKey] ||\n parentNode[internalInstanceKey])\n ) {\n parentNode = targetInst.alternate;\n if (\n null !== targetInst.child ||\n (null !== parentNode && null !== parentNode.child)\n )\n for (\n targetNode = getParentHydrationBoundary(targetNode);\n null !== targetNode;\n\n ) {\n if ((parentNode = targetNode[internalInstanceKey])) return parentNode;\n targetNode = getParentHydrationBoundary(targetNode);\n }\n return targetInst;\n }\n targetNode = parentNode;\n parentNode = targetNode.parentNode;\n }\n return null;\n}\nfunction getInstanceFromNode(node) {\n if (\n (node = node[internalInstanceKey] || node[internalContainerInstanceKey])\n ) {\n var tag = node.tag;\n if (\n 5 === tag ||\n 6 === tag ||\n 13 === tag ||\n 31 === tag ||\n 26 === tag ||\n 27 === tag ||\n 3 === tag\n )\n return node;\n }\n return null;\n}\nfunction getNodeFromInstance(inst) {\n var tag = inst.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return inst.stateNode;\n throw Error(formatProdErrorMessage(33));\n}\nfunction getResourcesFromRoot(root) {\n var resources = root[internalRootNodeResourcesKey];\n resources ||\n (resources = root[internalRootNodeResourcesKey] =\n { hoistableStyles: new Map(), hoistableScripts: new Map() });\n return resources;\n}\nfunction markNodeAsHoistable(node) {\n node[internalHoistableMarker] = !0;\n}\nvar allNativeEvents = new Set(),\n registrationNameDependencies = {};\nfunction registerTwoPhaseEvent(registrationName, dependencies) {\n registerDirectEvent(registrationName, dependencies);\n registerDirectEvent(registrationName + \"Capture\", dependencies);\n}\nfunction registerDirectEvent(registrationName, dependencies) {\n registrationNameDependencies[registrationName] = dependencies;\n for (\n registrationName = 0;\n registrationName < dependencies.length;\n registrationName++\n )\n allNativeEvents.add(dependencies[registrationName]);\n}\nvar VALID_ATTRIBUTE_NAME_REGEX = RegExp(\n \"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"\n ),\n illegalAttributeNameCache = {},\n validatedAttributeNameCache = {};\nfunction isAttributeNameSafe(attributeName) {\n if (hasOwnProperty.call(validatedAttributeNameCache, attributeName))\n return !0;\n if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) return !1;\n if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName))\n return (validatedAttributeNameCache[attributeName] = !0);\n illegalAttributeNameCache[attributeName] = !0;\n return !1;\n}\nfunction setValueForAttribute(node, name, value) {\n if (isAttributeNameSafe(name))\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n node.removeAttribute(name);\n return;\n case \"boolean\":\n var prefix$10 = name.toLowerCase().slice(0, 5);\n if (\"data-\" !== prefix$10 && \"aria-\" !== prefix$10) {\n node.removeAttribute(name);\n return;\n }\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForKnownAttribute(node, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForNamespacedAttribute(node, namespace, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttributeNS(namespace, name, \"\" + value);\n }\n}\nfunction getToStringValue(value) {\n switch (typeof value) {\n case \"bigint\":\n case \"boolean\":\n case \"number\":\n case \"string\":\n case \"undefined\":\n return value;\n case \"object\":\n return value;\n default:\n return \"\";\n }\n}\nfunction isCheckable(elem) {\n var type = elem.type;\n return (\n (elem = elem.nodeName) &&\n \"input\" === elem.toLowerCase() &&\n (\"checkbox\" === type || \"radio\" === type)\n );\n}\nfunction trackValueOnNode(node, valueField, currentValue) {\n var descriptor = Object.getOwnPropertyDescriptor(\n node.constructor.prototype,\n valueField\n );\n if (\n !node.hasOwnProperty(valueField) &&\n \"undefined\" !== typeof descriptor &&\n \"function\" === typeof descriptor.get &&\n \"function\" === typeof descriptor.set\n ) {\n var get = descriptor.get,\n set = descriptor.set;\n Object.defineProperty(node, valueField, {\n configurable: !0,\n get: function () {\n return get.call(this);\n },\n set: function (value) {\n currentValue = \"\" + value;\n set.call(this, value);\n }\n });\n Object.defineProperty(node, valueField, {\n enumerable: descriptor.enumerable\n });\n return {\n getValue: function () {\n return currentValue;\n },\n setValue: function (value) {\n currentValue = \"\" + value;\n },\n stopTracking: function () {\n node._valueTracker = null;\n delete node[valueField];\n }\n };\n }\n}\nfunction track(node) {\n if (!node._valueTracker) {\n var valueField = isCheckable(node) ? \"checked\" : \"value\";\n node._valueTracker = trackValueOnNode(\n node,\n valueField,\n \"\" + node[valueField]\n );\n }\n}\nfunction updateValueIfChanged(node) {\n if (!node) return !1;\n var tracker = node._valueTracker;\n if (!tracker) return !0;\n var lastValue = tracker.getValue();\n var value = \"\";\n node &&\n (value = isCheckable(node)\n ? node.checked\n ? \"true\"\n : \"false\"\n : node.value);\n node = value;\n return node !== lastValue ? (tracker.setValue(node), !0) : !1;\n}\nfunction getActiveElement(doc) {\n doc = doc || (\"undefined\" !== typeof document ? document : void 0);\n if (\"undefined\" === typeof doc) return null;\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\nvar escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\\n\"\\\\]/g;\nfunction escapeSelectorAttributeValueInsideDoubleQuotes(value) {\n return value.replace(\n escapeSelectorAttributeValueInsideDoubleQuotesRegex,\n function (ch) {\n return \"\\\\\" + ch.charCodeAt(0).toString(16) + \" \";\n }\n );\n}\nfunction updateInput(\n element,\n value,\n defaultValue,\n lastDefaultValue,\n checked,\n defaultChecked,\n type,\n name\n) {\n element.name = \"\";\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type\n ? (element.type = type)\n : element.removeAttribute(\"type\");\n if (null != value)\n if (\"number\" === type) {\n if ((0 === value && \"\" === element.value) || element.value != value)\n element.value = \"\" + getToStringValue(value);\n } else\n element.value !== \"\" + getToStringValue(value) &&\n (element.value = \"\" + getToStringValue(value));\n else\n (\"submit\" !== type && \"reset\" !== type) || element.removeAttribute(\"value\");\n null != value\n ? setDefaultValue(element, type, getToStringValue(value))\n : null != defaultValue\n ? setDefaultValue(element, type, getToStringValue(defaultValue))\n : null != lastDefaultValue && element.removeAttribute(\"value\");\n null == checked &&\n null != defaultChecked &&\n (element.defaultChecked = !!defaultChecked);\n null != checked &&\n (element.checked =\n checked && \"function\" !== typeof checked && \"symbol\" !== typeof checked);\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name\n ? (element.name = \"\" + getToStringValue(name))\n : element.removeAttribute(\"name\");\n}\nfunction initInput(\n element,\n value,\n defaultValue,\n checked,\n defaultChecked,\n type,\n name,\n isHydrating\n) {\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type &&\n (element.type = type);\n if (null != value || null != defaultValue) {\n if (\n !(\n (\"submit\" !== type && \"reset\" !== type) ||\n (void 0 !== value && null !== value)\n )\n ) {\n track(element);\n return;\n }\n defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n value = null != value ? \"\" + getToStringValue(value) : defaultValue;\n isHydrating || value === element.value || (element.value = value);\n element.defaultValue = value;\n }\n checked = null != checked ? checked : defaultChecked;\n checked =\n \"function\" !== typeof checked && \"symbol\" !== typeof checked && !!checked;\n element.checked = isHydrating ? element.checked : !!checked;\n element.defaultChecked = !!checked;\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name &&\n (element.name = name);\n track(element);\n}\nfunction setDefaultValue(node, type, value) {\n (\"number\" === type && getActiveElement(node.ownerDocument) === node) ||\n node.defaultValue === \"\" + value ||\n (node.defaultValue = \"\" + value);\n}\nfunction updateOptions(node, multiple, propValue, setDefaultSelected) {\n node = node.options;\n if (multiple) {\n multiple = {};\n for (var i = 0; i < propValue.length; i++)\n multiple[\"$\" + propValue[i]] = !0;\n for (propValue = 0; propValue < node.length; propValue++)\n (i = multiple.hasOwnProperty(\"$\" + node[propValue].value)),\n node[propValue].selected !== i && (node[propValue].selected = i),\n i && setDefaultSelected && (node[propValue].defaultSelected = !0);\n } else {\n propValue = \"\" + getToStringValue(propValue);\n multiple = null;\n for (i = 0; i < node.length; i++) {\n if (node[i].value === propValue) {\n node[i].selected = !0;\n setDefaultSelected && (node[i].defaultSelected = !0);\n return;\n }\n null !== multiple || node[i].disabled || (multiple = node[i]);\n }\n null !== multiple && (multiple.selected = !0);\n }\n}\nfunction updateTextarea(element, value, defaultValue) {\n if (\n null != value &&\n ((value = \"\" + getToStringValue(value)),\n value !== element.value && (element.value = value),\n null == defaultValue)\n ) {\n element.defaultValue !== value && (element.defaultValue = value);\n return;\n }\n element.defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n}\nfunction initTextarea(element, value, defaultValue, children) {\n if (null == value) {\n if (null != children) {\n if (null != defaultValue) throw Error(formatProdErrorMessage(92));\n if (isArrayImpl(children)) {\n if (1 < children.length) throw Error(formatProdErrorMessage(93));\n children = children[0];\n }\n defaultValue = children;\n }\n null == defaultValue && (defaultValue = \"\");\n value = defaultValue;\n }\n defaultValue = getToStringValue(value);\n element.defaultValue = defaultValue;\n children = element.textContent;\n children === defaultValue &&\n \"\" !== children &&\n null !== children &&\n (element.value = children);\n track(element);\n}\nfunction setTextContent(node, text) {\n if (text) {\n var firstChild = node.firstChild;\n if (\n firstChild &&\n firstChild === node.lastChild &&\n 3 === firstChild.nodeType\n ) {\n firstChild.nodeValue = text;\n return;\n }\n }\n node.textContent = text;\n}\nvar unitlessNumbers = new Set(\n \"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\n \" \"\n )\n);\nfunction setValueForStyle(style, styleName, value) {\n var isCustomProperty = 0 === styleName.indexOf(\"--\");\n null == value || \"boolean\" === typeof value || \"\" === value\n ? isCustomProperty\n ? style.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (style.cssFloat = \"\")\n : (style[styleName] = \"\")\n : isCustomProperty\n ? style.setProperty(styleName, value)\n : \"number\" !== typeof value ||\n 0 === value ||\n unitlessNumbers.has(styleName)\n ? \"float\" === styleName\n ? (style.cssFloat = value)\n : (style[styleName] = (\"\" + value).trim())\n : (style[styleName] = value + \"px\");\n}\nfunction setValueForStyles(node, styles, prevStyles) {\n if (null != styles && \"object\" !== typeof styles)\n throw Error(formatProdErrorMessage(62));\n node = node.style;\n if (null != prevStyles) {\n for (var styleName in prevStyles)\n !prevStyles.hasOwnProperty(styleName) ||\n (null != styles && styles.hasOwnProperty(styleName)) ||\n (0 === styleName.indexOf(\"--\")\n ? node.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (node.cssFloat = \"\")\n : (node[styleName] = \"\"));\n for (var styleName$16 in styles)\n (styleName = styles[styleName$16]),\n styles.hasOwnProperty(styleName$16) &&\n prevStyles[styleName$16] !== styleName &&\n setValueForStyle(node, styleName$16, styleName);\n } else\n for (var styleName$17 in styles)\n styles.hasOwnProperty(styleName$17) &&\n setValueForStyle(node, styleName$17, styles[styleName$17]);\n}\nfunction isCustomElement(tagName) {\n if (-1 === tagName.indexOf(\"-\")) return !1;\n switch (tagName) {\n case \"annotation-xml\":\n case \"color-profile\":\n case \"font-face\":\n case \"font-face-src\":\n case \"font-face-uri\":\n case \"font-face-format\":\n case \"font-face-name\":\n case \"missing-glyph\":\n return !1;\n default:\n return !0;\n }\n}\nvar aliases = new Map([\n [\"acceptCharset\", \"accept-charset\"],\n [\"htmlFor\", \"for\"],\n [\"httpEquiv\", \"http-equiv\"],\n [\"crossOrigin\", \"crossorigin\"],\n [\"accentHeight\", \"accent-height\"],\n [\"alignmentBaseline\", \"alignment-baseline\"],\n [\"arabicForm\", \"arabic-form\"],\n [\"baselineShift\", \"baseline-shift\"],\n [\"capHeight\", \"cap-height\"],\n [\"clipPath\", \"clip-path\"],\n [\"clipRule\", \"clip-rule\"],\n [\"colorInterpolation\", \"color-interpolation\"],\n [\"colorInterpolationFilters\", \"color-interpolation-filters\"],\n [\"colorProfile\", \"color-profile\"],\n [\"colorRendering\", \"color-rendering\"],\n [\"dominantBaseline\", \"dominant-baseline\"],\n [\"enableBackground\", \"enable-background\"],\n [\"fillOpacity\", \"fill-opacity\"],\n [\"fillRule\", \"fill-rule\"],\n [\"floodColor\", \"flood-color\"],\n [\"floodOpacity\", \"flood-opacity\"],\n [\"fontFamily\", \"font-family\"],\n [\"fontSize\", \"font-size\"],\n [\"fontSizeAdjust\", \"font-size-adjust\"],\n [\"fontStretch\", \"font-stretch\"],\n [\"fontStyle\", \"font-style\"],\n [\"fontVariant\", \"font-variant\"],\n [\"fontWeight\", \"font-weight\"],\n [\"glyphName\", \"glyph-name\"],\n [\"glyphOrientationHorizontal\", \"glyph-orientation-horizontal\"],\n [\"glyphOrientationVertical\", \"glyph-orientation-vertical\"],\n [\"horizAdvX\", \"horiz-adv-x\"],\n [\"horizOriginX\", \"horiz-origin-x\"],\n [\"imageRendering\", \"image-rendering\"],\n [\"letterSpacing\", \"letter-spacing\"],\n [\"lightingColor\", \"lighting-color\"],\n [\"markerEnd\", \"marker-end\"],\n [\"markerMid\", \"marker-mid\"],\n [\"markerStart\", \"marker-start\"],\n [\"overlinePosition\", \"overline-position\"],\n [\"overlineThickness\", \"overline-thickness\"],\n [\"paintOrder\", \"paint-order\"],\n [\"panose-1\", \"panose-1\"],\n [\"pointerEvents\", \"pointer-events\"],\n [\"renderingIntent\", \"rendering-intent\"],\n [\"shapeRendering\", \"shape-rendering\"],\n [\"stopColor\", \"stop-color\"],\n [\"stopOpacity\", \"stop-opacity\"],\n [\"strikethroughPosition\", \"strikethrough-position\"],\n [\"strikethroughThickness\", \"strikethrough-thickness\"],\n [\"strokeDasharray\", \"stroke-dasharray\"],\n [\"strokeDashoffset\", \"stroke-dashoffset\"],\n [\"strokeLinecap\", \"stroke-linecap\"],\n [\"strokeLinejoin\", \"stroke-linejoin\"],\n [\"strokeMiterlimit\", \"stroke-miterlimit\"],\n [\"strokeOpacity\", \"stroke-opacity\"],\n [\"strokeWidth\", \"stroke-width\"],\n [\"textAnchor\", \"text-anchor\"],\n [\"textDecoration\", \"text-decoration\"],\n [\"textRendering\", \"text-rendering\"],\n [\"transformOrigin\", \"transform-origin\"],\n [\"underlinePosition\", \"underline-position\"],\n [\"underlineThickness\", \"underline-thickness\"],\n [\"unicodeBidi\", \"unicode-bidi\"],\n [\"unicodeRange\", \"unicode-range\"],\n [\"unitsPerEm\", \"units-per-em\"],\n [\"vAlphabetic\", \"v-alphabetic\"],\n [\"vHanging\", \"v-hanging\"],\n [\"vIdeographic\", \"v-ideographic\"],\n [\"vMathematical\", \"v-mathematical\"],\n [\"vectorEffect\", \"vector-effect\"],\n [\"vertAdvY\", \"vert-adv-y\"],\n [\"vertOriginX\", \"vert-origin-x\"],\n [\"vertOriginY\", \"vert-origin-y\"],\n [\"wordSpacing\", \"word-spacing\"],\n [\"writingMode\", \"writing-mode\"],\n [\"xmlnsXlink\", \"xmlns:xlink\"],\n [\"xHeight\", \"x-height\"]\n ]),\n isJavaScriptProtocol =\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;\nfunction sanitizeURL(url) {\n return isJavaScriptProtocol.test(\"\" + url)\n ? \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\"\n : url;\n}\nfunction noop$1() {}\nvar currentReplayingEvent = null;\nfunction getEventTarget(nativeEvent) {\n nativeEvent = nativeEvent.target || nativeEvent.srcElement || window;\n nativeEvent.correspondingUseElement &&\n (nativeEvent = nativeEvent.correspondingUseElement);\n return 3 === nativeEvent.nodeType ? nativeEvent.parentNode : nativeEvent;\n}\nvar restoreTarget = null,\n restoreQueue = null;\nfunction restoreStateOfTarget(target) {\n var internalInstance = getInstanceFromNode(target);\n if (internalInstance && (target = internalInstance.stateNode)) {\n var props = target[internalPropsKey] || null;\n a: switch (((target = internalInstance.stateNode), internalInstance.type)) {\n case \"input\":\n updateInput(\n target,\n props.value,\n props.defaultValue,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name\n );\n internalInstance = props.name;\n if (\"radio\" === props.type && null != internalInstance) {\n for (props = target; props.parentNode; ) props = props.parentNode;\n props = props.querySelectorAll(\n 'input[name=\"' +\n escapeSelectorAttributeValueInsideDoubleQuotes(\n \"\" + internalInstance\n ) +\n '\"][type=\"radio\"]'\n );\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n ) {\n var otherNode = props[internalInstance];\n if (otherNode !== target && otherNode.form === target.form) {\n var otherProps = otherNode[internalPropsKey] || null;\n if (!otherProps) throw Error(formatProdErrorMessage(90));\n updateInput(\n otherNode,\n otherProps.value,\n otherProps.defaultValue,\n otherProps.defaultValue,\n otherProps.checked,\n otherProps.defaultChecked,\n otherProps.type,\n otherProps.name\n );\n }\n }\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n )\n (otherNode = props[internalInstance]),\n otherNode.form === target.form && updateValueIfChanged(otherNode);\n }\n break a;\n case \"textarea\":\n updateTextarea(target, props.value, props.defaultValue);\n break a;\n case \"select\":\n (internalInstance = props.value),\n null != internalInstance &&\n updateOptions(target, !!props.multiple, internalInstance, !1);\n }\n }\n}\nvar isInsideEventHandler = !1;\nfunction batchedUpdates$1(fn, a, b) {\n if (isInsideEventHandler) return fn(a, b);\n isInsideEventHandler = !0;\n try {\n var JSCompiler_inline_result = fn(a);\n return JSCompiler_inline_result;\n } finally {\n if (\n ((isInsideEventHandler = !1),\n null !== restoreTarget || null !== restoreQueue)\n )\n if (\n (flushSyncWork$1(),\n restoreTarget &&\n ((a = restoreTarget),\n (fn = restoreQueue),\n (restoreQueue = restoreTarget = null),\n restoreStateOfTarget(a),\n fn))\n )\n for (a = 0; a < fn.length; a++) restoreStateOfTarget(fn[a]);\n }\n}\nfunction getListener(inst, registrationName) {\n var stateNode = inst.stateNode;\n if (null === stateNode) return null;\n var props = stateNode[internalPropsKey] || null;\n if (null === props) return null;\n stateNode = props[registrationName];\n a: switch (registrationName) {\n case \"onClick\":\n case \"onClickCapture\":\n case \"onDoubleClick\":\n case \"onDoubleClickCapture\":\n case \"onMouseDown\":\n case \"onMouseDownCapture\":\n case \"onMouseMove\":\n case \"onMouseMoveCapture\":\n case \"onMouseUp\":\n case \"onMouseUpCapture\":\n case \"onMouseEnter\":\n (props = !props.disabled) ||\n ((inst = inst.type),\n (props = !(\n \"button\" === inst ||\n \"input\" === inst ||\n \"select\" === inst ||\n \"textarea\" === inst\n )));\n inst = !props;\n break a;\n default:\n inst = !1;\n }\n if (inst) return null;\n if (stateNode && \"function\" !== typeof stateNode)\n throw Error(\n formatProdErrorMessage(231, registrationName, typeof stateNode)\n );\n return stateNode;\n}\nvar canUseDOM = !(\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ),\n passiveBrowserEventsSupported = !1;\nif (canUseDOM)\n try {\n var options = {};\n Object.defineProperty(options, \"passive\", {\n get: function () {\n passiveBrowserEventsSupported = !0;\n }\n });\n window.addEventListener(\"test\", options, options);\n window.removeEventListener(\"test\", options, options);\n } catch (e) {\n passiveBrowserEventsSupported = !1;\n }\nvar root = null,\n startText = null,\n fallbackText = null;\nfunction getData() {\n if (fallbackText) return fallbackText;\n var start,\n startValue = startText,\n startLength = startValue.length,\n end,\n endValue = \"value\" in root ? root.value : root.textContent,\n endLength = endValue.length;\n for (\n start = 0;\n start < startLength && startValue[start] === endValue[start];\n start++\n );\n var minEnd = startLength - start;\n for (\n end = 1;\n end <= minEnd &&\n startValue[startLength - end] === endValue[endLength - end];\n end++\n );\n return (fallbackText = endValue.slice(start, 1 < end ? 1 - end : void 0));\n}\nfunction getEventCharCode(nativeEvent) {\n var keyCode = nativeEvent.keyCode;\n \"charCode\" in nativeEvent\n ? ((nativeEvent = nativeEvent.charCode),\n 0 === nativeEvent && 13 === keyCode && (nativeEvent = 13))\n : (nativeEvent = keyCode);\n 10 === nativeEvent && (nativeEvent = 13);\n return 32 <= nativeEvent || 13 === nativeEvent ? nativeEvent : 0;\n}\nfunction functionThatReturnsTrue() {\n return !0;\n}\nfunction functionThatReturnsFalse() {\n return !1;\n}\nfunction createSyntheticEvent(Interface) {\n function SyntheticBaseEvent(\n reactName,\n reactEventType,\n targetInst,\n nativeEvent,\n nativeEventTarget\n ) {\n this._reactName = reactName;\n this._targetInst = targetInst;\n this.type = reactEventType;\n this.nativeEvent = nativeEvent;\n this.target = nativeEventTarget;\n this.currentTarget = null;\n for (var propName in Interface)\n Interface.hasOwnProperty(propName) &&\n ((reactName = Interface[propName]),\n (this[propName] = reactName\n ? reactName(nativeEvent)\n : nativeEvent[propName]));\n this.isDefaultPrevented = (\n null != nativeEvent.defaultPrevented\n ? nativeEvent.defaultPrevented\n : !1 === nativeEvent.returnValue\n )\n ? functionThatReturnsTrue\n : functionThatReturnsFalse;\n this.isPropagationStopped = functionThatReturnsFalse;\n return this;\n }\n assign(SyntheticBaseEvent.prototype, {\n preventDefault: function () {\n this.defaultPrevented = !0;\n var event = this.nativeEvent;\n event &&\n (event.preventDefault\n ? event.preventDefault()\n : \"unknown\" !== typeof event.returnValue && (event.returnValue = !1),\n (this.isDefaultPrevented = functionThatReturnsTrue));\n },\n stopPropagation: function () {\n var event = this.nativeEvent;\n event &&\n (event.stopPropagation\n ? event.stopPropagation()\n : \"unknown\" !== typeof event.cancelBubble &&\n (event.cancelBubble = !0),\n (this.isPropagationStopped = functionThatReturnsTrue));\n },\n persist: function () {},\n isPersistent: functionThatReturnsTrue\n });\n return SyntheticBaseEvent;\n}\nvar EventInterface = {\n eventPhase: 0,\n bubbles: 0,\n cancelable: 0,\n timeStamp: function (event) {\n return event.timeStamp || Date.now();\n },\n defaultPrevented: 0,\n isTrusted: 0\n },\n SyntheticEvent = createSyntheticEvent(EventInterface),\n UIEventInterface = assign({}, EventInterface, { view: 0, detail: 0 }),\n SyntheticUIEvent = createSyntheticEvent(UIEventInterface),\n lastMovementX,\n lastMovementY,\n lastMouseEvent,\n MouseEventInterface = assign({}, UIEventInterface, {\n screenX: 0,\n screenY: 0,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n getModifierState: getEventModifierState,\n button: 0,\n buttons: 0,\n relatedTarget: function (event) {\n return void 0 === event.relatedTarget\n ? event.fromElement === event.srcElement\n ? event.toElement\n : event.fromElement\n : event.relatedTarget;\n },\n movementX: function (event) {\n if (\"movementX\" in event) return event.movementX;\n event !== lastMouseEvent &&\n (lastMouseEvent && \"mousemove\" === event.type\n ? ((lastMovementX = event.screenX - lastMouseEvent.screenX),\n (lastMovementY = event.screenY - lastMouseEvent.screenY))\n : (lastMovementY = lastMovementX = 0),\n (lastMouseEvent = event));\n return lastMovementX;\n },\n movementY: function (event) {\n return \"movementY\" in event ? event.movementY : lastMovementY;\n }\n }),\n SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface),\n DragEventInterface = assign({}, MouseEventInterface, { dataTransfer: 0 }),\n SyntheticDragEvent = createSyntheticEvent(DragEventInterface),\n FocusEventInterface = assign({}, UIEventInterface, { relatedTarget: 0 }),\n SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface),\n AnimationEventInterface = assign({}, EventInterface, {\n animationName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface),\n ClipboardEventInterface = assign({}, EventInterface, {\n clipboardData: function (event) {\n return \"clipboardData\" in event\n ? event.clipboardData\n : window.clipboardData;\n }\n }),\n SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface),\n CompositionEventInterface = assign({}, EventInterface, { data: 0 }),\n SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface),\n normalizeKey = {\n Esc: \"Escape\",\n Spacebar: \" \",\n Left: \"ArrowLeft\",\n Up: \"ArrowUp\",\n Right: \"ArrowRight\",\n Down: \"ArrowDown\",\n Del: \"Delete\",\n Win: \"OS\",\n Menu: \"ContextMenu\",\n Apps: \"ContextMenu\",\n Scroll: \"ScrollLock\",\n MozPrintableKey: \"Unidentified\"\n },\n translateToKey = {\n 8: \"Backspace\",\n 9: \"Tab\",\n 12: \"Clear\",\n 13: \"Enter\",\n 16: \"Shift\",\n 17: \"Control\",\n 18: \"Alt\",\n 19: \"Pause\",\n 20: \"CapsLock\",\n 27: \"Escape\",\n 32: \" \",\n 33: \"PageUp\",\n 34: \"PageDown\",\n 35: \"End\",\n 36: \"Home\",\n 37: \"ArrowLeft\",\n 38: \"ArrowUp\",\n 39: \"ArrowRight\",\n 40: \"ArrowDown\",\n 45: \"Insert\",\n 46: \"Delete\",\n 112: \"F1\",\n 113: \"F2\",\n 114: \"F3\",\n 115: \"F4\",\n 116: \"F5\",\n 117: \"F6\",\n 118: \"F7\",\n 119: \"F8\",\n 120: \"F9\",\n 121: \"F10\",\n 122: \"F11\",\n 123: \"F12\",\n 144: \"NumLock\",\n 145: \"ScrollLock\",\n 224: \"Meta\"\n },\n modifierKeyToProp = {\n Alt: \"altKey\",\n Control: \"ctrlKey\",\n Meta: \"metaKey\",\n Shift: \"shiftKey\"\n };\nfunction modifierStateGetter(keyArg) {\n var nativeEvent = this.nativeEvent;\n return nativeEvent.getModifierState\n ? nativeEvent.getModifierState(keyArg)\n : (keyArg = modifierKeyToProp[keyArg])\n ? !!nativeEvent[keyArg]\n : !1;\n}\nfunction getEventModifierState() {\n return modifierStateGetter;\n}\nvar KeyboardEventInterface = assign({}, UIEventInterface, {\n key: function (nativeEvent) {\n if (nativeEvent.key) {\n var key = normalizeKey[nativeEvent.key] || nativeEvent.key;\n if (\"Unidentified\" !== key) return key;\n }\n return \"keypress\" === nativeEvent.type\n ? ((nativeEvent = getEventCharCode(nativeEvent)),\n 13 === nativeEvent ? \"Enter\" : String.fromCharCode(nativeEvent))\n : \"keydown\" === nativeEvent.type || \"keyup\" === nativeEvent.type\n ? translateToKey[nativeEvent.keyCode] || \"Unidentified\"\n : \"\";\n },\n code: 0,\n location: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n repeat: 0,\n locale: 0,\n getModifierState: getEventModifierState,\n charCode: function (event) {\n return \"keypress\" === event.type ? getEventCharCode(event) : 0;\n },\n keyCode: function (event) {\n return \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n },\n which: function (event) {\n return \"keypress\" === event.type\n ? getEventCharCode(event)\n : \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n }\n }),\n SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface),\n PointerEventInterface = assign({}, MouseEventInterface, {\n pointerId: 0,\n width: 0,\n height: 0,\n pressure: 0,\n tangentialPressure: 0,\n tiltX: 0,\n tiltY: 0,\n twist: 0,\n pointerType: 0,\n isPrimary: 0\n }),\n SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface),\n TouchEventInterface = assign({}, UIEventInterface, {\n touches: 0,\n targetTouches: 0,\n changedTouches: 0,\n altKey: 0,\n metaKey: 0,\n ctrlKey: 0,\n shiftKey: 0,\n getModifierState: getEventModifierState\n }),\n SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface),\n TransitionEventInterface = assign({}, EventInterface, {\n propertyName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface),\n WheelEventInterface = assign({}, MouseEventInterface, {\n deltaX: function (event) {\n return \"deltaX\" in event\n ? event.deltaX\n : \"wheelDeltaX\" in event\n ? -event.wheelDeltaX\n : 0;\n },\n deltaY: function (event) {\n return \"deltaY\" in event\n ? event.deltaY\n : \"wheelDeltaY\" in event\n ? -event.wheelDeltaY\n : \"wheelDelta\" in event\n ? -event.wheelDelta\n : 0;\n },\n deltaZ: 0,\n deltaMode: 0\n }),\n SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface),\n ToggleEventInterface = assign({}, EventInterface, {\n newState: 0,\n oldState: 0\n }),\n SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface),\n END_KEYCODES = [9, 13, 27, 32],\n canUseCompositionEvent = canUseDOM && \"CompositionEvent\" in window,\n documentMode = null;\ncanUseDOM &&\n \"documentMode\" in document &&\n (documentMode = document.documentMode);\nvar canUseTextInputEvent = canUseDOM && \"TextEvent\" in window && !documentMode,\n useFallbackCompositionData =\n canUseDOM &&\n (!canUseCompositionEvent ||\n (documentMode && 8 < documentMode && 11 >= documentMode)),\n SPACEBAR_CHAR = String.fromCharCode(32),\n hasSpaceKeypress = !1;\nfunction isFallbackCompositionEnd(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"keyup\":\n return -1 !== END_KEYCODES.indexOf(nativeEvent.keyCode);\n case \"keydown\":\n return 229 !== nativeEvent.keyCode;\n case \"keypress\":\n case \"mousedown\":\n case \"focusout\":\n return !0;\n default:\n return !1;\n }\n}\nfunction getDataFromCustomEvent(nativeEvent) {\n nativeEvent = nativeEvent.detail;\n return \"object\" === typeof nativeEvent && \"data\" in nativeEvent\n ? nativeEvent.data\n : null;\n}\nvar isComposing = !1;\nfunction getNativeBeforeInputChars(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"compositionend\":\n return getDataFromCustomEvent(nativeEvent);\n case \"keypress\":\n if (32 !== nativeEvent.which) return null;\n hasSpaceKeypress = !0;\n return SPACEBAR_CHAR;\n case \"textInput\":\n return (\n (domEventName = nativeEvent.data),\n domEventName === SPACEBAR_CHAR && hasSpaceKeypress ? null : domEventName\n );\n default:\n return null;\n }\n}\nfunction getFallbackBeforeInputChars(domEventName, nativeEvent) {\n if (isComposing)\n return \"compositionend\" === domEventName ||\n (!canUseCompositionEvent &&\n isFallbackCompositionEnd(domEventName, nativeEvent))\n ? ((domEventName = getData()),\n (fallbackText = startText = root = null),\n (isComposing = !1),\n domEventName)\n : null;\n switch (domEventName) {\n case \"paste\":\n return null;\n case \"keypress\":\n if (\n !(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) ||\n (nativeEvent.ctrlKey && nativeEvent.altKey)\n ) {\n if (nativeEvent.char && 1 < nativeEvent.char.length)\n return nativeEvent.char;\n if (nativeEvent.which) return String.fromCharCode(nativeEvent.which);\n }\n return null;\n case \"compositionend\":\n return useFallbackCompositionData && \"ko\" !== nativeEvent.locale\n ? null\n : nativeEvent.data;\n default:\n return null;\n }\n}\nvar supportedInputTypes = {\n color: !0,\n date: !0,\n datetime: !0,\n \"datetime-local\": !0,\n email: !0,\n month: !0,\n number: !0,\n password: !0,\n range: !0,\n search: !0,\n tel: !0,\n text: !0,\n time: !0,\n url: !0,\n week: !0\n};\nfunction isTextInputElement(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return \"input\" === nodeName\n ? !!supportedInputTypes[elem.type]\n : \"textarea\" === nodeName\n ? !0\n : !1;\n}\nfunction createAndAccumulateChangeEvent(\n dispatchQueue,\n inst,\n nativeEvent,\n target\n) {\n restoreTarget\n ? restoreQueue\n ? restoreQueue.push(target)\n : (restoreQueue = [target])\n : (restoreTarget = target);\n inst = accumulateTwoPhaseListeners(inst, \"onChange\");\n 0 < inst.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onChange\",\n \"change\",\n null,\n nativeEvent,\n target\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: inst }));\n}\nvar activeElement$1 = null,\n activeElementInst$1 = null;\nfunction runEventInBatch(dispatchQueue) {\n processDispatchQueue(dispatchQueue, 0);\n}\nfunction getInstIfValueChanged(targetInst) {\n var targetNode = getNodeFromInstance(targetInst);\n if (updateValueIfChanged(targetNode)) return targetInst;\n}\nfunction getTargetInstForChangeEvent(domEventName, targetInst) {\n if (\"change\" === domEventName) return targetInst;\n}\nvar isInputEventSupported = !1;\nif (canUseDOM) {\n var JSCompiler_inline_result$jscomp$286;\n if (canUseDOM) {\n var isSupported$jscomp$inline_427 = \"oninput\" in document;\n if (!isSupported$jscomp$inline_427) {\n var element$jscomp$inline_428 = document.createElement(\"div\");\n element$jscomp$inline_428.setAttribute(\"oninput\", \"return;\");\n isSupported$jscomp$inline_427 =\n \"function\" === typeof element$jscomp$inline_428.oninput;\n }\n JSCompiler_inline_result$jscomp$286 = isSupported$jscomp$inline_427;\n } else JSCompiler_inline_result$jscomp$286 = !1;\n isInputEventSupported =\n JSCompiler_inline_result$jscomp$286 &&\n (!document.documentMode || 9 < document.documentMode);\n}\nfunction stopWatchingForValueChange() {\n activeElement$1 &&\n (activeElement$1.detachEvent(\"onpropertychange\", handlePropertyChange),\n (activeElementInst$1 = activeElement$1 = null));\n}\nfunction handlePropertyChange(nativeEvent) {\n if (\n \"value\" === nativeEvent.propertyName &&\n getInstIfValueChanged(activeElementInst$1)\n ) {\n var dispatchQueue = [];\n createAndAccumulateChangeEvent(\n dispatchQueue,\n activeElementInst$1,\n nativeEvent,\n getEventTarget(nativeEvent)\n );\n batchedUpdates$1(runEventInBatch, dispatchQueue);\n }\n}\nfunction handleEventsForInputEventPolyfill(domEventName, target, targetInst) {\n \"focusin\" === domEventName\n ? (stopWatchingForValueChange(),\n (activeElement$1 = target),\n (activeElementInst$1 = targetInst),\n activeElement$1.attachEvent(\"onpropertychange\", handlePropertyChange))\n : \"focusout\" === domEventName && stopWatchingForValueChange();\n}\nfunction getTargetInstForInputEventPolyfill(domEventName) {\n if (\n \"selectionchange\" === domEventName ||\n \"keyup\" === domEventName ||\n \"keydown\" === domEventName\n )\n return getInstIfValueChanged(activeElementInst$1);\n}\nfunction getTargetInstForClickEvent(domEventName, targetInst) {\n if (\"click\" === domEventName) return getInstIfValueChanged(targetInst);\n}\nfunction getTargetInstForInputOrChangeEvent(domEventName, targetInst) {\n if (\"input\" === domEventName || \"change\" === domEventName)\n return getInstIfValueChanged(targetInst);\n}\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is;\nfunction shallowEqual(objA, objB) {\n if (objectIs(objA, objB)) return !0;\n if (\n \"object\" !== typeof objA ||\n null === objA ||\n \"object\" !== typeof objB ||\n null === objB\n )\n return !1;\n var keysA = Object.keys(objA),\n keysB = Object.keys(objB);\n if (keysA.length !== keysB.length) return !1;\n for (keysB = 0; keysB < keysA.length; keysB++) {\n var currentKey = keysA[keysB];\n if (\n !hasOwnProperty.call(objB, currentKey) ||\n !objectIs(objA[currentKey], objB[currentKey])\n )\n return !1;\n }\n return !0;\n}\nfunction getLeafNode(node) {\n for (; node && node.firstChild; ) node = node.firstChild;\n return node;\n}\nfunction getNodeForCharacterOffset(root, offset) {\n var node = getLeafNode(root);\n root = 0;\n for (var nodeEnd; node; ) {\n if (3 === node.nodeType) {\n nodeEnd = root + node.textContent.length;\n if (root <= offset && nodeEnd >= offset)\n return { node: node, offset: offset - root };\n root = nodeEnd;\n }\n a: {\n for (; node; ) {\n if (node.nextSibling) {\n node = node.nextSibling;\n break a;\n }\n node = node.parentNode;\n }\n node = void 0;\n }\n node = getLeafNode(node);\n }\n}\nfunction containsNode(outerNode, innerNode) {\n return outerNode && innerNode\n ? outerNode === innerNode\n ? !0\n : outerNode && 3 === outerNode.nodeType\n ? !1\n : innerNode && 3 === innerNode.nodeType\n ? containsNode(outerNode, innerNode.parentNode)\n : \"contains\" in outerNode\n ? outerNode.contains(innerNode)\n : outerNode.compareDocumentPosition\n ? !!(outerNode.compareDocumentPosition(innerNode) & 16)\n : !1\n : !1;\n}\nfunction getActiveElementDeep(containerInfo) {\n containerInfo =\n null != containerInfo &&\n null != containerInfo.ownerDocument &&\n null != containerInfo.ownerDocument.defaultView\n ? containerInfo.ownerDocument.defaultView\n : window;\n for (\n var element = getActiveElement(containerInfo.document);\n element instanceof containerInfo.HTMLIFrameElement;\n\n ) {\n try {\n var JSCompiler_inline_result =\n \"string\" === typeof element.contentWindow.location.href;\n } catch (err) {\n JSCompiler_inline_result = !1;\n }\n if (JSCompiler_inline_result) containerInfo = element.contentWindow;\n else break;\n element = getActiveElement(containerInfo.document);\n }\n return element;\n}\nfunction hasSelectionCapabilities(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return (\n nodeName &&\n ((\"input\" === nodeName &&\n (\"text\" === elem.type ||\n \"search\" === elem.type ||\n \"tel\" === elem.type ||\n \"url\" === elem.type ||\n \"password\" === elem.type)) ||\n \"textarea\" === nodeName ||\n \"true\" === elem.contentEditable)\n );\n}\nvar skipSelectionChangeEvent =\n canUseDOM && \"documentMode\" in document && 11 >= document.documentMode,\n activeElement = null,\n activeElementInst = null,\n lastSelection = null,\n mouseDown = !1;\nfunction constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {\n var doc =\n nativeEventTarget.window === nativeEventTarget\n ? nativeEventTarget.document\n : 9 === nativeEventTarget.nodeType\n ? nativeEventTarget\n : nativeEventTarget.ownerDocument;\n mouseDown ||\n null == activeElement ||\n activeElement !== getActiveElement(doc) ||\n ((doc = activeElement),\n \"selectionStart\" in doc && hasSelectionCapabilities(doc)\n ? (doc = { start: doc.selectionStart, end: doc.selectionEnd })\n : ((doc = (\n (doc.ownerDocument && doc.ownerDocument.defaultView) ||\n window\n ).getSelection()),\n (doc = {\n anchorNode: doc.anchorNode,\n anchorOffset: doc.anchorOffset,\n focusNode: doc.focusNode,\n focusOffset: doc.focusOffset\n })),\n (lastSelection && shallowEqual(lastSelection, doc)) ||\n ((lastSelection = doc),\n (doc = accumulateTwoPhaseListeners(activeElementInst, \"onSelect\")),\n 0 < doc.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onSelect\",\n \"select\",\n null,\n nativeEvent,\n nativeEventTarget\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: doc }),\n (nativeEvent.target = activeElement))));\n}\nfunction makePrefixMap(styleProp, eventName) {\n var prefixes = {};\n prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();\n prefixes[\"Webkit\" + styleProp] = \"webkit\" + eventName;\n prefixes[\"Moz\" + styleProp] = \"moz\" + eventName;\n return prefixes;\n}\nvar vendorPrefixes = {\n animationend: makePrefixMap(\"Animation\", \"AnimationEnd\"),\n animationiteration: makePrefixMap(\"Animation\", \"AnimationIteration\"),\n animationstart: makePrefixMap(\"Animation\", \"AnimationStart\"),\n transitionrun: makePrefixMap(\"Transition\", \"TransitionRun\"),\n transitionstart: makePrefixMap(\"Transition\", \"TransitionStart\"),\n transitioncancel: makePrefixMap(\"Transition\", \"TransitionCancel\"),\n transitionend: makePrefixMap(\"Transition\", \"TransitionEnd\")\n },\n prefixedEventNames = {},\n style = {};\ncanUseDOM &&\n ((style = document.createElement(\"div\").style),\n \"AnimationEvent\" in window ||\n (delete vendorPrefixes.animationend.animation,\n delete vendorPrefixes.animationiteration.animation,\n delete vendorPrefixes.animationstart.animation),\n \"TransitionEvent\" in window ||\n delete vendorPrefixes.transitionend.transition);\nfunction getVendorPrefixedEventName(eventName) {\n if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];\n if (!vendorPrefixes[eventName]) return eventName;\n var prefixMap = vendorPrefixes[eventName],\n styleProp;\n for (styleProp in prefixMap)\n if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)\n return (prefixedEventNames[eventName] = prefixMap[styleProp]);\n return eventName;\n}\nvar ANIMATION_END = getVendorPrefixedEventName(\"animationend\"),\n ANIMATION_ITERATION = getVendorPrefixedEventName(\"animationiteration\"),\n ANIMATION_START = getVendorPrefixedEventName(\"animationstart\"),\n TRANSITION_RUN = getVendorPrefixedEventName(\"transitionrun\"),\n TRANSITION_START = getVendorPrefixedEventName(\"transitionstart\"),\n TRANSITION_CANCEL = getVendorPrefixedEventName(\"transitioncancel\"),\n TRANSITION_END = getVendorPrefixedEventName(\"transitionend\"),\n topLevelEventsToReactNames = new Map(),\n simpleEventPluginEvents =\n \"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\n \" \"\n );\nsimpleEventPluginEvents.push(\"scrollEnd\");\nfunction registerSimpleEvent(domEventName, reactName) {\n topLevelEventsToReactNames.set(domEventName, reactName);\n registerTwoPhaseEvent(reactName, [domEventName]);\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n concurrentQueues = [],\n concurrentQueuesIndex = 0,\n concurrentlyUpdatedLanes = 0;\nfunction finishQueueingConcurrentUpdates() {\n for (\n var endIndex = concurrentQueuesIndex,\n i = (concurrentlyUpdatedLanes = concurrentQueuesIndex = 0);\n i < endIndex;\n\n ) {\n var fiber = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var queue = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var update = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var lane = concurrentQueues[i];\n concurrentQueues[i++] = null;\n if (null !== queue && null !== update) {\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n }\n 0 !== lane && markUpdateLaneFromFiberToRoot(fiber, update, lane);\n }\n}\nfunction enqueueUpdate$1(fiber, queue, update, lane) {\n concurrentQueues[concurrentQueuesIndex++] = fiber;\n concurrentQueues[concurrentQueuesIndex++] = queue;\n concurrentQueues[concurrentQueuesIndex++] = update;\n concurrentQueues[concurrentQueuesIndex++] = lane;\n concurrentlyUpdatedLanes |= lane;\n fiber.lanes |= lane;\n fiber = fiber.alternate;\n null !== fiber && (fiber.lanes |= lane);\n}\nfunction enqueueConcurrentHookUpdate(fiber, queue, update, lane) {\n enqueueUpdate$1(fiber, queue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction enqueueConcurrentRenderForLane(fiber, lane) {\n enqueueUpdate$1(fiber, null, null, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {\n sourceFiber.lanes |= lane;\n var alternate = sourceFiber.alternate;\n null !== alternate && (alternate.lanes |= lane);\n for (var isHidden = !1, parent = sourceFiber.return; null !== parent; )\n (parent.childLanes |= lane),\n (alternate = parent.alternate),\n null !== alternate && (alternate.childLanes |= lane),\n 22 === parent.tag &&\n ((sourceFiber = parent.stateNode),\n null === sourceFiber || sourceFiber._visibility & 1 || (isHidden = !0)),\n (sourceFiber = parent),\n (parent = parent.return);\n return 3 === sourceFiber.tag\n ? ((parent = sourceFiber.stateNode),\n isHidden &&\n null !== update &&\n ((isHidden = 31 - clz32(lane)),\n (sourceFiber = parent.hiddenUpdates),\n (alternate = sourceFiber[isHidden]),\n null === alternate\n ? (sourceFiber[isHidden] = [update])\n : alternate.push(update),\n (update.lane = lane | 536870912)),\n parent)\n : null;\n}\nfunction getRootForUpdatedFiber(sourceFiber) {\n if (50 < nestedUpdateCount)\n throw (\n ((nestedUpdateCount = 0),\n (rootWithNestedUpdates = null),\n Error(formatProdErrorMessage(185)))\n );\n for (var parent = sourceFiber.return; null !== parent; )\n (sourceFiber = parent), (parent = sourceFiber.return);\n return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;\n}\nvar emptyContextObject = {};\nfunction FiberNode(tag, pendingProps, key, mode) {\n this.tag = tag;\n this.key = key;\n this.sibling =\n this.child =\n this.return =\n this.stateNode =\n this.type =\n this.elementType =\n null;\n this.index = 0;\n this.refCleanup = this.ref = null;\n this.pendingProps = pendingProps;\n this.dependencies =\n this.memoizedState =\n this.updateQueue =\n this.memoizedProps =\n null;\n this.mode = mode;\n this.subtreeFlags = this.flags = 0;\n this.deletions = null;\n this.childLanes = this.lanes = 0;\n this.alternate = null;\n}\nfunction createFiberImplClass(tag, pendingProps, key, mode) {\n return new FiberNode(tag, pendingProps, key, mode);\n}\nfunction shouldConstruct(Component) {\n Component = Component.prototype;\n return !(!Component || !Component.isReactComponent);\n}\nfunction createWorkInProgress(current, pendingProps) {\n var workInProgress = current.alternate;\n null === workInProgress\n ? ((workInProgress = createFiberImplClass(\n current.tag,\n pendingProps,\n current.key,\n current.mode\n )),\n (workInProgress.elementType = current.elementType),\n (workInProgress.type = current.type),\n (workInProgress.stateNode = current.stateNode),\n (workInProgress.alternate = current),\n (current.alternate = workInProgress))\n : ((workInProgress.pendingProps = pendingProps),\n (workInProgress.type = current.type),\n (workInProgress.flags = 0),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null));\n workInProgress.flags = current.flags & 65011712;\n workInProgress.childLanes = current.childLanes;\n workInProgress.lanes = current.lanes;\n workInProgress.child = current.child;\n workInProgress.memoizedProps = current.memoizedProps;\n workInProgress.memoizedState = current.memoizedState;\n workInProgress.updateQueue = current.updateQueue;\n pendingProps = current.dependencies;\n workInProgress.dependencies =\n null === pendingProps\n ? null\n : { lanes: pendingProps.lanes, firstContext: pendingProps.firstContext };\n workInProgress.sibling = current.sibling;\n workInProgress.index = current.index;\n workInProgress.ref = current.ref;\n workInProgress.refCleanup = current.refCleanup;\n return workInProgress;\n}\nfunction resetWorkInProgress(workInProgress, renderLanes) {\n workInProgress.flags &= 65011714;\n var current = workInProgress.alternate;\n null === current\n ? ((workInProgress.childLanes = 0),\n (workInProgress.lanes = renderLanes),\n (workInProgress.child = null),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.memoizedProps = null),\n (workInProgress.memoizedState = null),\n (workInProgress.updateQueue = null),\n (workInProgress.dependencies = null),\n (workInProgress.stateNode = null))\n : ((workInProgress.childLanes = current.childLanes),\n (workInProgress.lanes = current.lanes),\n (workInProgress.child = current.child),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null),\n (workInProgress.memoizedProps = current.memoizedProps),\n (workInProgress.memoizedState = current.memoizedState),\n (workInProgress.updateQueue = current.updateQueue),\n (workInProgress.type = current.type),\n (renderLanes = current.dependencies),\n (workInProgress.dependencies =\n null === renderLanes\n ? null\n : {\n lanes: renderLanes.lanes,\n firstContext: renderLanes.firstContext\n }));\n return workInProgress;\n}\nfunction createFiberFromTypeAndProps(\n type,\n key,\n pendingProps,\n owner,\n mode,\n lanes\n) {\n var fiberTag = 0;\n owner = type;\n if (\"function\" === typeof type) shouldConstruct(type) && (fiberTag = 1);\n else if (\"string\" === typeof type)\n fiberTag = isHostHoistableType(\n type,\n pendingProps,\n contextStackCursor.current\n )\n ? 26\n : \"html\" === type || \"head\" === type || \"body\" === type\n ? 27\n : 5;\n else\n a: switch (type) {\n case REACT_ACTIVITY_TYPE:\n return (\n (type = createFiberImplClass(31, pendingProps, key, mode)),\n (type.elementType = REACT_ACTIVITY_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_FRAGMENT_TYPE:\n return createFiberFromFragment(pendingProps.children, mode, lanes, key);\n case REACT_STRICT_MODE_TYPE:\n fiberTag = 8;\n mode |= 24;\n break;\n case REACT_PROFILER_TYPE:\n return (\n (type = createFiberImplClass(12, pendingProps, key, mode | 2)),\n (type.elementType = REACT_PROFILER_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_TYPE:\n return (\n (type = createFiberImplClass(13, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_LIST_TYPE:\n return (\n (type = createFiberImplClass(19, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_LIST_TYPE),\n (type.lanes = lanes),\n type\n );\n default:\n if (\"object\" === typeof type && null !== type)\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n fiberTag = 10;\n break a;\n case REACT_CONSUMER_TYPE:\n fiberTag = 9;\n break a;\n case REACT_FORWARD_REF_TYPE:\n fiberTag = 11;\n break a;\n case REACT_MEMO_TYPE:\n fiberTag = 14;\n break a;\n case REACT_LAZY_TYPE:\n fiberTag = 16;\n owner = null;\n break a;\n }\n fiberTag = 29;\n pendingProps = Error(\n formatProdErrorMessage(130, null === type ? \"null\" : typeof type, \"\")\n );\n owner = null;\n }\n key = createFiberImplClass(fiberTag, pendingProps, key, mode);\n key.elementType = type;\n key.type = owner;\n key.lanes = lanes;\n return key;\n}\nfunction createFiberFromFragment(elements, mode, lanes, key) {\n elements = createFiberImplClass(7, elements, key, mode);\n elements.lanes = lanes;\n return elements;\n}\nfunction createFiberFromText(content, mode, lanes) {\n content = createFiberImplClass(6, content, null, mode);\n content.lanes = lanes;\n return content;\n}\nfunction createFiberFromDehydratedFragment(dehydratedNode) {\n var fiber = createFiberImplClass(18, null, null, 0);\n fiber.stateNode = dehydratedNode;\n return fiber;\n}\nfunction createFiberFromPortal(portal, mode, lanes) {\n mode = createFiberImplClass(\n 4,\n null !== portal.children ? portal.children : [],\n portal.key,\n mode\n );\n mode.lanes = lanes;\n mode.stateNode = {\n containerInfo: portal.containerInfo,\n pendingChildren: null,\n implementation: portal.implementation\n };\n return mode;\n}\nvar CapturedStacks = new WeakMap();\nfunction createCapturedValueAtFiber(value, source) {\n if (\"object\" === typeof value && null !== value) {\n var existing = CapturedStacks.get(value);\n if (void 0 !== existing) return existing;\n source = {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n CapturedStacks.set(value, source);\n return source;\n }\n return {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n}\nvar forkStack = [],\n forkStackIndex = 0,\n treeForkProvider = null,\n treeForkCount = 0,\n idStack = [],\n idStackIndex = 0,\n treeContextProvider = null,\n treeContextId = 1,\n treeContextOverflow = \"\";\nfunction pushTreeFork(workInProgress, totalChildren) {\n forkStack[forkStackIndex++] = treeForkCount;\n forkStack[forkStackIndex++] = treeForkProvider;\n treeForkProvider = workInProgress;\n treeForkCount = totalChildren;\n}\nfunction pushTreeId(workInProgress, totalChildren, index) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextProvider = workInProgress;\n var baseIdWithLeadingBit = treeContextId;\n workInProgress = treeContextOverflow;\n var baseLength = 32 - clz32(baseIdWithLeadingBit) - 1;\n baseIdWithLeadingBit &= ~(1 << baseLength);\n index += 1;\n var length = 32 - clz32(totalChildren) + baseLength;\n if (30 < length) {\n var numberOfOverflowBits = baseLength - (baseLength % 5);\n length = (\n baseIdWithLeadingBit &\n ((1 << numberOfOverflowBits) - 1)\n ).toString(32);\n baseIdWithLeadingBit >>= numberOfOverflowBits;\n baseLength -= numberOfOverflowBits;\n treeContextId =\n (1 << (32 - clz32(totalChildren) + baseLength)) |\n (index << baseLength) |\n baseIdWithLeadingBit;\n treeContextOverflow = length + workInProgress;\n } else\n (treeContextId =\n (1 << length) | (index << baseLength) | baseIdWithLeadingBit),\n (treeContextOverflow = workInProgress);\n}\nfunction pushMaterializedTreeId(workInProgress) {\n null !== workInProgress.return &&\n (pushTreeFork(workInProgress, 1), pushTreeId(workInProgress, 1, 0));\n}\nfunction popTreeContext(workInProgress) {\n for (; workInProgress === treeForkProvider; )\n (treeForkProvider = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null),\n (treeForkCount = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null);\n for (; workInProgress === treeContextProvider; )\n (treeContextProvider = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextOverflow = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextId = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null);\n}\nfunction restoreSuspendedTreeContext(workInProgress, suspendedContext) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextId = suspendedContext.id;\n treeContextOverflow = suspendedContext.overflow;\n treeContextProvider = workInProgress;\n}\nvar hydrationParentFiber = null,\n nextHydratableInstance = null,\n isHydrating = !1,\n hydrationErrors = null,\n rootOrSingletonContext = !1,\n HydrationMismatchException = Error(formatProdErrorMessage(519));\nfunction throwOnHydrationMismatch(fiber) {\n var error = Error(\n formatProdErrorMessage(\n 418,\n 1 < arguments.length && void 0 !== arguments[1] && arguments[1]\n ? \"text\"\n : \"HTML\",\n \"\"\n )\n );\n queueHydrationError(createCapturedValueAtFiber(error, fiber));\n throw HydrationMismatchException;\n}\nfunction prepareToHydrateHostInstance(fiber) {\n var instance = fiber.stateNode,\n type = fiber.type,\n props = fiber.memoizedProps;\n instance[internalInstanceKey] = fiber;\n instance[internalPropsKey] = props;\n switch (type) {\n case \"dialog\":\n listenToNonDelegatedEvent(\"cancel\", instance);\n listenToNonDelegatedEvent(\"close\", instance);\n break;\n case \"iframe\":\n case \"object\":\n case \"embed\":\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"video\":\n case \"audio\":\n for (type = 0; type < mediaEventTypes.length; type++)\n listenToNonDelegatedEvent(mediaEventTypes[type], instance);\n break;\n case \"source\":\n listenToNonDelegatedEvent(\"error\", instance);\n break;\n case \"img\":\n case \"image\":\n case \"link\":\n listenToNonDelegatedEvent(\"error\", instance);\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"details\":\n listenToNonDelegatedEvent(\"toggle\", instance);\n break;\n case \"input\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n initInput(\n instance,\n props.value,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name,\n !0\n );\n break;\n case \"select\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n break;\n case \"textarea\":\n listenToNonDelegatedEvent(\"invalid\", instance),\n initTextarea(instance, props.value, props.defaultValue, props.children);\n }\n type = props.children;\n (\"string\" !== typeof type &&\n \"number\" !== typeof type &&\n \"bigint\" !== typeof type) ||\n instance.textContent === \"\" + type ||\n !0 === props.suppressHydrationWarning ||\n checkForUnmatchedText(instance.textContent, type)\n ? (null != props.popover &&\n (listenToNonDelegatedEvent(\"beforetoggle\", instance),\n listenToNonDelegatedEvent(\"toggle\", instance)),\n null != props.onScroll && listenToNonDelegatedEvent(\"scroll\", instance),\n null != props.onScrollEnd &&\n listenToNonDelegatedEvent(\"scrollend\", instance),\n null != props.onClick && (instance.onclick = noop$1),\n (instance = !0))\n : (instance = !1);\n instance || throwOnHydrationMismatch(fiber, !0);\n}\nfunction popToNextHostParent(fiber) {\n for (hydrationParentFiber = fiber.return; hydrationParentFiber; )\n switch (hydrationParentFiber.tag) {\n case 5:\n case 31:\n case 13:\n rootOrSingletonContext = !1;\n return;\n case 27:\n case 3:\n rootOrSingletonContext = !0;\n return;\n default:\n hydrationParentFiber = hydrationParentFiber.return;\n }\n}\nfunction popHydrationState(fiber) {\n if (fiber !== hydrationParentFiber) return !1;\n if (!isHydrating) return popToNextHostParent(fiber), (isHydrating = !0), !1;\n var tag = fiber.tag,\n JSCompiler_temp;\n if ((JSCompiler_temp = 3 !== tag && 27 !== tag)) {\n if ((JSCompiler_temp = 5 === tag))\n (JSCompiler_temp = fiber.type),\n (JSCompiler_temp =\n !(\"form\" !== JSCompiler_temp && \"button\" !== JSCompiler_temp) ||\n shouldSetTextContent(fiber.type, fiber.memoizedProps));\n JSCompiler_temp = !JSCompiler_temp;\n }\n JSCompiler_temp && nextHydratableInstance && throwOnHydrationMismatch(fiber);\n popToNextHostParent(fiber);\n if (13 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else if (31 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else\n 27 === tag\n ? ((tag = nextHydratableInstance),\n isSingletonScope(fiber.type)\n ? ((fiber = previousHydratableOnEnteringScopedSingleton),\n (previousHydratableOnEnteringScopedSingleton = null),\n (nextHydratableInstance = fiber))\n : (nextHydratableInstance = tag))\n : (nextHydratableInstance = hydrationParentFiber\n ? getNextHydratable(fiber.stateNode.nextSibling)\n : null);\n return !0;\n}\nfunction resetHydrationState() {\n nextHydratableInstance = hydrationParentFiber = null;\n isHydrating = !1;\n}\nfunction upgradeHydrationErrorsToRecoverable() {\n var queuedErrors = hydrationErrors;\n null !== queuedErrors &&\n (null === workInProgressRootRecoverableErrors\n ? (workInProgressRootRecoverableErrors = queuedErrors)\n : workInProgressRootRecoverableErrors.push.apply(\n workInProgressRootRecoverableErrors,\n queuedErrors\n ),\n (hydrationErrors = null));\n return queuedErrors;\n}\nfunction queueHydrationError(error) {\n null === hydrationErrors\n ? (hydrationErrors = [error])\n : hydrationErrors.push(error);\n}\nvar valueCursor = createCursor(null),\n currentlyRenderingFiber$1 = null,\n lastContextDependency = null;\nfunction pushProvider(providerFiber, context, nextValue) {\n push(valueCursor, context._currentValue);\n context._currentValue = nextValue;\n}\nfunction popProvider(context) {\n context._currentValue = valueCursor.current;\n pop(valueCursor);\n}\nfunction scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {\n for (; null !== parent; ) {\n var alternate = parent.alternate;\n (parent.childLanes & renderLanes) !== renderLanes\n ? ((parent.childLanes |= renderLanes),\n null !== alternate && (alternate.childLanes |= renderLanes))\n : null !== alternate &&\n (alternate.childLanes & renderLanes) !== renderLanes &&\n (alternate.childLanes |= renderLanes);\n if (parent === propagationRoot) break;\n parent = parent.return;\n }\n}\nfunction propagateContextChanges(\n workInProgress,\n contexts,\n renderLanes,\n forcePropagateEntireTree\n) {\n var fiber = workInProgress.child;\n null !== fiber && (fiber.return = workInProgress);\n for (; null !== fiber; ) {\n var list = fiber.dependencies;\n if (null !== list) {\n var nextFiber = fiber.child;\n list = list.firstContext;\n a: for (; null !== list; ) {\n var dependency = list;\n list = fiber;\n for (var i = 0; i < contexts.length; i++)\n if (dependency.context === contexts[i]) {\n list.lanes |= renderLanes;\n dependency = list.alternate;\n null !== dependency && (dependency.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(\n list.return,\n renderLanes,\n workInProgress\n );\n forcePropagateEntireTree || (nextFiber = null);\n break a;\n }\n list = dependency.next;\n }\n } else if (18 === fiber.tag) {\n nextFiber = fiber.return;\n if (null === nextFiber) throw Error(formatProdErrorMessage(341));\n nextFiber.lanes |= renderLanes;\n list = nextFiber.alternate;\n null !== list && (list.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(nextFiber, renderLanes, workInProgress);\n nextFiber = null;\n } else nextFiber = fiber.child;\n if (null !== nextFiber) nextFiber.return = fiber;\n else\n for (nextFiber = fiber; null !== nextFiber; ) {\n if (nextFiber === workInProgress) {\n nextFiber = null;\n break;\n }\n fiber = nextFiber.sibling;\n if (null !== fiber) {\n fiber.return = nextFiber.return;\n nextFiber = fiber;\n break;\n }\n nextFiber = nextFiber.return;\n }\n fiber = nextFiber;\n }\n}\nfunction propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n forcePropagateEntireTree\n) {\n current = null;\n for (\n var parent = workInProgress, isInsidePropagationBailout = !1;\n null !== parent;\n\n ) {\n if (!isInsidePropagationBailout)\n if (0 !== (parent.flags & 524288)) isInsidePropagationBailout = !0;\n else if (0 !== (parent.flags & 262144)) break;\n if (10 === parent.tag) {\n var currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent = currentParent.memoizedProps;\n if (null !== currentParent) {\n var context = parent.type;\n objectIs(parent.pendingProps.value, currentParent.value) ||\n (null !== current ? current.push(context) : (current = [context]));\n }\n } else if (parent === hostTransitionProviderCursor.current) {\n currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent.memoizedState.memoizedState !==\n parent.memoizedState.memoizedState &&\n (null !== current\n ? current.push(HostTransitionContext)\n : (current = [HostTransitionContext]));\n }\n parent = parent.return;\n }\n null !== current &&\n propagateContextChanges(\n workInProgress,\n current,\n renderLanes,\n forcePropagateEntireTree\n );\n workInProgress.flags |= 262144;\n}\nfunction checkIfContextChanged(currentDependencies) {\n for (\n currentDependencies = currentDependencies.firstContext;\n null !== currentDependencies;\n\n ) {\n if (\n !objectIs(\n currentDependencies.context._currentValue,\n currentDependencies.memoizedValue\n )\n )\n return !0;\n currentDependencies = currentDependencies.next;\n }\n return !1;\n}\nfunction prepareToReadContext(workInProgress) {\n currentlyRenderingFiber$1 = workInProgress;\n lastContextDependency = null;\n workInProgress = workInProgress.dependencies;\n null !== workInProgress && (workInProgress.firstContext = null);\n}\nfunction readContext(context) {\n return readContextForConsumer(currentlyRenderingFiber$1, context);\n}\nfunction readContextDuringReconciliation(consumer, context) {\n null === currentlyRenderingFiber$1 && prepareToReadContext(consumer);\n return readContextForConsumer(consumer, context);\n}\nfunction readContextForConsumer(consumer, context) {\n var value = context._currentValue;\n context = { context: context, memoizedValue: value, next: null };\n if (null === lastContextDependency) {\n if (null === consumer) throw Error(formatProdErrorMessage(308));\n lastContextDependency = context;\n consumer.dependencies = { lanes: 0, firstContext: context };\n consumer.flags |= 524288;\n } else lastContextDependency = lastContextDependency.next = context;\n return value;\n}\nvar AbortControllerLocal =\n \"undefined\" !== typeof AbortController\n ? AbortController\n : function () {\n var listeners = [],\n signal = (this.signal = {\n aborted: !1,\n addEventListener: function (type, listener) {\n listeners.push(listener);\n }\n });\n this.abort = function () {\n signal.aborted = !0;\n listeners.forEach(function (listener) {\n return listener();\n });\n };\n },\n scheduleCallback$2 = Scheduler.unstable_scheduleCallback,\n NormalPriority = Scheduler.unstable_NormalPriority,\n CacheContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n Consumer: null,\n Provider: null,\n _currentValue: null,\n _currentValue2: null,\n _threadCount: 0\n };\nfunction createCache() {\n return {\n controller: new AbortControllerLocal(),\n data: new Map(),\n refCount: 0\n };\n}\nfunction releaseCache(cache) {\n cache.refCount--;\n 0 === cache.refCount &&\n scheduleCallback$2(NormalPriority, function () {\n cache.controller.abort();\n });\n}\nvar currentEntangledListeners = null,\n currentEntangledPendingCount = 0,\n currentEntangledLane = 0,\n currentEntangledActionThenable = null;\nfunction entangleAsyncAction(transition, thenable) {\n if (null === currentEntangledListeners) {\n var entangledListeners = (currentEntangledListeners = []);\n currentEntangledPendingCount = 0;\n currentEntangledLane = requestTransitionLane();\n currentEntangledActionThenable = {\n status: \"pending\",\n value: void 0,\n then: function (resolve) {\n entangledListeners.push(resolve);\n }\n };\n }\n currentEntangledPendingCount++;\n thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);\n return thenable;\n}\nfunction pingEngtangledActionScope() {\n if (\n 0 === --currentEntangledPendingCount &&\n null !== currentEntangledListeners\n ) {\n null !== currentEntangledActionThenable &&\n (currentEntangledActionThenable.status = \"fulfilled\");\n var listeners = currentEntangledListeners;\n currentEntangledListeners = null;\n currentEntangledLane = 0;\n currentEntangledActionThenable = null;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])();\n }\n}\nfunction chainThenableValue(thenable, result) {\n var listeners = [],\n thenableWithOverride = {\n status: \"pending\",\n value: null,\n reason: null,\n then: function (resolve) {\n listeners.push(resolve);\n }\n };\n thenable.then(\n function () {\n thenableWithOverride.status = \"fulfilled\";\n thenableWithOverride.value = result;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])(result);\n },\n function (error) {\n thenableWithOverride.status = \"rejected\";\n thenableWithOverride.reason = error;\n for (error = 0; error < listeners.length; error++)\n (0, listeners[error])(void 0);\n }\n );\n return thenableWithOverride;\n}\nvar prevOnStartTransitionFinish = ReactSharedInternals.S;\nReactSharedInternals.S = function (transition, returnValue) {\n globalMostRecentTransitionTime = now();\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n entangleAsyncAction(transition, returnValue);\n null !== prevOnStartTransitionFinish &&\n prevOnStartTransitionFinish(transition, returnValue);\n};\nvar resumedCache = createCursor(null);\nfunction peekCacheFromPool() {\n var cacheResumedFromPreviousRender = resumedCache.current;\n return null !== cacheResumedFromPreviousRender\n ? cacheResumedFromPreviousRender\n : workInProgressRoot.pooledCache;\n}\nfunction pushTransition(offscreenWorkInProgress, prevCachePool) {\n null === prevCachePool\n ? push(resumedCache, resumedCache.current)\n : push(resumedCache, prevCachePool.pool);\n}\nfunction getSuspendedCache() {\n var cacheFromPool = peekCacheFromPool();\n return null === cacheFromPool\n ? null\n : { parent: CacheContext._currentValue, pool: cacheFromPool };\n}\nvar SuspenseException = Error(formatProdErrorMessage(460)),\n SuspenseyCommitException = Error(formatProdErrorMessage(474)),\n SuspenseActionException = Error(formatProdErrorMessage(542)),\n noopSuspenseyCommitThenable = { then: function () {} };\nfunction isThenableResolved(thenable) {\n thenable = thenable.status;\n return \"fulfilled\" === thenable || \"rejected\" === thenable;\n}\nfunction trackUsedThenable(thenableState, thenable, index) {\n index = thenableState[index];\n void 0 === index\n ? thenableState.push(thenable)\n : index !== thenable && (thenable.then(noop$1, noop$1), (thenable = index));\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n default:\n if (\"string\" === typeof thenable.status) thenable.then(noop$1, noop$1);\n else {\n thenableState = workInProgressRoot;\n if (null !== thenableState && 100 < thenableState.shellSuspendCounter)\n throw Error(formatProdErrorMessage(482));\n thenableState = thenable;\n thenableState.status = \"pending\";\n thenableState.then(\n function (fulfilledValue) {\n if (\"pending\" === thenable.status) {\n var fulfilledThenable = thenable;\n fulfilledThenable.status = \"fulfilled\";\n fulfilledThenable.value = fulfilledValue;\n }\n },\n function (error) {\n if (\"pending\" === thenable.status) {\n var rejectedThenable = thenable;\n rejectedThenable.status = \"rejected\";\n rejectedThenable.reason = error;\n }\n }\n );\n }\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n }\n suspendedThenable = thenable;\n throw SuspenseException;\n }\n}\nfunction resolveLazy(lazyType) {\n try {\n var init = lazyType._init;\n return init(lazyType._payload);\n } catch (x) {\n if (null !== x && \"object\" === typeof x && \"function\" === typeof x.then)\n throw ((suspendedThenable = x), SuspenseException);\n throw x;\n }\n}\nvar suspendedThenable = null;\nfunction getSuspendedThenable() {\n if (null === suspendedThenable) throw Error(formatProdErrorMessage(459));\n var thenable = suspendedThenable;\n suspendedThenable = null;\n return thenable;\n}\nfunction checkIfUseWrappedInAsyncCatch(rejectedReason) {\n if (\n rejectedReason === SuspenseException ||\n rejectedReason === SuspenseActionException\n )\n throw Error(formatProdErrorMessage(483));\n}\nvar thenableState$1 = null,\n thenableIndexCounter$1 = 0;\nfunction unwrapThenable(thenable) {\n var index = thenableIndexCounter$1;\n thenableIndexCounter$1 += 1;\n null === thenableState$1 && (thenableState$1 = []);\n return trackUsedThenable(thenableState$1, thenable, index);\n}\nfunction coerceRef(workInProgress, element) {\n element = element.props.ref;\n workInProgress.ref = void 0 !== element ? element : null;\n}\nfunction throwOnInvalidObjectTypeImpl(returnFiber, newChild) {\n if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE)\n throw Error(formatProdErrorMessage(525));\n returnFiber = Object.prototype.toString.call(newChild);\n throw Error(\n formatProdErrorMessage(\n 31,\n \"[object Object]\" === returnFiber\n ? \"object with keys {\" + Object.keys(newChild).join(\", \") + \"}\"\n : returnFiber\n )\n );\n}\nfunction createChildReconciler(shouldTrackSideEffects) {\n function deleteChild(returnFiber, childToDelete) {\n if (shouldTrackSideEffects) {\n var deletions = returnFiber.deletions;\n null === deletions\n ? ((returnFiber.deletions = [childToDelete]), (returnFiber.flags |= 16))\n : deletions.push(childToDelete);\n }\n }\n function deleteRemainingChildren(returnFiber, currentFirstChild) {\n if (!shouldTrackSideEffects) return null;\n for (; null !== currentFirstChild; )\n deleteChild(returnFiber, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return null;\n }\n function mapRemainingChildren(currentFirstChild) {\n for (var existingChildren = new Map(); null !== currentFirstChild; )\n null !== currentFirstChild.key\n ? existingChildren.set(currentFirstChild.key, currentFirstChild)\n : existingChildren.set(currentFirstChild.index, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return existingChildren;\n }\n function useFiber(fiber, pendingProps) {\n fiber = createWorkInProgress(fiber, pendingProps);\n fiber.index = 0;\n fiber.sibling = null;\n return fiber;\n }\n function placeChild(newFiber, lastPlacedIndex, newIndex) {\n newFiber.index = newIndex;\n if (!shouldTrackSideEffects)\n return (newFiber.flags |= 1048576), lastPlacedIndex;\n newIndex = newFiber.alternate;\n if (null !== newIndex)\n return (\n (newIndex = newIndex.index),\n newIndex < lastPlacedIndex\n ? ((newFiber.flags |= 67108866), lastPlacedIndex)\n : newIndex\n );\n newFiber.flags |= 67108866;\n return lastPlacedIndex;\n }\n function placeSingleChild(newFiber) {\n shouldTrackSideEffects &&\n null === newFiber.alternate &&\n (newFiber.flags |= 67108866);\n return newFiber;\n }\n function updateTextNode(returnFiber, current, textContent, lanes) {\n if (null === current || 6 !== current.tag)\n return (\n (current = createFiberFromText(textContent, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, textContent);\n current.return = returnFiber;\n return current;\n }\n function updateElement(returnFiber, current, element, lanes) {\n var elementType = element.type;\n if (elementType === REACT_FRAGMENT_TYPE)\n return updateFragment(\n returnFiber,\n current,\n element.props.children,\n lanes,\n element.key\n );\n if (\n null !== current &&\n (current.elementType === elementType ||\n (\"object\" === typeof elementType &&\n null !== elementType &&\n elementType.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(elementType) === current.type))\n )\n return (\n (current = useFiber(current, element.props)),\n coerceRef(current, element),\n (current.return = returnFiber),\n current\n );\n current = createFiberFromTypeAndProps(\n element.type,\n element.key,\n element.props,\n null,\n returnFiber.mode,\n lanes\n );\n coerceRef(current, element);\n current.return = returnFiber;\n return current;\n }\n function updatePortal(returnFiber, current, portal, lanes) {\n if (\n null === current ||\n 4 !== current.tag ||\n current.stateNode.containerInfo !== portal.containerInfo ||\n current.stateNode.implementation !== portal.implementation\n )\n return (\n (current = createFiberFromPortal(portal, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, portal.children || []);\n current.return = returnFiber;\n return current;\n }\n function updateFragment(returnFiber, current, fragment, lanes, key) {\n if (null === current || 7 !== current.tag)\n return (\n (current = createFiberFromFragment(\n fragment,\n returnFiber.mode,\n lanes,\n key\n )),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, fragment);\n current.return = returnFiber;\n return current;\n }\n function createChild(returnFiber, newChild, lanes) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (newChild = createFiberFromText(\n \"\" + newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n lanes\n );\n case REACT_PORTAL_TYPE:\n return (\n (newChild = createFiberFromPortal(\n newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n createChild(returnFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (newChild = createFiberFromFragment(\n newChild,\n returnFiber.mode,\n lanes,\n null\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"function\" === typeof newChild.then)\n return createChild(returnFiber, unwrapThenable(newChild), lanes);\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return createChild(\n returnFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateSlot(returnFiber, oldFiber, newChild, lanes) {\n var key = null !== oldFiber ? oldFiber.key : null;\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return null !== key\n ? null\n : updateTextNode(returnFiber, oldFiber, \"\" + newChild, lanes);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return newChild.key === key\n ? updateElement(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_PORTAL_TYPE:\n return newChild.key === key\n ? updatePortal(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateSlot(returnFiber, oldFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return null !== key\n ? null\n : updateFragment(returnFiber, oldFiber, newChild, lanes, null);\n if (\"function\" === typeof newChild.then)\n return updateSlot(\n returnFiber,\n oldFiber,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateSlot(\n returnFiber,\n oldFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n ) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateTextNode(returnFiber, existingChildren, \"\" + newChild, lanes)\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updateElement(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_PORTAL_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updatePortal(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateFragment(returnFiber, existingChildren, newChild, lanes, null)\n );\n if (\"function\" === typeof newChild.then)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null;\n null !== oldFiber && newIdx < newChildren.length;\n newIdx++\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(\n returnFiber,\n oldFiber,\n newChildren[newIdx],\n lanes\n );\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (newIdx === newChildren.length)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; newIdx < newChildren.length; newIdx++)\n (oldFiber = createChild(returnFiber, newChildren[newIdx], lanes)),\n null !== oldFiber &&\n ((currentFirstChild = placeChild(\n oldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = oldFiber)\n : (previousNewFiber.sibling = oldFiber),\n (previousNewFiber = oldFiber));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n newIdx < newChildren.length;\n newIdx++\n )\n (nextOldFiber = updateFromMap(\n oldFiber,\n returnFiber,\n newIdx,\n newChildren[newIdx],\n lanes\n )),\n null !== nextOldFiber &&\n (shouldTrackSideEffects &&\n null !== nextOldFiber.alternate &&\n oldFiber.delete(\n null === nextOldFiber.key ? newIdx : nextOldFiber.key\n ),\n (currentFirstChild = placeChild(\n nextOldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = nextOldFiber)\n : (previousNewFiber.sibling = nextOldFiber),\n (previousNewFiber = nextOldFiber));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n if (null == newChildren) throw Error(formatProdErrorMessage(151));\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null,\n step = newChildren.next();\n null !== oldFiber && !step.done;\n newIdx++, step = newChildren.next()\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (step.done)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; !step.done; newIdx++, step = newChildren.next())\n (step = createChild(returnFiber, step.value, lanes)),\n null !== step &&\n ((currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n !step.done;\n newIdx++, step = newChildren.next()\n )\n (step = updateFromMap(oldFiber, returnFiber, newIdx, step.value, lanes)),\n null !== step &&\n (shouldTrackSideEffects &&\n null !== step.alternate &&\n oldFiber.delete(null === step.key ? newIdx : step.key),\n (currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n ) {\n \"object\" === typeof newChild &&\n null !== newChild &&\n newChild.type === REACT_FRAGMENT_TYPE &&\n null === newChild.key &&\n (newChild = newChild.props.children);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n a: {\n for (var key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key) {\n key = newChild.type;\n if (key === REACT_FRAGMENT_TYPE) {\n if (7 === currentFirstChild.tag) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(\n currentFirstChild,\n newChild.props.children\n );\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n } else if (\n currentFirstChild.elementType === key ||\n (\"object\" === typeof key &&\n null !== key &&\n key.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(key) === currentFirstChild.type)\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.props);\n coerceRef(lanes, newChild);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n } else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n newChild.type === REACT_FRAGMENT_TYPE\n ? ((lanes = createFiberFromFragment(\n newChild.props.children,\n returnFiber.mode,\n lanes,\n newChild.key\n )),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : ((lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n (returnFiber = lanes));\n }\n return placeSingleChild(returnFiber);\n case REACT_PORTAL_TYPE:\n a: {\n for (key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key)\n if (\n 4 === currentFirstChild.tag &&\n currentFirstChild.stateNode.containerInfo ===\n newChild.containerInfo &&\n currentFirstChild.stateNode.implementation ===\n newChild.implementation\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.children || []);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n } else {\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n }\n else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n lanes = createFiberFromPortal(newChild, returnFiber.mode, lanes);\n lanes.return = returnFiber;\n returnFiber = lanes;\n }\n return placeSingleChild(returnFiber);\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild))\n return reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n if (getIteratorFn(newChild)) {\n key = getIteratorFn(newChild);\n if (\"function\" !== typeof key) throw Error(formatProdErrorMessage(150));\n newChild = key.call(newChild);\n return reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n }\n if (\"function\" === typeof newChild.then)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n ? ((newChild = \"\" + newChild),\n null !== currentFirstChild && 6 === currentFirstChild.tag\n ? (deleteRemainingChildren(returnFiber, currentFirstChild.sibling),\n (lanes = useFiber(currentFirstChild, newChild)),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : (deleteRemainingChildren(returnFiber, currentFirstChild),\n (lanes = createFiberFromText(newChild, returnFiber.mode, lanes)),\n (lanes.return = returnFiber),\n (returnFiber = lanes)),\n placeSingleChild(returnFiber))\n : deleteRemainingChildren(returnFiber, currentFirstChild);\n }\n return function (returnFiber, currentFirstChild, newChild, lanes) {\n try {\n thenableIndexCounter$1 = 0;\n var firstChildFiber = reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n thenableState$1 = null;\n return firstChildFiber;\n } catch (x) {\n if (x === SuspenseException || x === SuspenseActionException) throw x;\n var fiber = createFiberImplClass(29, x, null, returnFiber.mode);\n fiber.lanes = lanes;\n fiber.return = returnFiber;\n return fiber;\n } finally {\n }\n };\n}\nvar reconcileChildFibers = createChildReconciler(!0),\n mountChildFibers = createChildReconciler(!1),\n hasForceUpdate = !1;\nfunction initializeUpdateQueue(fiber) {\n fiber.updateQueue = {\n baseState: fiber.memoizedState,\n firstBaseUpdate: null,\n lastBaseUpdate: null,\n shared: { pending: null, lanes: 0, hiddenCallbacks: null },\n callbacks: null\n };\n}\nfunction cloneUpdateQueue(current, workInProgress) {\n current = current.updateQueue;\n workInProgress.updateQueue === current &&\n (workInProgress.updateQueue = {\n baseState: current.baseState,\n firstBaseUpdate: current.firstBaseUpdate,\n lastBaseUpdate: current.lastBaseUpdate,\n shared: current.shared,\n callbacks: null\n });\n}\nfunction createUpdate(lane) {\n return { lane: lane, tag: 0, payload: null, callback: null, next: null };\n}\nfunction enqueueUpdate(fiber, update, lane) {\n var updateQueue = fiber.updateQueue;\n if (null === updateQueue) return null;\n updateQueue = updateQueue.shared;\n if (0 !== (executionContext & 2)) {\n var pending = updateQueue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n updateQueue.pending = update;\n update = getRootForUpdatedFiber(fiber);\n markUpdateLaneFromFiberToRoot(fiber, null, lane);\n return update;\n }\n enqueueUpdate$1(fiber, updateQueue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction entangleTransitions(root, fiber, lane) {\n fiber = fiber.updateQueue;\n if (null !== fiber && ((fiber = fiber.shared), 0 !== (lane & 4194048))) {\n var queueLanes = fiber.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n fiber.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nfunction enqueueCapturedUpdate(workInProgress, capturedUpdate) {\n var queue = workInProgress.updateQueue,\n current = workInProgress.alternate;\n if (\n null !== current &&\n ((current = current.updateQueue), queue === current)\n ) {\n var newFirst = null,\n newLast = null;\n queue = queue.firstBaseUpdate;\n if (null !== queue) {\n do {\n var clone = {\n lane: queue.lane,\n tag: queue.tag,\n payload: queue.payload,\n callback: null,\n next: null\n };\n null === newLast\n ? (newFirst = newLast = clone)\n : (newLast = newLast.next = clone);\n queue = queue.next;\n } while (null !== queue);\n null === newLast\n ? (newFirst = newLast = capturedUpdate)\n : (newLast = newLast.next = capturedUpdate);\n } else newFirst = newLast = capturedUpdate;\n queue = {\n baseState: current.baseState,\n firstBaseUpdate: newFirst,\n lastBaseUpdate: newLast,\n shared: current.shared,\n callbacks: current.callbacks\n };\n workInProgress.updateQueue = queue;\n return;\n }\n workInProgress = queue.lastBaseUpdate;\n null === workInProgress\n ? (queue.firstBaseUpdate = capturedUpdate)\n : (workInProgress.next = capturedUpdate);\n queue.lastBaseUpdate = capturedUpdate;\n}\nvar didReadFromEntangledAsyncAction = !1;\nfunction suspendIfUpdateReadFromEntangledAsyncAction() {\n if (didReadFromEntangledAsyncAction) {\n var entangledActionThenable = currentEntangledActionThenable;\n if (null !== entangledActionThenable) throw entangledActionThenable;\n }\n}\nfunction processUpdateQueue(\n workInProgress$jscomp$0,\n props,\n instance$jscomp$0,\n renderLanes\n) {\n didReadFromEntangledAsyncAction = !1;\n var queue = workInProgress$jscomp$0.updateQueue;\n hasForceUpdate = !1;\n var firstBaseUpdate = queue.firstBaseUpdate,\n lastBaseUpdate = queue.lastBaseUpdate,\n pendingQueue = queue.shared.pending;\n if (null !== pendingQueue) {\n queue.shared.pending = null;\n var lastPendingUpdate = pendingQueue,\n firstPendingUpdate = lastPendingUpdate.next;\n lastPendingUpdate.next = null;\n null === lastBaseUpdate\n ? (firstBaseUpdate = firstPendingUpdate)\n : (lastBaseUpdate.next = firstPendingUpdate);\n lastBaseUpdate = lastPendingUpdate;\n var current = workInProgress$jscomp$0.alternate;\n null !== current &&\n ((current = current.updateQueue),\n (pendingQueue = current.lastBaseUpdate),\n pendingQueue !== lastBaseUpdate &&\n (null === pendingQueue\n ? (current.firstBaseUpdate = firstPendingUpdate)\n : (pendingQueue.next = firstPendingUpdate),\n (current.lastBaseUpdate = lastPendingUpdate)));\n }\n if (null !== firstBaseUpdate) {\n var newState = queue.baseState;\n lastBaseUpdate = 0;\n current = firstPendingUpdate = lastPendingUpdate = null;\n pendingQueue = firstBaseUpdate;\n do {\n var updateLane = pendingQueue.lane & -536870913,\n isHiddenUpdate = updateLane !== pendingQueue.lane;\n if (\n isHiddenUpdate\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n 0 !== updateLane &&\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction = !0);\n null !== current &&\n (current = current.next =\n {\n lane: 0,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: null,\n next: null\n });\n a: {\n var workInProgress = workInProgress$jscomp$0,\n update = pendingQueue;\n updateLane = props;\n var instance = instance$jscomp$0;\n switch (update.tag) {\n case 1:\n workInProgress = update.payload;\n if (\"function\" === typeof workInProgress) {\n newState = workInProgress.call(instance, newState, updateLane);\n break a;\n }\n newState = workInProgress;\n break a;\n case 3:\n workInProgress.flags = (workInProgress.flags & -65537) | 128;\n case 0:\n workInProgress = update.payload;\n updateLane =\n \"function\" === typeof workInProgress\n ? workInProgress.call(instance, newState, updateLane)\n : workInProgress;\n if (null === updateLane || void 0 === updateLane) break a;\n newState = assign({}, newState, updateLane);\n break a;\n case 2:\n hasForceUpdate = !0;\n }\n }\n updateLane = pendingQueue.callback;\n null !== updateLane &&\n ((workInProgress$jscomp$0.flags |= 64),\n isHiddenUpdate && (workInProgress$jscomp$0.flags |= 8192),\n (isHiddenUpdate = queue.callbacks),\n null === isHiddenUpdate\n ? (queue.callbacks = [updateLane])\n : isHiddenUpdate.push(updateLane));\n } else\n (isHiddenUpdate = {\n lane: updateLane,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: pendingQueue.callback,\n next: null\n }),\n null === current\n ? ((firstPendingUpdate = current = isHiddenUpdate),\n (lastPendingUpdate = newState))\n : (current = current.next = isHiddenUpdate),\n (lastBaseUpdate |= updateLane);\n pendingQueue = pendingQueue.next;\n if (null === pendingQueue)\n if (((pendingQueue = queue.shared.pending), null === pendingQueue))\n break;\n else\n (isHiddenUpdate = pendingQueue),\n (pendingQueue = isHiddenUpdate.next),\n (isHiddenUpdate.next = null),\n (queue.lastBaseUpdate = isHiddenUpdate),\n (queue.shared.pending = null);\n } while (1);\n null === current && (lastPendingUpdate = newState);\n queue.baseState = lastPendingUpdate;\n queue.firstBaseUpdate = firstPendingUpdate;\n queue.lastBaseUpdate = current;\n null === firstBaseUpdate && (queue.shared.lanes = 0);\n workInProgressRootSkippedLanes |= lastBaseUpdate;\n workInProgress$jscomp$0.lanes = lastBaseUpdate;\n workInProgress$jscomp$0.memoizedState = newState;\n }\n}\nfunction callCallback(callback, context) {\n if (\"function\" !== typeof callback)\n throw Error(formatProdErrorMessage(191, callback));\n callback.call(context);\n}\nfunction commitCallbacks(updateQueue, context) {\n var callbacks = updateQueue.callbacks;\n if (null !== callbacks)\n for (\n updateQueue.callbacks = null, updateQueue = 0;\n updateQueue < callbacks.length;\n updateQueue++\n )\n callCallback(callbacks[updateQueue], context);\n}\nvar currentTreeHiddenStackCursor = createCursor(null),\n prevEntangledRenderLanesCursor = createCursor(0);\nfunction pushHiddenContext(fiber, context) {\n fiber = entangledRenderLanes;\n push(prevEntangledRenderLanesCursor, fiber);\n push(currentTreeHiddenStackCursor, context);\n entangledRenderLanes = fiber | context.baseLanes;\n}\nfunction reuseHiddenContextOnStack() {\n push(prevEntangledRenderLanesCursor, entangledRenderLanes);\n push(currentTreeHiddenStackCursor, currentTreeHiddenStackCursor.current);\n}\nfunction popHiddenContext() {\n entangledRenderLanes = prevEntangledRenderLanesCursor.current;\n pop(currentTreeHiddenStackCursor);\n pop(prevEntangledRenderLanesCursor);\n}\nvar suspenseHandlerStackCursor = createCursor(null),\n shellBoundary = null;\nfunction pushPrimaryTreeSuspenseHandler(handler) {\n var current = handler.alternate;\n push(suspenseStackCursor, suspenseStackCursor.current & 1);\n push(suspenseHandlerStackCursor, handler);\n null === shellBoundary &&\n (null === current || null !== currentTreeHiddenStackCursor.current\n ? (shellBoundary = handler)\n : null !== current.memoizedState && (shellBoundary = handler));\n}\nfunction pushDehydratedActivitySuspenseHandler(fiber) {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, fiber);\n null === shellBoundary && (shellBoundary = fiber);\n}\nfunction pushOffscreenSuspenseHandler(fiber) {\n 22 === fiber.tag\n ? (push(suspenseStackCursor, suspenseStackCursor.current),\n push(suspenseHandlerStackCursor, fiber),\n null === shellBoundary && (shellBoundary = fiber))\n : reuseSuspenseHandlerOnStack(fiber);\n}\nfunction reuseSuspenseHandlerOnStack() {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, suspenseHandlerStackCursor.current);\n}\nfunction popSuspenseHandler(fiber) {\n pop(suspenseHandlerStackCursor);\n shellBoundary === fiber && (shellBoundary = null);\n pop(suspenseStackCursor);\n}\nvar suspenseStackCursor = createCursor(0);\nfunction findFirstSuspended(row) {\n for (var node = row; null !== node; ) {\n if (13 === node.tag) {\n var state = node.memoizedState;\n if (\n null !== state &&\n ((state = state.dehydrated),\n null === state ||\n isSuspenseInstancePending(state) ||\n isSuspenseInstanceFallback(state))\n )\n return node;\n } else if (\n 19 === node.tag &&\n (\"forwards\" === node.memoizedProps.revealOrder ||\n \"backwards\" === node.memoizedProps.revealOrder ||\n \"unstable_legacy-backwards\" === node.memoizedProps.revealOrder ||\n \"together\" === node.memoizedProps.revealOrder)\n ) {\n if (0 !== (node.flags & 128)) return node;\n } else if (null !== node.child) {\n node.child.return = node;\n node = node.child;\n continue;\n }\n if (node === row) break;\n for (; null === node.sibling; ) {\n if (null === node.return || node.return === row) return null;\n node = node.return;\n }\n node.sibling.return = node.return;\n node = node.sibling;\n }\n return null;\n}\nvar renderLanes = 0,\n currentlyRenderingFiber = null,\n currentHook = null,\n workInProgressHook = null,\n didScheduleRenderPhaseUpdate = !1,\n didScheduleRenderPhaseUpdateDuringThisPass = !1,\n shouldDoubleInvokeUserFnsInHooksDEV = !1,\n localIdCounter = 0,\n thenableIndexCounter = 0,\n thenableState = null,\n globalClientIdCounter = 0;\nfunction throwInvalidHookError() {\n throw Error(formatProdErrorMessage(321));\n}\nfunction areHookInputsEqual(nextDeps, prevDeps) {\n if (null === prevDeps) return !1;\n for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)\n if (!objectIs(nextDeps[i], prevDeps[i])) return !1;\n return !0;\n}\nfunction renderWithHooks(\n current,\n workInProgress,\n Component,\n props,\n secondArg,\n nextRenderLanes\n) {\n renderLanes = nextRenderLanes;\n currentlyRenderingFiber = workInProgress;\n workInProgress.memoizedState = null;\n workInProgress.updateQueue = null;\n workInProgress.lanes = 0;\n ReactSharedInternals.H =\n null === current || null === current.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate;\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n nextRenderLanes = Component(props, secondArg);\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n didScheduleRenderPhaseUpdateDuringThisPass &&\n (nextRenderLanes = renderWithHooksAgain(\n workInProgress,\n Component,\n props,\n secondArg\n ));\n finishRenderingHooks(current);\n return nextRenderLanes;\n}\nfunction finishRenderingHooks(current) {\n ReactSharedInternals.H = ContextOnlyDispatcher;\n var didRenderTooFewHooks = null !== currentHook && null !== currentHook.next;\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdate = !1;\n thenableIndexCounter = 0;\n thenableState = null;\n if (didRenderTooFewHooks) throw Error(formatProdErrorMessage(300));\n null === current ||\n didReceiveUpdate ||\n ((current = current.dependencies),\n null !== current &&\n checkIfContextChanged(current) &&\n (didReceiveUpdate = !0));\n}\nfunction renderWithHooksAgain(workInProgress, Component, props, secondArg) {\n currentlyRenderingFiber = workInProgress;\n var numberOfReRenders = 0;\n do {\n didScheduleRenderPhaseUpdateDuringThisPass && (thenableState = null);\n thenableIndexCounter = 0;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n if (25 <= numberOfReRenders) throw Error(formatProdErrorMessage(301));\n numberOfReRenders += 1;\n workInProgressHook = currentHook = null;\n if (null != workInProgress.updateQueue) {\n var children = workInProgress.updateQueue;\n children.lastEffect = null;\n children.events = null;\n children.stores = null;\n null != children.memoCache && (children.memoCache.index = 0);\n }\n ReactSharedInternals.H = HooksDispatcherOnRerender;\n children = Component(props, secondArg);\n } while (didScheduleRenderPhaseUpdateDuringThisPass);\n return children;\n}\nfunction TransitionAwareHostComponent() {\n var dispatcher = ReactSharedInternals.H,\n maybeThenable = dispatcher.useState()[0];\n maybeThenable =\n \"function\" === typeof maybeThenable.then\n ? useThenable(maybeThenable)\n : maybeThenable;\n dispatcher = dispatcher.useState()[0];\n (null !== currentHook ? currentHook.memoizedState : null) !== dispatcher &&\n (currentlyRenderingFiber.flags |= 1024);\n return maybeThenable;\n}\nfunction checkDidRenderIdHook() {\n var didRenderIdHook = 0 !== localIdCounter;\n localIdCounter = 0;\n return didRenderIdHook;\n}\nfunction bailoutHooks(current, workInProgress, lanes) {\n workInProgress.updateQueue = current.updateQueue;\n workInProgress.flags &= -2053;\n current.lanes &= ~lanes;\n}\nfunction resetHooksOnUnwind(workInProgress) {\n if (didScheduleRenderPhaseUpdate) {\n for (\n workInProgress = workInProgress.memoizedState;\n null !== workInProgress;\n\n ) {\n var queue = workInProgress.queue;\n null !== queue && (queue.pending = null);\n workInProgress = workInProgress.next;\n }\n didScheduleRenderPhaseUpdate = !1;\n }\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n thenableIndexCounter = localIdCounter = 0;\n thenableState = null;\n}\nfunction mountWorkInProgressHook() {\n var hook = {\n memoizedState: null,\n baseState: null,\n baseQueue: null,\n queue: null,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook = hook)\n : (workInProgressHook = workInProgressHook.next = hook);\n return workInProgressHook;\n}\nfunction updateWorkInProgressHook() {\n if (null === currentHook) {\n var nextCurrentHook = currentlyRenderingFiber.alternate;\n nextCurrentHook =\n null !== nextCurrentHook ? nextCurrentHook.memoizedState : null;\n } else nextCurrentHook = currentHook.next;\n var nextWorkInProgressHook =\n null === workInProgressHook\n ? currentlyRenderingFiber.memoizedState\n : workInProgressHook.next;\n if (null !== nextWorkInProgressHook)\n (workInProgressHook = nextWorkInProgressHook),\n (currentHook = nextCurrentHook);\n else {\n if (null === nextCurrentHook) {\n if (null === currentlyRenderingFiber.alternate)\n throw Error(formatProdErrorMessage(467));\n throw Error(formatProdErrorMessage(310));\n }\n currentHook = nextCurrentHook;\n nextCurrentHook = {\n memoizedState: currentHook.memoizedState,\n baseState: currentHook.baseState,\n baseQueue: currentHook.baseQueue,\n queue: currentHook.queue,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook =\n nextCurrentHook)\n : (workInProgressHook = workInProgressHook.next = nextCurrentHook);\n }\n return workInProgressHook;\n}\nfunction createFunctionComponentUpdateQueue() {\n return { lastEffect: null, events: null, stores: null, memoCache: null };\n}\nfunction useThenable(thenable) {\n var index = thenableIndexCounter;\n thenableIndexCounter += 1;\n null === thenableState && (thenableState = []);\n thenable = trackUsedThenable(thenableState, thenable, index);\n index = currentlyRenderingFiber;\n null ===\n (null === workInProgressHook\n ? index.memoizedState\n : workInProgressHook.next) &&\n ((index = index.alternate),\n (ReactSharedInternals.H =\n null === index || null === index.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate));\n return thenable;\n}\nfunction use(usable) {\n if (null !== usable && \"object\" === typeof usable) {\n if (\"function\" === typeof usable.then) return useThenable(usable);\n if (usable.$$typeof === REACT_CONTEXT_TYPE) return readContext(usable);\n }\n throw Error(formatProdErrorMessage(438, String(usable)));\n}\nfunction useMemoCache(size) {\n var memoCache = null,\n updateQueue = currentlyRenderingFiber.updateQueue;\n null !== updateQueue && (memoCache = updateQueue.memoCache);\n if (null == memoCache) {\n var current = currentlyRenderingFiber.alternate;\n null !== current &&\n ((current = current.updateQueue),\n null !== current &&\n ((current = current.memoCache),\n null != current &&\n (memoCache = {\n data: current.data.map(function (array) {\n return array.slice();\n }),\n index: 0\n })));\n }\n null == memoCache && (memoCache = { data: [], index: 0 });\n null === updateQueue &&\n ((updateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = updateQueue));\n updateQueue.memoCache = memoCache;\n updateQueue = memoCache.data[memoCache.index];\n if (void 0 === updateQueue)\n for (\n updateQueue = memoCache.data[memoCache.index] = Array(size), current = 0;\n current < size;\n current++\n )\n updateQueue[current] = REACT_MEMO_CACHE_SENTINEL;\n memoCache.index++;\n return updateQueue;\n}\nfunction basicStateReducer(state, action) {\n return \"function\" === typeof action ? action(state) : action;\n}\nfunction updateReducer(reducer) {\n var hook = updateWorkInProgressHook();\n return updateReducerImpl(hook, currentHook, reducer);\n}\nfunction updateReducerImpl(hook, current, reducer) {\n var queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var baseQueue = hook.baseQueue,\n pendingQueue = queue.pending;\n if (null !== pendingQueue) {\n if (null !== baseQueue) {\n var baseFirst = baseQueue.next;\n baseQueue.next = pendingQueue.next;\n pendingQueue.next = baseFirst;\n }\n current.baseQueue = baseQueue = pendingQueue;\n queue.pending = null;\n }\n pendingQueue = hook.baseState;\n if (null === baseQueue) hook.memoizedState = pendingQueue;\n else {\n current = baseQueue.next;\n var newBaseQueueFirst = (baseFirst = null),\n newBaseQueueLast = null,\n update = current,\n didReadFromEntangledAsyncAction$60 = !1;\n do {\n var updateLane = update.lane & -536870913;\n if (\n updateLane !== update.lane\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n var revertLane = update.revertLane;\n if (0 === revertLane)\n null !== newBaseQueueLast &&\n (newBaseQueueLast = newBaseQueueLast.next =\n {\n lane: 0,\n revertLane: 0,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n else if ((renderLanes & revertLane) === revertLane) {\n update = update.next;\n revertLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n continue;\n } else\n (updateLane = {\n lane: 0,\n revertLane: update.revertLane,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = updateLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = updateLane),\n (currentlyRenderingFiber.lanes |= revertLane),\n (workInProgressRootSkippedLanes |= revertLane);\n updateLane = update.action;\n shouldDoubleInvokeUserFnsInHooksDEV &&\n reducer(pendingQueue, updateLane);\n pendingQueue = update.hasEagerState\n ? update.eagerState\n : reducer(pendingQueue, updateLane);\n } else\n (revertLane = {\n lane: updateLane,\n revertLane: update.revertLane,\n gesture: update.gesture,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = revertLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = revertLane),\n (currentlyRenderingFiber.lanes |= updateLane),\n (workInProgressRootSkippedLanes |= updateLane);\n update = update.next;\n } while (null !== update && update !== current);\n null === newBaseQueueLast\n ? (baseFirst = pendingQueue)\n : (newBaseQueueLast.next = newBaseQueueFirst);\n if (\n !objectIs(pendingQueue, hook.memoizedState) &&\n ((didReceiveUpdate = !0),\n didReadFromEntangledAsyncAction$60 &&\n ((reducer = currentEntangledActionThenable), null !== reducer))\n )\n throw reducer;\n hook.memoizedState = pendingQueue;\n hook.baseState = baseFirst;\n hook.baseQueue = newBaseQueueLast;\n queue.lastRenderedState = pendingQueue;\n }\n null === baseQueue && (queue.lanes = 0);\n return [hook.memoizedState, queue.dispatch];\n}\nfunction rerenderReducer(reducer) {\n var hook = updateWorkInProgressHook(),\n queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var dispatch = queue.dispatch,\n lastRenderPhaseUpdate = queue.pending,\n newState = hook.memoizedState;\n if (null !== lastRenderPhaseUpdate) {\n queue.pending = null;\n var update = (lastRenderPhaseUpdate = lastRenderPhaseUpdate.next);\n do (newState = reducer(newState, update.action)), (update = update.next);\n while (update !== lastRenderPhaseUpdate);\n objectIs(newState, hook.memoizedState) || (didReceiveUpdate = !0);\n hook.memoizedState = newState;\n null === hook.baseQueue && (hook.baseState = newState);\n queue.lastRenderedState = newState;\n }\n return [newState, dispatch];\n}\nfunction updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = updateWorkInProgressHook(),\n isHydrating$jscomp$0 = isHydrating;\n if (isHydrating$jscomp$0) {\n if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else getServerSnapshot = getSnapshot();\n var snapshotChanged = !objectIs(\n (currentHook || hook).memoizedState,\n getServerSnapshot\n );\n snapshotChanged &&\n ((hook.memoizedState = getServerSnapshot), (didReceiveUpdate = !0));\n hook = hook.queue;\n updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [\n subscribe\n ]);\n if (\n hook.getSnapshot !== getSnapshot ||\n snapshotChanged ||\n (null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1)\n ) {\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n hook,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));\n isHydrating$jscomp$0 ||\n 0 !== (renderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n return getServerSnapshot;\n}\nfunction pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {\n fiber.flags |= 16384;\n fiber = { getSnapshot: getSnapshot, value: renderedSnapshot };\n getSnapshot = currentlyRenderingFiber.updateQueue;\n null === getSnapshot\n ? ((getSnapshot = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = getSnapshot),\n (getSnapshot.stores = [fiber]))\n : ((renderedSnapshot = getSnapshot.stores),\n null === renderedSnapshot\n ? (getSnapshot.stores = [fiber])\n : renderedSnapshot.push(fiber));\n}\nfunction updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {\n inst.value = nextSnapshot;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n}\nfunction subscribeToStore(fiber, inst, subscribe) {\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n });\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction forceStoreRerender(fiber) {\n var root = enqueueConcurrentRenderForLane(fiber, 2);\n null !== root && scheduleUpdateOnFiber(root, fiber, 2);\n}\nfunction mountStateImpl(initialState) {\n var hook = mountWorkInProgressHook();\n if (\"function\" === typeof initialState) {\n var initialStateInitializer = initialState;\n initialState = initialStateInitializer();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n initialStateInitializer();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n }\n hook.memoizedState = hook.baseState = initialState;\n hook.queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState\n };\n return hook;\n}\nfunction updateOptimisticImpl(hook, current, passthrough, reducer) {\n hook.baseState = passthrough;\n return updateReducerImpl(\n hook,\n currentHook,\n \"function\" === typeof reducer ? reducer : basicStateReducer\n );\n}\nfunction dispatchActionState(\n fiber,\n actionQueue,\n setPendingState,\n setState,\n payload\n) {\n if (isRenderPhaseUpdate(fiber)) throw Error(formatProdErrorMessage(485));\n fiber = actionQueue.action;\n if (null !== fiber) {\n var actionNode = {\n payload: payload,\n action: fiber,\n next: null,\n isTransition: !0,\n status: \"pending\",\n value: null,\n reason: null,\n listeners: [],\n then: function (listener) {\n actionNode.listeners.push(listener);\n }\n };\n null !== ReactSharedInternals.T\n ? setPendingState(!0)\n : (actionNode.isTransition = !1);\n setState(actionNode);\n setPendingState = actionQueue.pending;\n null === setPendingState\n ? ((actionNode.next = actionQueue.pending = actionNode),\n runActionStateAction(actionQueue, actionNode))\n : ((actionNode.next = setPendingState.next),\n (actionQueue.pending = setPendingState.next = actionNode));\n }\n}\nfunction runActionStateAction(actionQueue, node) {\n var action = node.action,\n payload = node.payload,\n prevState = actionQueue.state;\n if (node.isTransition) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = action(prevState, payload),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n handleActionReturnValue(actionQueue, node, returnValue);\n } catch (error) {\n onActionError(actionQueue, node, error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n } else\n try {\n (prevTransition = action(prevState, payload)),\n handleActionReturnValue(actionQueue, node, prevTransition);\n } catch (error$66) {\n onActionError(actionQueue, node, error$66);\n }\n}\nfunction handleActionReturnValue(actionQueue, node, returnValue) {\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ? returnValue.then(\n function (nextState) {\n onActionSuccess(actionQueue, node, nextState);\n },\n function (error) {\n return onActionError(actionQueue, node, error);\n }\n )\n : onActionSuccess(actionQueue, node, returnValue);\n}\nfunction onActionSuccess(actionQueue, actionNode, nextState) {\n actionNode.status = \"fulfilled\";\n actionNode.value = nextState;\n notifyActionListeners(actionNode);\n actionQueue.state = nextState;\n actionNode = actionQueue.pending;\n null !== actionNode &&\n ((nextState = actionNode.next),\n nextState === actionNode\n ? (actionQueue.pending = null)\n : ((nextState = nextState.next),\n (actionNode.next = nextState),\n runActionStateAction(actionQueue, nextState)));\n}\nfunction onActionError(actionQueue, actionNode, error) {\n var last = actionQueue.pending;\n actionQueue.pending = null;\n if (null !== last) {\n last = last.next;\n do\n (actionNode.status = \"rejected\"),\n (actionNode.reason = error),\n notifyActionListeners(actionNode),\n (actionNode = actionNode.next);\n while (actionNode !== last);\n }\n actionQueue.action = null;\n}\nfunction notifyActionListeners(actionNode) {\n actionNode = actionNode.listeners;\n for (var i = 0; i < actionNode.length; i++) (0, actionNode[i])();\n}\nfunction actionStateReducer(oldState, newState) {\n return newState;\n}\nfunction mountActionState(action, initialStateProp) {\n if (isHydrating) {\n var ssrFormState = workInProgressRoot.formState;\n if (null !== ssrFormState) {\n a: {\n var JSCompiler_inline_result = currentlyRenderingFiber;\n if (isHydrating) {\n if (nextHydratableInstance) {\n b: {\n var JSCompiler_inline_result$jscomp$0 = nextHydratableInstance;\n for (\n var inRootOrSingleton = rootOrSingletonContext;\n 8 !== JSCompiler_inline_result$jscomp$0.nodeType;\n\n ) {\n if (!inRootOrSingleton) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n JSCompiler_inline_result$jscomp$0 = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n if (null === JSCompiler_inline_result$jscomp$0) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n }\n inRootOrSingleton = JSCompiler_inline_result$jscomp$0.data;\n JSCompiler_inline_result$jscomp$0 =\n \"F!\" === inRootOrSingleton || \"F\" === inRootOrSingleton\n ? JSCompiler_inline_result$jscomp$0\n : null;\n }\n if (JSCompiler_inline_result$jscomp$0) {\n nextHydratableInstance = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n JSCompiler_inline_result =\n \"F!\" === JSCompiler_inline_result$jscomp$0.data;\n break a;\n }\n }\n throwOnHydrationMismatch(JSCompiler_inline_result);\n }\n JSCompiler_inline_result = !1;\n }\n JSCompiler_inline_result && (initialStateProp = ssrFormState[0]);\n }\n }\n ssrFormState = mountWorkInProgressHook();\n ssrFormState.memoizedState = ssrFormState.baseState = initialStateProp;\n JSCompiler_inline_result = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: actionStateReducer,\n lastRenderedState: initialStateProp\n };\n ssrFormState.queue = JSCompiler_inline_result;\n ssrFormState = dispatchSetState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result\n );\n JSCompiler_inline_result.dispatch = ssrFormState;\n JSCompiler_inline_result = mountStateImpl(!1);\n inRootOrSingleton = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !1,\n JSCompiler_inline_result.queue\n );\n JSCompiler_inline_result = mountWorkInProgressHook();\n JSCompiler_inline_result$jscomp$0 = {\n state: initialStateProp,\n dispatch: null,\n action: action,\n pending: null\n };\n JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;\n ssrFormState = dispatchActionState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result$jscomp$0,\n inRootOrSingleton,\n ssrFormState\n );\n JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;\n JSCompiler_inline_result.memoizedState = action;\n return [initialStateProp, ssrFormState, !1];\n}\nfunction updateActionState(action) {\n var stateHook = updateWorkInProgressHook();\n return updateActionStateImpl(stateHook, currentHook, action);\n}\nfunction updateActionStateImpl(stateHook, currentStateHook, action) {\n currentStateHook = updateReducerImpl(\n stateHook,\n currentStateHook,\n actionStateReducer\n )[0];\n stateHook = updateReducer(basicStateReducer)[0];\n if (\n \"object\" === typeof currentStateHook &&\n null !== currentStateHook &&\n \"function\" === typeof currentStateHook.then\n )\n try {\n var state = useThenable(currentStateHook);\n } catch (x) {\n if (x === SuspenseException) throw SuspenseActionException;\n throw x;\n }\n else state = currentStateHook;\n currentStateHook = updateWorkInProgressHook();\n var actionQueue = currentStateHook.queue,\n dispatch = actionQueue.dispatch;\n action !== currentStateHook.memoizedState &&\n ((currentlyRenderingFiber.flags |= 2048),\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n actionStateActionEffect.bind(null, actionQueue, action),\n null\n ));\n return [state, dispatch, stateHook];\n}\nfunction actionStateActionEffect(actionQueue, action) {\n actionQueue.action = action;\n}\nfunction rerenderActionState(action) {\n var stateHook = updateWorkInProgressHook(),\n currentStateHook = currentHook;\n if (null !== currentStateHook)\n return updateActionStateImpl(stateHook, currentStateHook, action);\n updateWorkInProgressHook();\n stateHook = stateHook.memoizedState;\n currentStateHook = updateWorkInProgressHook();\n var dispatch = currentStateHook.queue.dispatch;\n currentStateHook.memoizedState = action;\n return [stateHook, dispatch, !1];\n}\nfunction pushSimpleEffect(tag, inst, create, deps) {\n tag = { tag: tag, create: create, deps: deps, inst: inst, next: null };\n inst = currentlyRenderingFiber.updateQueue;\n null === inst &&\n ((inst = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = inst));\n create = inst.lastEffect;\n null === create\n ? (inst.lastEffect = tag.next = tag)\n : ((deps = create.next),\n (create.next = tag),\n (tag.next = deps),\n (inst.lastEffect = tag));\n return tag;\n}\nfunction updateRef() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = mountWorkInProgressHook();\n currentlyRenderingFiber.flags |= fiberFlags;\n hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n { destroy: void 0 },\n create,\n void 0 === deps ? null : deps\n );\n}\nfunction updateEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var inst = hook.memoizedState.inst;\n null !== currentHook &&\n null !== deps &&\n areHookInputsEqual(deps, currentHook.memoizedState.deps)\n ? (hook.memoizedState = pushSimpleEffect(hookFlags, inst, create, deps))\n : ((currentlyRenderingFiber.flags |= fiberFlags),\n (hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n inst,\n create,\n deps\n )));\n}\nfunction mountEffect(create, deps) {\n mountEffectImpl(8390656, 8, create, deps);\n}\nfunction updateEffect(create, deps) {\n updateEffectImpl(2048, 8, create, deps);\n}\nfunction useEffectEventImpl(payload) {\n currentlyRenderingFiber.flags |= 4;\n var componentUpdateQueue = currentlyRenderingFiber.updateQueue;\n if (null === componentUpdateQueue)\n (componentUpdateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = componentUpdateQueue),\n (componentUpdateQueue.events = [payload]);\n else {\n var events = componentUpdateQueue.events;\n null === events\n ? (componentUpdateQueue.events = [payload])\n : events.push(payload);\n }\n}\nfunction updateEvent(callback) {\n var ref = updateWorkInProgressHook().memoizedState;\n useEffectEventImpl({ ref: ref, nextImpl: callback });\n return function () {\n if (0 !== (executionContext & 2)) throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n}\nfunction updateInsertionEffect(create, deps) {\n return updateEffectImpl(4, 2, create, deps);\n}\nfunction updateLayoutEffect(create, deps) {\n return updateEffectImpl(4, 4, create, deps);\n}\nfunction imperativeHandleEffect(create, ref) {\n if (\"function\" === typeof ref) {\n create = create();\n var refCleanup = ref(create);\n return function () {\n \"function\" === typeof refCleanup ? refCleanup() : ref(null);\n };\n }\n if (null !== ref && void 0 !== ref)\n return (\n (create = create()),\n (ref.current = create),\n function () {\n ref.current = null;\n }\n );\n}\nfunction updateImperativeHandle(ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n updateEffectImpl(4, 4, imperativeHandleEffect.bind(null, create, ref), deps);\n}\nfunction mountDebugValue() {}\nfunction updateCallback(callback, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n hook.memoizedState = [callback, deps];\n return callback;\n}\nfunction updateMemo(nextCreate, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n prevState = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [prevState, deps];\n return prevState;\n}\nfunction mountDeferredValueImpl(hook, value, initialValue) {\n if (\n void 0 === initialValue ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (hook.memoizedState = value);\n hook.memoizedState = initialValue;\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return initialValue;\n}\nfunction updateDeferredValueImpl(hook, prevValue, value, initialValue) {\n if (objectIs(value, prevValue)) return value;\n if (null !== currentTreeHiddenStackCursor.current)\n return (\n (hook = mountDeferredValueImpl(hook, value, initialValue)),\n objectIs(hook, prevValue) || (didReceiveUpdate = !0),\n hook\n );\n if (\n 0 === (renderLanes & 42) ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (didReceiveUpdate = !0), (hook.memoizedState = value);\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return prevValue;\n}\nfunction startTransition(fiber, queue, pendingState, finishedState, callback) {\n var previousPriority = ReactDOMSharedInternals.p;\n ReactDOMSharedInternals.p =\n 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8;\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n dispatchOptimisticSetState(fiber, !1, queue, pendingState);\n try {\n var returnValue = callback(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n if (\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ) {\n var thenableForFinishedState = chainThenableValue(\n returnValue,\n finishedState\n );\n dispatchSetStateInternal(\n fiber,\n queue,\n thenableForFinishedState,\n requestUpdateLane(fiber)\n );\n } else\n dispatchSetStateInternal(\n fiber,\n queue,\n finishedState,\n requestUpdateLane(fiber)\n );\n } catch (error) {\n dispatchSetStateInternal(\n fiber,\n queue,\n { then: function () {}, status: \"rejected\", reason: error },\n requestUpdateLane()\n );\n } finally {\n (ReactDOMSharedInternals.p = previousPriority),\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n}\nfunction noop() {}\nfunction startHostTransition(formFiber, pendingState, action, formData) {\n if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));\n var queue = ensureFormComponentIsStateful(formFiber).queue;\n startTransition(\n formFiber,\n queue,\n pendingState,\n sharedNotPendingObject,\n null === action\n ? noop\n : function () {\n requestFormReset$1(formFiber);\n return action(formData);\n }\n );\n}\nfunction ensureFormComponentIsStateful(formFiber) {\n var existingStateHook = formFiber.memoizedState;\n if (null !== existingStateHook) return existingStateHook;\n existingStateHook = {\n memoizedState: sharedNotPendingObject,\n baseState: sharedNotPendingObject,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: sharedNotPendingObject\n },\n next: null\n };\n var initialResetState = {};\n existingStateHook.next = {\n memoizedState: initialResetState,\n baseState: initialResetState,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialResetState\n },\n next: null\n };\n formFiber.memoizedState = existingStateHook;\n formFiber = formFiber.alternate;\n null !== formFiber && (formFiber.memoizedState = existingStateHook);\n return existingStateHook;\n}\nfunction requestFormReset$1(formFiber) {\n var stateHook = ensureFormComponentIsStateful(formFiber);\n null === stateHook.next && (stateHook = formFiber.alternate.memoizedState);\n dispatchSetStateInternal(\n formFiber,\n stateHook.next.queue,\n {},\n requestUpdateLane()\n );\n}\nfunction useHostTransitionStatus() {\n return readContext(HostTransitionContext);\n}\nfunction updateId() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction updateRefresh() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction refreshCache(fiber) {\n for (var provider = fiber.return; null !== provider; ) {\n switch (provider.tag) {\n case 24:\n case 3:\n var lane = requestUpdateLane();\n fiber = createUpdate(lane);\n var root$69 = enqueueUpdate(provider, fiber, lane);\n null !== root$69 &&\n (scheduleUpdateOnFiber(root$69, provider, lane),\n entangleTransitions(root$69, provider, lane));\n provider = { cache: createCache() };\n fiber.payload = provider;\n return;\n }\n provider = provider.return;\n }\n}\nfunction dispatchReducerAction(fiber, queue, action) {\n var lane = requestUpdateLane();\n action = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n isRenderPhaseUpdate(fiber)\n ? enqueueRenderPhaseUpdate(queue, action)\n : ((action = enqueueConcurrentHookUpdate(fiber, queue, action, lane)),\n null !== action &&\n (scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane)));\n}\nfunction dispatchSetState(fiber, queue, action) {\n var lane = requestUpdateLane();\n dispatchSetStateInternal(fiber, queue, action, lane);\n}\nfunction dispatchSetStateInternal(fiber, queue, action, lane) {\n var update = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) enqueueRenderPhaseUpdate(queue, update);\n else {\n var alternate = fiber.alternate;\n if (\n 0 === fiber.lanes &&\n (null === alternate || 0 === alternate.lanes) &&\n ((alternate = queue.lastRenderedReducer), null !== alternate)\n )\n try {\n var currentState = queue.lastRenderedState,\n eagerState = alternate(currentState, action);\n update.hasEagerState = !0;\n update.eagerState = eagerState;\n if (objectIs(eagerState, currentState))\n return (\n enqueueUpdate$1(fiber, queue, update, 0),\n null === workInProgressRoot && finishQueueingConcurrentUpdates(),\n !1\n );\n } catch (error) {\n } finally {\n }\n action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n if (null !== action)\n return (\n scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane),\n !0\n );\n }\n return !1;\n}\nfunction dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {\n action = {\n lane: 2,\n revertLane: requestTransitionLane(),\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) {\n if (throwIfDuringRender) throw Error(formatProdErrorMessage(479));\n } else\n (throwIfDuringRender = enqueueConcurrentHookUpdate(\n fiber,\n queue,\n action,\n 2\n )),\n null !== throwIfDuringRender &&\n scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);\n}\nfunction isRenderPhaseUpdate(fiber) {\n var alternate = fiber.alternate;\n return (\n fiber === currentlyRenderingFiber ||\n (null !== alternate && alternate === currentlyRenderingFiber)\n );\n}\nfunction enqueueRenderPhaseUpdate(queue, update) {\n didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =\n !0;\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n}\nfunction entangleTransitionUpdate(root, queue, lane) {\n if (0 !== (lane & 4194048)) {\n var queueLanes = queue.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n queue.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nvar ContextOnlyDispatcher = {\n readContext: readContext,\n use: use,\n useCallback: throwInvalidHookError,\n useContext: throwInvalidHookError,\n useEffect: throwInvalidHookError,\n useImperativeHandle: throwInvalidHookError,\n useLayoutEffect: throwInvalidHookError,\n useInsertionEffect: throwInvalidHookError,\n useMemo: throwInvalidHookError,\n useReducer: throwInvalidHookError,\n useRef: throwInvalidHookError,\n useState: throwInvalidHookError,\n useDebugValue: throwInvalidHookError,\n useDeferredValue: throwInvalidHookError,\n useTransition: throwInvalidHookError,\n useSyncExternalStore: throwInvalidHookError,\n useId: throwInvalidHookError,\n useHostTransitionStatus: throwInvalidHookError,\n useFormState: throwInvalidHookError,\n useActionState: throwInvalidHookError,\n useOptimistic: throwInvalidHookError,\n useMemoCache: throwInvalidHookError,\n useCacheRefresh: throwInvalidHookError\n};\nContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;\nvar HooksDispatcherOnMount = {\n readContext: readContext,\n use: use,\n useCallback: function (callback, deps) {\n mountWorkInProgressHook().memoizedState = [\n callback,\n void 0 === deps ? null : deps\n ];\n return callback;\n },\n useContext: readContext,\n useEffect: mountEffect,\n useImperativeHandle: function (ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n mountEffectImpl(\n 4194308,\n 4,\n imperativeHandleEffect.bind(null, create, ref),\n deps\n );\n },\n useLayoutEffect: function (create, deps) {\n return mountEffectImpl(4194308, 4, create, deps);\n },\n useInsertionEffect: function (create, deps) {\n mountEffectImpl(4, 2, create, deps);\n },\n useMemo: function (nextCreate, deps) {\n var hook = mountWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var nextValue = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [nextValue, deps];\n return nextValue;\n },\n useReducer: function (reducer, initialArg, init) {\n var hook = mountWorkInProgressHook();\n if (void 0 !== init) {\n var initialState = init(initialArg);\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n init(initialArg);\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n } else initialState = initialArg;\n hook.memoizedState = hook.baseState = initialState;\n reducer = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: reducer,\n lastRenderedState: initialState\n };\n hook.queue = reducer;\n reducer = reducer.dispatch = dispatchReducerAction.bind(\n null,\n currentlyRenderingFiber,\n reducer\n );\n return [hook.memoizedState, reducer];\n },\n useRef: function (initialValue) {\n var hook = mountWorkInProgressHook();\n initialValue = { current: initialValue };\n return (hook.memoizedState = initialValue);\n },\n useState: function (initialState) {\n initialState = mountStateImpl(initialState);\n var queue = initialState.queue,\n dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);\n queue.dispatch = dispatch;\n return [initialState.memoizedState, dispatch];\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = mountWorkInProgressHook();\n return mountDeferredValueImpl(hook, value, initialValue);\n },\n useTransition: function () {\n var stateHook = mountStateImpl(!1);\n stateHook = startTransition.bind(\n null,\n currentlyRenderingFiber,\n stateHook.queue,\n !0,\n !1\n );\n mountWorkInProgressHook().memoizedState = stateHook;\n return [!1, stateHook];\n },\n useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = mountWorkInProgressHook();\n if (isHydrating) {\n if (void 0 === getServerSnapshot)\n throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else {\n getServerSnapshot = getSnapshot();\n if (null === workInProgressRoot)\n throw Error(formatProdErrorMessage(349));\n 0 !== (workInProgressRootRenderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n hook.memoizedState = getServerSnapshot;\n var inst = { value: getServerSnapshot, getSnapshot: getSnapshot };\n hook.queue = inst;\n mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [\n subscribe\n ]);\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n inst,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n return getServerSnapshot;\n },\n useId: function () {\n var hook = mountWorkInProgressHook(),\n identifierPrefix = workInProgressRoot.identifierPrefix;\n if (isHydrating) {\n var JSCompiler_inline_result = treeContextOverflow;\n var idWithLeadingBit = treeContextId;\n JSCompiler_inline_result =\n (\n idWithLeadingBit & ~(1 << (32 - clz32(idWithLeadingBit) - 1))\n ).toString(32) + JSCompiler_inline_result;\n identifierPrefix =\n \"_\" + identifierPrefix + \"R_\" + JSCompiler_inline_result;\n JSCompiler_inline_result = localIdCounter++;\n 0 < JSCompiler_inline_result &&\n (identifierPrefix += \"H\" + JSCompiler_inline_result.toString(32));\n identifierPrefix += \"_\";\n } else\n (JSCompiler_inline_result = globalClientIdCounter++),\n (identifierPrefix =\n \"_\" +\n identifierPrefix +\n \"r_\" +\n JSCompiler_inline_result.toString(32) +\n \"_\");\n return (hook.memoizedState = identifierPrefix);\n },\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: mountActionState,\n useActionState: mountActionState,\n useOptimistic: function (passthrough) {\n var hook = mountWorkInProgressHook();\n hook.memoizedState = hook.baseState = passthrough;\n var queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: null,\n lastRenderedState: null\n };\n hook.queue = queue;\n hook = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !0,\n queue\n );\n queue.dispatch = hook;\n return [passthrough, hook];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: function () {\n return (mountWorkInProgressHook().memoizedState = refreshCache.bind(\n null,\n currentlyRenderingFiber\n ));\n },\n useEffectEvent: function (callback) {\n var hook = mountWorkInProgressHook(),\n ref = { impl: callback };\n hook.memoizedState = ref;\n return function () {\n if (0 !== (executionContext & 2))\n throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n }\n },\n HooksDispatcherOnUpdate = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: updateReducer,\n useRef: updateRef,\n useState: function () {\n return updateReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = updateReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: updateActionState,\n useActionState: updateActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n };\nHooksDispatcherOnUpdate.useEffectEvent = updateEvent;\nvar HooksDispatcherOnRerender = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: rerenderReducer,\n useRef: updateRef,\n useState: function () {\n return rerenderReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return null === currentHook\n ? mountDeferredValueImpl(hook, value, initialValue)\n : updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = rerenderReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: rerenderActionState,\n useActionState: rerenderActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n if (null !== currentHook)\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n hook.baseState = passthrough;\n return [passthrough, hook.queue.dispatch];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n};\nHooksDispatcherOnRerender.useEffectEvent = updateEvent;\nfunction applyDerivedStateFromProps(\n workInProgress,\n ctor,\n getDerivedStateFromProps,\n nextProps\n) {\n ctor = workInProgress.memoizedState;\n getDerivedStateFromProps = getDerivedStateFromProps(nextProps, ctor);\n getDerivedStateFromProps =\n null === getDerivedStateFromProps || void 0 === getDerivedStateFromProps\n ? ctor\n : assign({}, ctor, getDerivedStateFromProps);\n workInProgress.memoizedState = getDerivedStateFromProps;\n 0 === workInProgress.lanes &&\n (workInProgress.updateQueue.baseState = getDerivedStateFromProps);\n}\nvar classComponentUpdater = {\n enqueueSetState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueReplaceState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 1;\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueForceUpdate: function (inst, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 2;\n void 0 !== callback && null !== callback && (update.callback = callback);\n callback = enqueueUpdate(inst, update, lane);\n null !== callback &&\n (scheduleUpdateOnFiber(callback, inst, lane),\n entangleTransitions(callback, inst, lane));\n }\n};\nfunction checkShouldComponentUpdate(\n workInProgress,\n ctor,\n oldProps,\n newProps,\n oldState,\n newState,\n nextContext\n) {\n workInProgress = workInProgress.stateNode;\n return \"function\" === typeof workInProgress.shouldComponentUpdate\n ? workInProgress.shouldComponentUpdate(newProps, newState, nextContext)\n : ctor.prototype && ctor.prototype.isPureReactComponent\n ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)\n : !0;\n}\nfunction callComponentWillReceiveProps(\n workInProgress,\n instance,\n newProps,\n nextContext\n) {\n workInProgress = instance.state;\n \"function\" === typeof instance.componentWillReceiveProps &&\n instance.componentWillReceiveProps(newProps, nextContext);\n \"function\" === typeof instance.UNSAFE_componentWillReceiveProps &&\n instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);\n instance.state !== workInProgress &&\n classComponentUpdater.enqueueReplaceState(instance, instance.state, null);\n}\nfunction resolveClassComponentProps(Component, baseProps) {\n var newProps = baseProps;\n if (\"ref\" in baseProps) {\n newProps = {};\n for (var propName in baseProps)\n \"ref\" !== propName && (newProps[propName] = baseProps[propName]);\n }\n if ((Component = Component.defaultProps)) {\n newProps === baseProps && (newProps = assign({}, newProps));\n for (var propName$73 in Component)\n void 0 === newProps[propName$73] &&\n (newProps[propName$73] = Component[propName$73]);\n }\n return newProps;\n}\nfunction defaultOnUncaughtError(error) {\n reportGlobalError(error);\n}\nfunction defaultOnCaughtError(error) {\n console.error(error);\n}\nfunction defaultOnRecoverableError(error) {\n reportGlobalError(error);\n}\nfunction logUncaughtError(root, errorInfo) {\n try {\n var onUncaughtError = root.onUncaughtError;\n onUncaughtError(errorInfo.value, { componentStack: errorInfo.stack });\n } catch (e$74) {\n setTimeout(function () {\n throw e$74;\n });\n }\n}\nfunction logCaughtError(root, boundary, errorInfo) {\n try {\n var onCaughtError = root.onCaughtError;\n onCaughtError(errorInfo.value, {\n componentStack: errorInfo.stack,\n errorBoundary: 1 === boundary.tag ? boundary.stateNode : null\n });\n } catch (e$75) {\n setTimeout(function () {\n throw e$75;\n });\n }\n}\nfunction createRootErrorUpdate(root, errorInfo, lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n lane.payload = { element: null };\n lane.callback = function () {\n logUncaughtError(root, errorInfo);\n };\n return lane;\n}\nfunction createClassErrorUpdate(lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n return lane;\n}\nfunction initializeClassErrorUpdate(update, root, fiber, errorInfo) {\n var getDerivedStateFromError = fiber.type.getDerivedStateFromError;\n if (\"function\" === typeof getDerivedStateFromError) {\n var error = errorInfo.value;\n update.payload = function () {\n return getDerivedStateFromError(error);\n };\n update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n };\n }\n var inst = fiber.stateNode;\n null !== inst &&\n \"function\" === typeof inst.componentDidCatch &&\n (update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n \"function\" !== typeof getDerivedStateFromError &&\n (null === legacyErrorBoundariesThatAlreadyFailed\n ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this]))\n : legacyErrorBoundariesThatAlreadyFailed.add(this));\n var stack = errorInfo.stack;\n this.componentDidCatch(errorInfo.value, {\n componentStack: null !== stack ? stack : \"\"\n });\n });\n}\nfunction throwException(\n root,\n returnFiber,\n sourceFiber,\n value,\n rootRenderLanes\n) {\n sourceFiber.flags |= 32768;\n if (\n null !== value &&\n \"object\" === typeof value &&\n \"function\" === typeof value.then\n ) {\n returnFiber = sourceFiber.alternate;\n null !== returnFiber &&\n propagateParentContextChanges(\n returnFiber,\n sourceFiber,\n rootRenderLanes,\n !0\n );\n sourceFiber = suspenseHandlerStackCursor.current;\n if (null !== sourceFiber) {\n switch (sourceFiber.tag) {\n case 31:\n case 13:\n return (\n null === shellBoundary\n ? renderDidSuspendDelayIfPossible()\n : null === sourceFiber.alternate &&\n 0 === workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 3),\n (sourceFiber.flags &= -257),\n (sourceFiber.flags |= 65536),\n (sourceFiber.lanes = rootRenderLanes),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? (sourceFiber.updateQueue = new Set([value]))\n : returnFiber.add(value),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n case 22:\n return (\n (sourceFiber.flags |= 65536),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? ((returnFiber = {\n transitions: null,\n markerInstances: null,\n retryQueue: new Set([value])\n }),\n (sourceFiber.updateQueue = returnFiber))\n : ((sourceFiber = returnFiber.retryQueue),\n null === sourceFiber\n ? (returnFiber.retryQueue = new Set([value]))\n : sourceFiber.add(value)),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n }\n throw Error(formatProdErrorMessage(435, sourceFiber.tag));\n }\n attachPingListener(root, value, rootRenderLanes);\n renderDidSuspendDelayIfPossible();\n return !1;\n }\n if (isHydrating)\n return (\n (returnFiber = suspenseHandlerStackCursor.current),\n null !== returnFiber\n ? (0 === (returnFiber.flags & 65536) && (returnFiber.flags |= 256),\n (returnFiber.flags |= 65536),\n (returnFiber.lanes = rootRenderLanes),\n value !== HydrationMismatchException &&\n ((root = Error(formatProdErrorMessage(422), { cause: value })),\n queueHydrationError(createCapturedValueAtFiber(root, sourceFiber))))\n : (value !== HydrationMismatchException &&\n ((returnFiber = Error(formatProdErrorMessage(423), {\n cause: value\n })),\n queueHydrationError(\n createCapturedValueAtFiber(returnFiber, sourceFiber)\n )),\n (root = root.current.alternate),\n (root.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (root.lanes |= rootRenderLanes),\n (value = createCapturedValueAtFiber(value, sourceFiber)),\n (rootRenderLanes = createRootErrorUpdate(\n root.stateNode,\n value,\n rootRenderLanes\n )),\n enqueueCapturedUpdate(root, rootRenderLanes),\n 4 !== workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 2)),\n !1\n );\n var wrapperError = Error(formatProdErrorMessage(520), { cause: value });\n wrapperError = createCapturedValueAtFiber(wrapperError, sourceFiber);\n null === workInProgressRootConcurrentErrors\n ? (workInProgressRootConcurrentErrors = [wrapperError])\n : workInProgressRootConcurrentErrors.push(wrapperError);\n 4 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2);\n if (null === returnFiber) return !0;\n value = createCapturedValueAtFiber(value, sourceFiber);\n sourceFiber = returnFiber;\n do {\n switch (sourceFiber.tag) {\n case 3:\n return (\n (sourceFiber.flags |= 65536),\n (root = rootRenderLanes & -rootRenderLanes),\n (sourceFiber.lanes |= root),\n (root = createRootErrorUpdate(sourceFiber.stateNode, value, root)),\n enqueueCapturedUpdate(sourceFiber, root),\n !1\n );\n case 1:\n if (\n ((returnFiber = sourceFiber.type),\n (wrapperError = sourceFiber.stateNode),\n 0 === (sourceFiber.flags & 128) &&\n (\"function\" === typeof returnFiber.getDerivedStateFromError ||\n (null !== wrapperError &&\n \"function\" === typeof wrapperError.componentDidCatch &&\n (null === legacyErrorBoundariesThatAlreadyFailed ||\n !legacyErrorBoundariesThatAlreadyFailed.has(wrapperError)))))\n )\n return (\n (sourceFiber.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (sourceFiber.lanes |= rootRenderLanes),\n (rootRenderLanes = createClassErrorUpdate(rootRenderLanes)),\n initializeClassErrorUpdate(\n rootRenderLanes,\n root,\n sourceFiber,\n value\n ),\n enqueueCapturedUpdate(sourceFiber, rootRenderLanes),\n !1\n );\n }\n sourceFiber = sourceFiber.return;\n } while (null !== sourceFiber);\n return !1;\n}\nvar SelectiveHydrationException = Error(formatProdErrorMessage(461)),\n didReceiveUpdate = !1;\nfunction reconcileChildren(current, workInProgress, nextChildren, renderLanes) {\n workInProgress.child =\n null === current\n ? mountChildFibers(workInProgress, null, nextChildren, renderLanes)\n : reconcileChildFibers(\n workInProgress,\n current.child,\n nextChildren,\n renderLanes\n );\n}\nfunction updateForwardRef(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n Component = Component.render;\n var ref = workInProgress.ref;\n if (\"ref\" in nextProps) {\n var propsWithoutRef = {};\n for (var key in nextProps)\n \"ref\" !== key && (propsWithoutRef[key] = nextProps[key]);\n } else propsWithoutRef = nextProps;\n prepareToReadContext(workInProgress);\n nextProps = renderWithHooks(\n current,\n workInProgress,\n Component,\n propsWithoutRef,\n ref,\n renderLanes\n );\n key = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && key && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null === current) {\n var type = Component.type;\n if (\n \"function\" === typeof type &&\n !shouldConstruct(type) &&\n void 0 === type.defaultProps &&\n null === Component.compare\n )\n return (\n (workInProgress.tag = 15),\n (workInProgress.type = type),\n updateSimpleMemoComponent(\n current,\n workInProgress,\n type,\n nextProps,\n renderLanes\n )\n );\n current = createFiberFromTypeAndProps(\n Component.type,\n null,\n nextProps,\n workInProgress,\n workInProgress.mode,\n renderLanes\n );\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n }\n type = current.child;\n if (!checkScheduledUpdateOrContext(current, renderLanes)) {\n var prevProps = type.memoizedProps;\n Component = Component.compare;\n Component = null !== Component ? Component : shallowEqual;\n if (Component(prevProps, nextProps) && current.ref === workInProgress.ref)\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n }\n workInProgress.flags |= 1;\n current = createWorkInProgress(type, nextProps);\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n}\nfunction updateSimpleMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null !== current) {\n var prevProps = current.memoizedProps;\n if (\n shallowEqual(prevProps, nextProps) &&\n current.ref === workInProgress.ref\n )\n if (\n ((didReceiveUpdate = !1),\n (workInProgress.pendingProps = nextProps = prevProps),\n checkScheduledUpdateOrContext(current, renderLanes))\n )\n 0 !== (current.flags & 131072) && (didReceiveUpdate = !0);\n else\n return (\n (workInProgress.lanes = current.lanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n }\n return updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n );\n}\nfunction updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n nextProps\n) {\n var nextChildren = nextProps.children,\n prevState = null !== current ? current.memoizedState : null;\n null === current &&\n null === workInProgress.stateNode &&\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n if (\"hidden\" === nextProps.mode) {\n if (0 !== (workInProgress.flags & 128)) {\n prevState =\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes;\n if (null !== current) {\n nextProps = workInProgress.child = current.child;\n for (nextChildren = 0; null !== nextProps; )\n (nextChildren =\n nextChildren | nextProps.lanes | nextProps.childLanes),\n (nextProps = nextProps.sibling);\n nextProps = nextChildren & ~prevState;\n } else (nextProps = 0), (workInProgress.child = null);\n return deferHiddenOffscreenComponent(\n current,\n workInProgress,\n prevState,\n renderLanes,\n nextProps\n );\n }\n if (0 !== (renderLanes & 536870912))\n (workInProgress.memoizedState = { baseLanes: 0, cachePool: null }),\n null !== current &&\n pushTransition(\n workInProgress,\n null !== prevState ? prevState.cachePool : null\n ),\n null !== prevState\n ? pushHiddenContext(workInProgress, prevState)\n : reuseHiddenContextOnStack(),\n pushOffscreenSuspenseHandler(workInProgress);\n else\n return (\n (nextProps = workInProgress.lanes = 536870912),\n deferHiddenOffscreenComponent(\n current,\n workInProgress,\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes,\n renderLanes,\n nextProps\n )\n );\n } else\n null !== prevState\n ? (pushTransition(workInProgress, prevState.cachePool),\n pushHiddenContext(workInProgress, prevState),\n reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.memoizedState = null))\n : (null !== current && pushTransition(workInProgress, null),\n reuseHiddenContextOnStack(),\n reuseSuspenseHandlerOnStack(workInProgress));\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nfunction bailoutOffscreenComponent(current, workInProgress) {\n (null !== current && 22 === current.tag) ||\n null !== workInProgress.stateNode ||\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n return workInProgress.sibling;\n}\nfunction deferHiddenOffscreenComponent(\n current,\n workInProgress,\n nextBaseLanes,\n renderLanes,\n remainingChildLanes\n) {\n var JSCompiler_inline_result = peekCacheFromPool();\n JSCompiler_inline_result =\n null === JSCompiler_inline_result\n ? null\n : { parent: CacheContext._currentValue, pool: JSCompiler_inline_result };\n workInProgress.memoizedState = {\n baseLanes: nextBaseLanes,\n cachePool: JSCompiler_inline_result\n };\n null !== current && pushTransition(workInProgress, null);\n reuseHiddenContextOnStack();\n pushOffscreenSuspenseHandler(workInProgress);\n null !== current &&\n propagateParentContextChanges(current, workInProgress, renderLanes, !0);\n workInProgress.childLanes = remainingChildLanes;\n return null;\n}\nfunction mountActivityChildren(workInProgress, nextProps) {\n nextProps = mountWorkInProgressOffscreenFiber(\n { mode: nextProps.mode, children: nextProps.children },\n workInProgress.mode\n );\n nextProps.ref = workInProgress.ref;\n workInProgress.child = nextProps;\n nextProps.return = workInProgress;\n return nextProps;\n}\nfunction retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountActivityChildren(workInProgress, workInProgress.pendingProps);\n current.flags |= 2;\n popSuspenseHandler(workInProgress);\n workInProgress.memoizedState = null;\n return current;\n}\nfunction updateActivityComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n didSuspend = 0 !== (workInProgress.flags & 128);\n workInProgress.flags &= -129;\n if (null === current) {\n if (isHydrating) {\n if (\"hidden\" === nextProps.mode)\n return (\n (current = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.lanes = 536870912),\n bailoutOffscreenComponent(null, current)\n );\n pushDehydratedActivitySuspenseHandler(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" === current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n workInProgress.lanes = 536870912;\n return null;\n }\n return mountActivityChildren(workInProgress, nextProps);\n }\n var prevState = current.memoizedState;\n if (null !== prevState) {\n var dehydrated = prevState.dehydrated;\n pushDehydratedActivitySuspenseHandler(workInProgress);\n if (didSuspend)\n if (workInProgress.flags & 256)\n (workInProgress.flags &= -257),\n (workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n ));\n else if (null !== workInProgress.memoizedState)\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null);\n else throw Error(formatProdErrorMessage(558));\n else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (didSuspend = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || didSuspend)\n ) {\n nextProps = workInProgressRoot;\n if (\n null !== nextProps &&\n ((dehydrated = getBumpedLaneForHydration(nextProps, renderLanes)),\n 0 !== dehydrated && dehydrated !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = dehydrated),\n enqueueConcurrentRenderForLane(current, dehydrated),\n scheduleUpdateOnFiber(nextProps, current, dehydrated),\n SelectiveHydrationException)\n );\n renderDidSuspendDelayIfPossible();\n workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n (current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(dehydrated.nextSibling)),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.flags |= 4096);\n return workInProgress;\n }\n current = createWorkInProgress(current.child, {\n mode: nextProps.mode,\n children: nextProps.children\n });\n current.ref = workInProgress.ref;\n workInProgress.child = current;\n current.return = workInProgress;\n return current;\n}\nfunction markRef(current, workInProgress) {\n var ref = workInProgress.ref;\n if (null === ref)\n null !== current &&\n null !== current.ref &&\n (workInProgress.flags |= 4194816);\n else {\n if (\"function\" !== typeof ref && \"object\" !== typeof ref)\n throw Error(formatProdErrorMessage(284));\n if (null === current || current.ref !== ref)\n workInProgress.flags |= 4194816;\n }\n}\nfunction updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n Component = renderWithHooks(\n current,\n workInProgress,\n Component,\n nextProps,\n void 0,\n renderLanes\n );\n nextProps = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && nextProps && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, Component, renderLanes);\n return workInProgress.child;\n}\nfunction replayFunctionComponent(\n current,\n workInProgress,\n nextProps,\n Component,\n secondArg,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n workInProgress.updateQueue = null;\n nextProps = renderWithHooksAgain(\n workInProgress,\n Component,\n nextProps,\n secondArg\n );\n finishRenderingHooks(current);\n Component = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && Component && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateClassComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n if (null === workInProgress.stateNode) {\n var context = emptyContextObject,\n contextType = Component.contextType;\n \"object\" === typeof contextType &&\n null !== contextType &&\n (context = readContext(contextType));\n context = new Component(nextProps, context);\n workInProgress.memoizedState =\n null !== context.state && void 0 !== context.state ? context.state : null;\n context.updater = classComponentUpdater;\n workInProgress.stateNode = context;\n context._reactInternals = workInProgress;\n context = workInProgress.stateNode;\n context.props = nextProps;\n context.state = workInProgress.memoizedState;\n context.refs = {};\n initializeUpdateQueue(workInProgress);\n contextType = Component.contextType;\n context.context =\n \"object\" === typeof contextType && null !== contextType\n ? readContext(contextType)\n : emptyContextObject;\n context.state = workInProgress.memoizedState;\n contextType = Component.getDerivedStateFromProps;\n \"function\" === typeof contextType &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n contextType,\n nextProps\n ),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof Component.getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n ((contextType = context.state),\n \"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount(),\n contextType !== context.state &&\n classComponentUpdater.enqueueReplaceState(context, context.state, null),\n processUpdateQueue(workInProgress, nextProps, context, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction(),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308);\n nextProps = !0;\n } else if (null === current) {\n context = workInProgress.stateNode;\n var unresolvedOldProps = workInProgress.memoizedProps,\n oldProps = resolveClassComponentProps(Component, unresolvedOldProps);\n context.props = oldProps;\n var oldContext = context.context,\n contextType$jscomp$0 = Component.contextType;\n contextType = emptyContextObject;\n \"object\" === typeof contextType$jscomp$0 &&\n null !== contextType$jscomp$0 &&\n (contextType = readContext(contextType$jscomp$0));\n var getDerivedStateFromProps = Component.getDerivedStateFromProps;\n contextType$jscomp$0 =\n \"function\" === typeof getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate;\n unresolvedOldProps = workInProgress.pendingProps !== unresolvedOldProps;\n contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((unresolvedOldProps || oldContext !== contextType) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n contextType\n ));\n hasForceUpdate = !1;\n var oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n oldContext = workInProgress.memoizedState;\n unresolvedOldProps || oldState !== oldContext || hasForceUpdate\n ? (\"function\" === typeof getDerivedStateFromProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n getDerivedStateFromProps,\n nextProps\n ),\n (oldContext = workInProgress.memoizedState)),\n (oldProps =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n oldProps,\n nextProps,\n oldState,\n oldContext,\n contextType\n ))\n ? (contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n (\"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount()),\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = oldContext)),\n (context.props = nextProps),\n (context.state = oldContext),\n (context.context = contextType),\n (nextProps = oldProps))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (nextProps = !1));\n } else {\n context = workInProgress.stateNode;\n cloneUpdateQueue(current, workInProgress);\n contextType = workInProgress.memoizedProps;\n contextType$jscomp$0 = resolveClassComponentProps(Component, contextType);\n context.props = contextType$jscomp$0;\n getDerivedStateFromProps = workInProgress.pendingProps;\n oldState = context.context;\n oldContext = Component.contextType;\n oldProps = emptyContextObject;\n \"object\" === typeof oldContext &&\n null !== oldContext &&\n (oldProps = readContext(oldContext));\n unresolvedOldProps = Component.getDerivedStateFromProps;\n (oldContext =\n \"function\" === typeof unresolvedOldProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate) ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((contextType !== getDerivedStateFromProps || oldState !== oldProps) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n oldProps\n ));\n hasForceUpdate = !1;\n oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n var newState = workInProgress.memoizedState;\n contextType !== getDerivedStateFromProps ||\n oldState !== newState ||\n hasForceUpdate ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies))\n ? (\"function\" === typeof unresolvedOldProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n unresolvedOldProps,\n nextProps\n ),\n (newState = workInProgress.memoizedState)),\n (contextType$jscomp$0 =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n contextType$jscomp$0,\n nextProps,\n oldState,\n newState,\n oldProps\n ) ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies)))\n ? (oldContext ||\n (\"function\" !== typeof context.UNSAFE_componentWillUpdate &&\n \"function\" !== typeof context.componentWillUpdate) ||\n (\"function\" === typeof context.componentWillUpdate &&\n context.componentWillUpdate(nextProps, newState, oldProps),\n \"function\" === typeof context.UNSAFE_componentWillUpdate &&\n context.UNSAFE_componentWillUpdate(\n nextProps,\n newState,\n oldProps\n )),\n \"function\" === typeof context.componentDidUpdate &&\n (workInProgress.flags |= 4),\n \"function\" === typeof context.getSnapshotBeforeUpdate &&\n (workInProgress.flags |= 1024))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = newState)),\n (context.props = nextProps),\n (context.state = newState),\n (context.context = oldProps),\n (nextProps = contextType$jscomp$0))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (nextProps = !1));\n }\n context = nextProps;\n markRef(current, workInProgress);\n nextProps = 0 !== (workInProgress.flags & 128);\n context || nextProps\n ? ((context = workInProgress.stateNode),\n (Component =\n nextProps && \"function\" !== typeof Component.getDerivedStateFromError\n ? null\n : context.render()),\n (workInProgress.flags |= 1),\n null !== current && nextProps\n ? ((workInProgress.child = reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n )),\n (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n Component,\n renderLanes\n )))\n : reconcileChildren(current, workInProgress, Component, renderLanes),\n (workInProgress.memoizedState = context.state),\n (current = workInProgress.child))\n : (current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n ));\n return current;\n}\nfunction mountHostRootWithoutHydrating(\n current,\n workInProgress,\n nextChildren,\n renderLanes\n) {\n resetHydrationState();\n workInProgress.flags |= 256;\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nvar SUSPENDED_MARKER = {\n dehydrated: null,\n treeContext: null,\n retryLane: 0,\n hydrationErrors: null\n};\nfunction mountSuspenseOffscreenState(renderLanes) {\n return { baseLanes: renderLanes, cachePool: getSuspendedCache() };\n}\nfunction getRemainingWorkInPrimaryTree(\n current,\n primaryTreeDidDefer,\n renderLanes\n) {\n current = null !== current ? current.childLanes & ~renderLanes : 0;\n primaryTreeDidDefer && (current |= workInProgressDeferredLane);\n return current;\n}\nfunction updateSuspenseComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n showFallback = !1,\n didSuspend = 0 !== (workInProgress.flags & 128),\n JSCompiler_temp;\n (JSCompiler_temp = didSuspend) ||\n (JSCompiler_temp =\n null !== current && null === current.memoizedState\n ? !1\n : 0 !== (suspenseStackCursor.current & 2));\n JSCompiler_temp && ((showFallback = !0), (workInProgress.flags &= -129));\n JSCompiler_temp = 0 !== (workInProgress.flags & 32);\n workInProgress.flags &= -33;\n if (null === current) {\n if (isHydrating) {\n showFallback\n ? pushPrimaryTreeSuspenseHandler(workInProgress)\n : reuseSuspenseHandlerOnStack(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" !== current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n isSuspenseInstanceFallback(current)\n ? (workInProgress.lanes = 32)\n : (workInProgress.lanes = 536870912);\n return null;\n }\n var nextPrimaryChildren = nextProps.children;\n nextProps = nextProps.fallback;\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (showFallback = workInProgress.mode),\n (nextPrimaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"hidden\", children: nextPrimaryChildren },\n showFallback\n )),\n (nextProps = createFiberFromFragment(\n nextProps,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.sibling = nextProps),\n (workInProgress.child = nextPrimaryChildren),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState = mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(null, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);\n }\n var prevState = current.memoizedState;\n if (\n null !== prevState &&\n ((nextPrimaryChildren = prevState.dehydrated), null !== nextPrimaryChildren)\n ) {\n if (didSuspend)\n workInProgress.flags & 256\n ? (pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags &= -257),\n (workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n )))\n : null !== workInProgress.memoizedState\n ? (reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null))\n : (reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (nextProps = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: nextProps.children },\n showFallback\n )),\n (nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n ),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState =\n mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n (workInProgress = bailoutOffscreenComponent(null, nextProps)));\n else if (\n (pushPrimaryTreeSuspenseHandler(workInProgress),\n isSuspenseInstanceFallback(nextPrimaryChildren))\n ) {\n JSCompiler_temp =\n nextPrimaryChildren.nextSibling &&\n nextPrimaryChildren.nextSibling.dataset;\n if (JSCompiler_temp) var digest = JSCompiler_temp.dgst;\n JSCompiler_temp = digest;\n nextProps = Error(formatProdErrorMessage(419));\n nextProps.stack = \"\";\n nextProps.digest = JSCompiler_temp;\n queueHydrationError({ value: nextProps, source: null, stack: null });\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (JSCompiler_temp = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || JSCompiler_temp)\n ) {\n JSCompiler_temp = workInProgressRoot;\n if (\n null !== JSCompiler_temp &&\n ((nextProps = getBumpedLaneForHydration(JSCompiler_temp, renderLanes)),\n 0 !== nextProps && nextProps !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = nextProps),\n enqueueConcurrentRenderForLane(current, nextProps),\n scheduleUpdateOnFiber(JSCompiler_temp, current, nextProps),\n SelectiveHydrationException)\n );\n isSuspenseInstancePending(nextPrimaryChildren) ||\n renderDidSuspendDelayIfPossible();\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n isSuspenseInstancePending(nextPrimaryChildren)\n ? ((workInProgress.flags |= 192),\n (workInProgress.child = current.child),\n (workInProgress = null))\n : ((current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(\n nextPrimaryChildren.nextSibling\n )),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountSuspensePrimaryChildren(\n workInProgress,\n nextProps.children\n )),\n (workInProgress.flags |= 4096));\n return workInProgress;\n }\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (prevState = current.child),\n (digest = prevState.sibling),\n (nextProps = createWorkInProgress(prevState, {\n mode: \"hidden\",\n children: nextProps.children\n })),\n (nextProps.subtreeFlags = prevState.subtreeFlags & 65011712),\n null !== digest\n ? (nextPrimaryChildren = createWorkInProgress(\n digest,\n nextPrimaryChildren\n ))\n : ((nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2)),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n bailoutOffscreenComponent(null, nextProps),\n (nextProps = workInProgress.child),\n (nextPrimaryChildren = current.child.memoizedState),\n null === nextPrimaryChildren\n ? (nextPrimaryChildren = mountSuspenseOffscreenState(renderLanes))\n : ((showFallback = nextPrimaryChildren.cachePool),\n null !== showFallback\n ? ((prevState = CacheContext._currentValue),\n (showFallback =\n showFallback.parent !== prevState\n ? { parent: prevState, pool: prevState }\n : showFallback))\n : (showFallback = getSuspendedCache()),\n (nextPrimaryChildren = {\n baseLanes: nextPrimaryChildren.baseLanes | renderLanes,\n cachePool: showFallback\n })),\n (nextProps.memoizedState = nextPrimaryChildren),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(current.child, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n renderLanes = current.child;\n current = renderLanes.sibling;\n renderLanes = createWorkInProgress(renderLanes, {\n mode: \"visible\",\n children: nextProps.children\n });\n renderLanes.return = workInProgress;\n renderLanes.sibling = null;\n null !== current &&\n ((JSCompiler_temp = workInProgress.deletions),\n null === JSCompiler_temp\n ? ((workInProgress.deletions = [current]), (workInProgress.flags |= 16))\n : JSCompiler_temp.push(current));\n workInProgress.child = renderLanes;\n workInProgress.memoizedState = null;\n return renderLanes;\n}\nfunction mountSuspensePrimaryChildren(workInProgress, primaryChildren) {\n primaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: primaryChildren },\n workInProgress.mode\n );\n primaryChildren.return = workInProgress;\n return (workInProgress.child = primaryChildren);\n}\nfunction mountWorkInProgressOffscreenFiber(offscreenProps, mode) {\n offscreenProps = createFiberImplClass(22, offscreenProps, null, mode);\n offscreenProps.lanes = 0;\n return offscreenProps;\n}\nfunction retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountSuspensePrimaryChildren(\n workInProgress,\n workInProgress.pendingProps.children\n );\n current.flags |= 2;\n workInProgress.memoizedState = null;\n return current;\n}\nfunction scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {\n fiber.lanes |= renderLanes;\n var alternate = fiber.alternate;\n null !== alternate && (alternate.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);\n}\nfunction initSuspenseListRenderState(\n workInProgress,\n isBackwards,\n tail,\n lastContentRow,\n tailMode,\n treeForkCount\n) {\n var renderState = workInProgress.memoizedState;\n null === renderState\n ? (workInProgress.memoizedState = {\n isBackwards: isBackwards,\n rendering: null,\n renderingStartTime: 0,\n last: lastContentRow,\n tail: tail,\n tailMode: tailMode,\n treeForkCount: treeForkCount\n })\n : ((renderState.isBackwards = isBackwards),\n (renderState.rendering = null),\n (renderState.renderingStartTime = 0),\n (renderState.last = lastContentRow),\n (renderState.tail = tail),\n (renderState.tailMode = tailMode),\n (renderState.treeForkCount = treeForkCount));\n}\nfunction updateSuspenseListComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n revealOrder = nextProps.revealOrder,\n tailMode = nextProps.tail;\n nextProps = nextProps.children;\n var suspenseContext = suspenseStackCursor.current,\n shouldForceFallback = 0 !== (suspenseContext & 2);\n shouldForceFallback\n ? ((suspenseContext = (suspenseContext & 1) | 2),\n (workInProgress.flags |= 128))\n : (suspenseContext &= 1);\n push(suspenseStackCursor, suspenseContext);\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n nextProps = isHydrating ? treeForkCount : 0;\n if (!shouldForceFallback && null !== current && 0 !== (current.flags & 128))\n a: for (current = workInProgress.child; null !== current; ) {\n if (13 === current.tag)\n null !== current.memoizedState &&\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (19 === current.tag)\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (null !== current.child) {\n current.child.return = current;\n current = current.child;\n continue;\n }\n if (current === workInProgress) break a;\n for (; null === current.sibling; ) {\n if (null === current.return || current.return === workInProgress)\n break a;\n current = current.return;\n }\n current.sibling.return = current.return;\n current = current.sibling;\n }\n switch (revealOrder) {\n case \"forwards\":\n renderLanes = workInProgress.child;\n for (revealOrder = null; null !== renderLanes; )\n (current = renderLanes.alternate),\n null !== current &&\n null === findFirstSuspended(current) &&\n (revealOrder = renderLanes),\n (renderLanes = renderLanes.sibling);\n renderLanes = revealOrder;\n null === renderLanes\n ? ((revealOrder = workInProgress.child), (workInProgress.child = null))\n : ((revealOrder = renderLanes.sibling), (renderLanes.sibling = null));\n initSuspenseListRenderState(\n workInProgress,\n !1,\n revealOrder,\n renderLanes,\n tailMode,\n nextProps\n );\n break;\n case \"backwards\":\n case \"unstable_legacy-backwards\":\n renderLanes = null;\n revealOrder = workInProgress.child;\n for (workInProgress.child = null; null !== revealOrder; ) {\n current = revealOrder.alternate;\n if (null !== current && null === findFirstSuspended(current)) {\n workInProgress.child = revealOrder;\n break;\n }\n current = revealOrder.sibling;\n revealOrder.sibling = renderLanes;\n renderLanes = revealOrder;\n revealOrder = current;\n }\n initSuspenseListRenderState(\n workInProgress,\n !0,\n renderLanes,\n null,\n tailMode,\n nextProps\n );\n break;\n case \"together\":\n initSuspenseListRenderState(\n workInProgress,\n !1,\n null,\n null,\n void 0,\n nextProps\n );\n break;\n default:\n workInProgress.memoizedState = null;\n }\n return workInProgress.child;\n}\nfunction bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {\n null !== current && (workInProgress.dependencies = current.dependencies);\n workInProgressRootSkippedLanes |= workInProgress.lanes;\n if (0 === (renderLanes & workInProgress.childLanes))\n if (null !== current) {\n if (\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n 0 === (renderLanes & workInProgress.childLanes))\n )\n return null;\n } else return null;\n if (null !== current && workInProgress.child !== current.child)\n throw Error(formatProdErrorMessage(153));\n if (null !== workInProgress.child) {\n current = workInProgress.child;\n renderLanes = createWorkInProgress(current, current.pendingProps);\n workInProgress.child = renderLanes;\n for (renderLanes.return = workInProgress; null !== current.sibling; )\n (current = current.sibling),\n (renderLanes = renderLanes.sibling =\n createWorkInProgress(current, current.pendingProps)),\n (renderLanes.return = workInProgress);\n renderLanes.sibling = null;\n }\n return workInProgress.child;\n}\nfunction checkScheduledUpdateOrContext(current, renderLanes) {\n if (0 !== (current.lanes & renderLanes)) return !0;\n current = current.dependencies;\n return null !== current && checkIfContextChanged(current) ? !0 : !1;\n}\nfunction attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n) {\n switch (workInProgress.tag) {\n case 3:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n resetHydrationState();\n break;\n case 27:\n case 5:\n pushHostContext(workInProgress);\n break;\n case 4:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n break;\n case 10:\n pushProvider(\n workInProgress,\n workInProgress.type,\n workInProgress.memoizedProps.value\n );\n break;\n case 31:\n if (null !== workInProgress.memoizedState)\n return (\n (workInProgress.flags |= 128),\n pushDehydratedActivitySuspenseHandler(workInProgress),\n null\n );\n break;\n case 13:\n var state$102 = workInProgress.memoizedState;\n if (null !== state$102) {\n if (null !== state$102.dehydrated)\n return (\n pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags |= 128),\n null\n );\n if (0 !== (renderLanes & workInProgress.child.childLanes))\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n pushPrimaryTreeSuspenseHandler(workInProgress);\n current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n return null !== current ? current.sibling : null;\n }\n pushPrimaryTreeSuspenseHandler(workInProgress);\n break;\n case 19:\n var didSuspendBefore = 0 !== (current.flags & 128);\n state$102 = 0 !== (renderLanes & workInProgress.childLanes);\n state$102 ||\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n (state$102 = 0 !== (renderLanes & workInProgress.childLanes)));\n if (didSuspendBefore) {\n if (state$102)\n return updateSuspenseListComponent(\n current,\n workInProgress,\n renderLanes\n );\n workInProgress.flags |= 128;\n }\n didSuspendBefore = workInProgress.memoizedState;\n null !== didSuspendBefore &&\n ((didSuspendBefore.rendering = null),\n (didSuspendBefore.tail = null),\n (didSuspendBefore.lastEffect = null));\n push(suspenseStackCursor, suspenseStackCursor.current);\n if (state$102) break;\n else return null;\n case 22:\n return (\n (workInProgress.lanes = 0),\n updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n )\n );\n case 24:\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n }\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n}\nfunction beginWork(current, workInProgress, renderLanes) {\n if (null !== current)\n if (current.memoizedProps !== workInProgress.pendingProps)\n didReceiveUpdate = !0;\n else {\n if (\n !checkScheduledUpdateOrContext(current, renderLanes) &&\n 0 === (workInProgress.flags & 128)\n )\n return (\n (didReceiveUpdate = !1),\n attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n )\n );\n didReceiveUpdate = 0 !== (current.flags & 131072) ? !0 : !1;\n }\n else\n (didReceiveUpdate = !1),\n isHydrating &&\n 0 !== (workInProgress.flags & 1048576) &&\n pushTreeId(workInProgress, treeForkCount, workInProgress.index);\n workInProgress.lanes = 0;\n switch (workInProgress.tag) {\n case 16:\n a: {\n var props = workInProgress.pendingProps;\n current = resolveLazy(workInProgress.elementType);\n workInProgress.type = current;\n if (\"function\" === typeof current)\n shouldConstruct(current)\n ? ((props = resolveClassComponentProps(current, props)),\n (workInProgress.tag = 1),\n (workInProgress = updateClassComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )))\n : ((workInProgress.tag = 0),\n (workInProgress = updateFunctionComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )));\n else {\n if (void 0 !== current && null !== current) {\n var $$typeof = current.$$typeof;\n if ($$typeof === REACT_FORWARD_REF_TYPE) {\n workInProgress.tag = 11;\n workInProgress = updateForwardRef(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n } else if ($$typeof === REACT_MEMO_TYPE) {\n workInProgress.tag = 14;\n workInProgress = updateMemoComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n }\n }\n workInProgress = getComponentNameFromType(current) || current;\n throw Error(formatProdErrorMessage(306, workInProgress, \"\"));\n }\n }\n return workInProgress;\n case 0:\n return updateFunctionComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 1:\n return (\n (props = workInProgress.type),\n ($$typeof = resolveClassComponentProps(\n props,\n workInProgress.pendingProps\n )),\n updateClassComponent(\n current,\n workInProgress,\n props,\n $$typeof,\n renderLanes\n )\n );\n case 3:\n a: {\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n );\n if (null === current) throw Error(formatProdErrorMessage(387));\n props = workInProgress.pendingProps;\n var prevState = workInProgress.memoizedState;\n $$typeof = prevState.element;\n cloneUpdateQueue(current, workInProgress);\n processUpdateQueue(workInProgress, props, null, renderLanes);\n var nextState = workInProgress.memoizedState;\n props = nextState.cache;\n pushProvider(workInProgress, CacheContext, props);\n props !== prevState.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n );\n suspendIfUpdateReadFromEntangledAsyncAction();\n props = nextState.element;\n if (prevState.isDehydrated)\n if (\n ((prevState = {\n element: props,\n isDehydrated: !1,\n cache: nextState.cache\n }),\n (workInProgress.updateQueue.baseState = prevState),\n (workInProgress.memoizedState = prevState),\n workInProgress.flags & 256)\n ) {\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else if (props !== $$typeof) {\n $$typeof = createCapturedValueAtFiber(\n Error(formatProdErrorMessage(424)),\n workInProgress\n );\n queueHydrationError($$typeof);\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else {\n current = workInProgress.stateNode.containerInfo;\n switch (current.nodeType) {\n case 9:\n current = current.body;\n break;\n default:\n current =\n \"HTML\" === current.nodeName\n ? current.ownerDocument.body\n : current;\n }\n nextHydratableInstance = getNextHydratable(current.firstChild);\n hydrationParentFiber = workInProgress;\n isHydrating = !0;\n hydrationErrors = null;\n rootOrSingletonContext = !0;\n renderLanes = mountChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n );\n for (workInProgress.child = renderLanes; renderLanes; )\n (renderLanes.flags = (renderLanes.flags & -3) | 4096),\n (renderLanes = renderLanes.sibling);\n }\n else {\n resetHydrationState();\n if (props === $$typeof) {\n workInProgress = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n break a;\n }\n reconcileChildren(current, workInProgress, props, renderLanes);\n }\n workInProgress = workInProgress.child;\n }\n return workInProgress;\n case 26:\n return (\n markRef(current, workInProgress),\n null === current\n ? (renderLanes = getResource(\n workInProgress.type,\n null,\n workInProgress.pendingProps,\n null\n ))\n ? (workInProgress.memoizedState = renderLanes)\n : isHydrating ||\n ((renderLanes = workInProgress.type),\n (current = workInProgress.pendingProps),\n (props = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n ).createElement(renderLanes)),\n (props[internalInstanceKey] = workInProgress),\n (props[internalPropsKey] = current),\n setInitialProperties(props, renderLanes, current),\n markNodeAsHoistable(props),\n (workInProgress.stateNode = props))\n : (workInProgress.memoizedState = getResource(\n workInProgress.type,\n current.memoizedProps,\n workInProgress.pendingProps,\n current.memoizedState\n )),\n null\n );\n case 27:\n return (\n pushHostContext(workInProgress),\n null === current &&\n isHydrating &&\n ((props = workInProgress.stateNode =\n resolveSingletonInstance(\n workInProgress.type,\n workInProgress.pendingProps,\n rootInstanceStackCursor.current\n )),\n (hydrationParentFiber = workInProgress),\n (rootOrSingletonContext = !0),\n ($$typeof = nextHydratableInstance),\n isSingletonScope(workInProgress.type)\n ? ((previousHydratableOnEnteringScopedSingleton = $$typeof),\n (nextHydratableInstance = getNextHydratable(props.firstChild)))\n : (nextHydratableInstance = $$typeof)),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n markRef(current, workInProgress),\n null === current && (workInProgress.flags |= 4194304),\n workInProgress.child\n );\n case 5:\n if (null === current && isHydrating) {\n if (($$typeof = props = nextHydratableInstance))\n (props = canHydrateInstance(\n props,\n workInProgress.type,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== props\n ? ((workInProgress.stateNode = props),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = getNextHydratable(props.firstChild)),\n (rootOrSingletonContext = !1),\n ($$typeof = !0))\n : ($$typeof = !1);\n $$typeof || throwOnHydrationMismatch(workInProgress);\n }\n pushHostContext(workInProgress);\n $$typeof = workInProgress.type;\n prevState = workInProgress.pendingProps;\n nextState = null !== current ? current.memoizedProps : null;\n props = prevState.children;\n shouldSetTextContent($$typeof, prevState)\n ? (props = null)\n : null !== nextState &&\n shouldSetTextContent($$typeof, nextState) &&\n (workInProgress.flags |= 32);\n null !== workInProgress.memoizedState &&\n (($$typeof = renderWithHooks(\n current,\n workInProgress,\n TransitionAwareHostComponent,\n null,\n null,\n renderLanes\n )),\n (HostTransitionContext._currentValue = $$typeof));\n markRef(current, workInProgress);\n reconcileChildren(current, workInProgress, props, renderLanes);\n return workInProgress.child;\n case 6:\n if (null === current && isHydrating) {\n if ((current = renderLanes = nextHydratableInstance))\n (renderLanes = canHydrateTextInstance(\n renderLanes,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== renderLanes\n ? ((workInProgress.stateNode = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null),\n (current = !0))\n : (current = !1);\n current || throwOnHydrationMismatch(workInProgress);\n }\n return null;\n case 13:\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n case 4:\n return (\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n ),\n (props = workInProgress.pendingProps),\n null === current\n ? (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n ))\n : reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 11:\n return updateForwardRef(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 7:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps,\n renderLanes\n ),\n workInProgress.child\n );\n case 8:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 12:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 10:\n return (\n (props = workInProgress.pendingProps),\n pushProvider(workInProgress, workInProgress.type, props.value),\n reconcileChildren(current, workInProgress, props.children, renderLanes),\n workInProgress.child\n );\n case 9:\n return (\n ($$typeof = workInProgress.type._context),\n (props = workInProgress.pendingProps.children),\n prepareToReadContext(workInProgress),\n ($$typeof = readContext($$typeof)),\n (props = props($$typeof)),\n (workInProgress.flags |= 1),\n reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 14:\n return updateMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 15:\n return updateSimpleMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 19:\n return updateSuspenseListComponent(current, workInProgress, renderLanes);\n case 31:\n return updateActivityComponent(current, workInProgress, renderLanes);\n case 22:\n return updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n );\n case 24:\n return (\n prepareToReadContext(workInProgress),\n (props = readContext(CacheContext)),\n null === current\n ? (($$typeof = peekCacheFromPool()),\n null === $$typeof &&\n (($$typeof = workInProgressRoot),\n (prevState = createCache()),\n ($$typeof.pooledCache = prevState),\n prevState.refCount++,\n null !== prevState && ($$typeof.pooledCacheLanes |= renderLanes),\n ($$typeof = prevState)),\n (workInProgress.memoizedState = { parent: props, cache: $$typeof }),\n initializeUpdateQueue(workInProgress),\n pushProvider(workInProgress, CacheContext, $$typeof))\n : (0 !== (current.lanes & renderLanes) &&\n (cloneUpdateQueue(current, workInProgress),\n processUpdateQueue(workInProgress, null, null, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction()),\n ($$typeof = current.memoizedState),\n (prevState = workInProgress.memoizedState),\n $$typeof.parent !== props\n ? (($$typeof = { parent: props, cache: props }),\n (workInProgress.memoizedState = $$typeof),\n 0 === workInProgress.lanes &&\n (workInProgress.memoizedState =\n workInProgress.updateQueue.baseState =\n $$typeof),\n pushProvider(workInProgress, CacheContext, props))\n : ((props = prevState.cache),\n pushProvider(workInProgress, CacheContext, props),\n props !== $$typeof.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n ))),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 29:\n throw workInProgress.pendingProps;\n }\n throw Error(formatProdErrorMessage(156, workInProgress.tag));\n}\nfunction markUpdate(workInProgress) {\n workInProgress.flags |= 4;\n}\nfunction preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n oldProps,\n newProps,\n renderLanes\n) {\n if ((type = 0 !== (workInProgress.mode & 32))) type = !1;\n if (type) {\n if (\n ((workInProgress.flags |= 16777216),\n (renderLanes & 335544128) === renderLanes)\n )\n if (workInProgress.stateNode.complete) workInProgress.flags |= 8192;\n else if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n } else workInProgress.flags &= -16777217;\n}\nfunction preloadResourceAndSuspendIfNeeded(workInProgress, resource) {\n if (\"stylesheet\" !== resource.type || 0 !== (resource.state.loading & 4))\n workInProgress.flags &= -16777217;\n else if (((workInProgress.flags |= 16777216), !preloadResource(resource)))\n if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n}\nfunction scheduleRetryEffect(workInProgress, retryQueue) {\n null !== retryQueue && (workInProgress.flags |= 4);\n workInProgress.flags & 16384 &&\n ((retryQueue =\n 22 !== workInProgress.tag ? claimNextRetryLane() : 536870912),\n (workInProgress.lanes |= retryQueue),\n (workInProgressSuspendedRetryLanes |= retryQueue));\n}\nfunction cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {\n if (!isHydrating)\n switch (renderState.tailMode) {\n case \"hidden\":\n hasRenderedATailFallback = renderState.tail;\n for (var lastTailNode = null; null !== hasRenderedATailFallback; )\n null !== hasRenderedATailFallback.alternate &&\n (lastTailNode = hasRenderedATailFallback),\n (hasRenderedATailFallback = hasRenderedATailFallback.sibling);\n null === lastTailNode\n ? (renderState.tail = null)\n : (lastTailNode.sibling = null);\n break;\n case \"collapsed\":\n lastTailNode = renderState.tail;\n for (var lastTailNode$106 = null; null !== lastTailNode; )\n null !== lastTailNode.alternate && (lastTailNode$106 = lastTailNode),\n (lastTailNode = lastTailNode.sibling);\n null === lastTailNode$106\n ? hasRenderedATailFallback || null === renderState.tail\n ? (renderState.tail = null)\n : (renderState.tail.sibling = null)\n : (lastTailNode$106.sibling = null);\n }\n}\nfunction bubbleProperties(completedWork) {\n var didBailout =\n null !== completedWork.alternate &&\n completedWork.alternate.child === completedWork.child,\n newChildLanes = 0,\n subtreeFlags = 0;\n if (didBailout)\n for (var child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags & 65011712),\n (subtreeFlags |= child$107.flags & 65011712),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n else\n for (child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags),\n (subtreeFlags |= child$107.flags),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n completedWork.subtreeFlags |= subtreeFlags;\n completedWork.childLanes = newChildLanes;\n return didBailout;\n}\nfunction completeWork(current, workInProgress, renderLanes) {\n var newProps = workInProgress.pendingProps;\n popTreeContext(workInProgress);\n switch (workInProgress.tag) {\n case 16:\n case 15:\n case 0:\n case 11:\n case 7:\n case 8:\n case 12:\n case 9:\n case 14:\n return bubbleProperties(workInProgress), null;\n case 1:\n return bubbleProperties(workInProgress), null;\n case 3:\n renderLanes = workInProgress.stateNode;\n newProps = null;\n null !== current && (newProps = current.memoizedState.cache);\n workInProgress.memoizedState.cache !== newProps &&\n (workInProgress.flags |= 2048);\n popProvider(CacheContext);\n popHostContainer();\n renderLanes.pendingContext &&\n ((renderLanes.context = renderLanes.pendingContext),\n (renderLanes.pendingContext = null));\n if (null === current || null === current.child)\n popHydrationState(workInProgress)\n ? markUpdate(workInProgress)\n : null === current ||\n (current.memoizedState.isDehydrated &&\n 0 === (workInProgress.flags & 256)) ||\n ((workInProgress.flags |= 1024),\n upgradeHydrationErrorsToRecoverable());\n bubbleProperties(workInProgress);\n return null;\n case 26:\n var type = workInProgress.type,\n nextResource = workInProgress.memoizedState;\n null === current\n ? (markUpdate(workInProgress),\n null !== nextResource\n ? (bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n null,\n newProps,\n renderLanes\n )))\n : nextResource\n ? nextResource !== current.memoizedState\n ? (markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n (workInProgress.flags &= -16777217))\n : ((current = current.memoizedProps),\n current !== newProps && markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n current,\n newProps,\n renderLanes\n ));\n return null;\n case 27:\n popHostContext(workInProgress);\n renderLanes = rootInstanceStackCursor.current;\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n current = contextStackCursor.current;\n popHydrationState(workInProgress)\n ? prepareToHydrateHostInstance(workInProgress, current)\n : ((current = resolveSingletonInstance(type, newProps, renderLanes)),\n (workInProgress.stateNode = current),\n markUpdate(workInProgress));\n }\n bubbleProperties(workInProgress);\n return null;\n case 5:\n popHostContext(workInProgress);\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n nextResource = contextStackCursor.current;\n if (popHydrationState(workInProgress))\n prepareToHydrateHostInstance(workInProgress, nextResource);\n else {\n var ownerDocument = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n );\n switch (nextResource) {\n case 1:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case 2:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n default:\n switch (type) {\n case \"svg\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case \"math\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n case \"script\":\n nextResource = ownerDocument.createElement(\"div\");\n nextResource.innerHTML = \" - + + diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index c6d71897..02bd6371 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.856", + "version": "1.0.869", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index 4da74875..c60af960 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1381", + "version": "1.0.1394", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, @@ -45,6 +45,7 @@ "webextension-polyfill": "^0.12.0" }, "devDependencies": { + "@types/chrome": "^0.1.24", "@types/node": "^24.3.0", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index a5552a57..d3fc4fe0 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 2de85caa..c7fab8a7 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1386", + "version": "0.5.1399", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 08a6c6bc..6a6e1fac 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, @@ -29,6 +29,7 @@ "@types/ws": "^8.18.1", "esbuild": "^0.25.4", "esm": "^3.2.25", + "fast-glob": "^3.3.3", "rollup": "^4.45.1", "ts-node": "^10.9.2", "ws": "^8.18.2" diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 116d05e5..19315073 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index b84ed267..2f7879a7 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 6dfe2989..6d1129c0 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 8c425912..7c0e3b0c 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 91510b35..e4543eb3 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index c84605fc..96c09e4c 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index dba18f12..ad6ae41e 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - ui components", "type": "module", "private": true, @@ -31,6 +31,7 @@ "devDependencies": { "@extension/tailwindcss-config": "workspace:*", "@extension/tsconfig": "workspace:*", + "deepmerge": "^4.3.1", "react-spinners": "^0.17.0", "tsc-alias": "^1.8.16", "tailwindcss": "^3.4.10" diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index e071f8c7..e4ec552d 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1407", + "version": "0.5.1420", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/lib/zip-bundle.ts b/packages/zipper/lib/zip-bundle.ts index e5704a71..8dce9035 100755 --- a/packages/zipper/lib/zip-bundle.ts +++ b/packages/zipper/lib/zip-bundle.ts @@ -48,7 +48,7 @@ export const zipBundle = async ( let aborted = false; let totalSize = 0; const timer = Date.now(); - const zip = new Zip((err, data, final) => { + const zip = new Zip((err: Error | null, data: Uint8Array, final: boolean) => { if (err) { pReject(err); } else { diff --git a/packages/zipper/package.json b/packages/zipper/package.json index cb4ba492..107a0d4b 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - zipper", "type": "module", "private": true, @@ -22,6 +22,10 @@ "format": "prettier . --write --ignore-path ../../.prettierignore", "type-check": "tsc --noEmit" }, + "dependencies": { + "fast-glob": "^3.3.2", + "fflate": "^0.8.2" + }, "devDependencies": { "@extension/tsconfig": "workspace:*", "@extension/dev-utils": "workspace:*", diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 0838d206..018a2fa8 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 52bd270c..8af122a3 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 7e2269bf..d1be3319 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 097e8cf8..9e2166eb 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index ed919b26..2d4dd878 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index a2303913..e88072ea 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 874fe556..2919d86c 100755 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1399", + "version": "0.5.1412", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index add7e941..7a7477a7 100755 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1404", + "version": "0.27.1417", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml old mode 100755 new mode 100644 index d16b9963..05166a9a --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,38 +10,41 @@ importers: dependencies: react: specifier: ^19.0.0 - version: 19.1.1 + version: 19.2.0 react-dom: specifier: ^19.0.0 - version: 19.1.1(react@19.1.1) + version: 19.2.0(react@19.2.0) webextension-polyfill: specifier: ^0.12.0 version: 0.12.0 devDependencies: + '@types/chrome': + specifier: ^0.1.24 + version: 0.1.24 '@types/node': specifier: ^24.3.0 - version: 24.3.0 + version: 24.9.1 '@types/react': specifier: ^19.0.8 - version: 19.1.10 + version: 19.2.2 '@types/react-dom': specifier: ^19.0.3 - version: 19.1.7(@types/react@19.1.10) + version: 19.2.2(@types/react@19.2.2) '@vitejs/plugin-react-swc': specifier: ^4.1.0 - version: 4.1.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 4.1.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) rimraf: specifier: ^6.0.1 version: 6.0.1 turbo: specifier: ^2.5.5 - version: 2.5.6 + version: 2.5.8 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.9.3 vite: specifier: ^7.1.9 - version: 7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) + version: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6) chrome-extension: dependencies: @@ -75,22 +78,22 @@ importers: version: link:../packages/vite-config '@laynezh/vite-plugin-lib-assets': specifier: ^1.1.0 - version: 1.2.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 1.2.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) '@types/chrome': specifier: '*' - version: 0.1.4 + version: 0.1.24 '@types/node': specifier: '*' - version: 24.3.0 + version: 24.9.1 magic-string: specifier: ^0.30.17 - version: 0.30.17 + version: 0.30.19 ts-loader: specifier: ^9.5.2 - version: 9.5.2(typescript@5.9.2)(webpack@5.101.2) + version: 9.5.4(typescript@5.9.3)(webpack@5.102.1) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.46.2)(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 0.22.0(rollup@4.52.5)(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) packages/dev-utils: devDependencies: @@ -108,7 +111,7 @@ importers: dependencies: '@dotenvx/dotenvx': specifier: ^1.44.1 - version: 1.48.4 + version: 1.51.0 devDependencies: '@extension/tsconfig': specifier: workspace:* @@ -124,22 +127,25 @@ importers: version: link:../tsconfig '@rollup/plugin-sucrase': specifier: ^5.0.2 - version: 5.0.2(rollup@4.46.2) + version: 5.0.2(rollup@4.52.5) '@types/ws': specifier: ^8.18.1 version: 8.18.1 esbuild: specifier: ^0.25.4 - version: 0.25.9 + version: 0.25.11 esm: specifier: ^3.2.25 version: 3.2.25 + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 rollup: specifier: ^4.45.1 - version: 4.46.2 + version: 4.52.5 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2) + version: 10.9.2(@swc/core@1.13.5)(@types/node@24.9.1)(typescript@5.9.3) ws: specifier: ^8.18.2 version: 8.18.3 @@ -167,13 +173,13 @@ importers: version: link:../tsconfig '@inquirer/prompts': specifier: ^7.5.1 - version: 7.8.2(@types/node@24.3.0) + version: 7.9.0(@types/node@24.9.1) '@types/yargs': specifier: ^17.0.33 version: 17.0.33 tsx: specifier: ^4.19.4 - version: 4.20.4 + version: 4.20.6 yargs: specifier: ^17.7.2 version: 17.7.2 @@ -228,12 +234,15 @@ importers: '@extension/tsconfig': specifier: workspace:* version: link:../tsconfig + deepmerge: + specifier: ^4.3.1 + version: 4.3.1 react-spinners: specifier: ^0.17.0 - version: 0.17.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 0.17.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) tailwindcss: specifier: ^3.4.10 - version: 3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2)) + version: 3.4.18(tsx@4.20.6) tsc-alias: specifier: ^1.8.16 version: 1.8.16 @@ -245,13 +254,13 @@ importers: version: link:../env '@vitejs/plugin-react-swc': specifier: ^4.1.0 - version: 4.1.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 4.1.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) deepmerge: specifier: ^4.3.1 version: 4.3.1 vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.46.2)(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 0.22.0(rollup@4.52.5)(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) devDependencies: '@extension/hmr': specifier: workspace:* @@ -261,6 +270,13 @@ importers: version: link:../tsconfig packages/zipper: + dependencies: + fast-glob: + specifier: ^3.3.2 + version: 3.3.3 + fflate: + specifier: ^0.8.2 + version: 0.8.2 devDependencies: '@extension/dev-utils': specifier: workspace:* @@ -295,7 +311,7 @@ importers: version: link:../../packages/vite-config tsx: specifier: ^4.16.2 - version: 4.20.4 + version: 4.20.6 pages/content-runtime: dependencies: @@ -320,7 +336,7 @@ importers: version: link:../../packages/vite-config tsx: specifier: ^4.16.2 - version: 4.20.4 + version: 4.20.6 pages/content-ui: dependencies: @@ -351,16 +367,16 @@ importers: version: link:../../packages/vite-config '@tailwindcss/postcss': specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) tailwindcss: specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 tsx: specifier: ^4.20.3 - version: 4.20.4 + version: 4.20.6 pages/devtools: dependencies: @@ -401,16 +417,16 @@ importers: version: link:../../packages/vite-config '@tailwindcss/postcss': specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) sass: specifier: ^1.89.0 - version: 1.90.0 + version: 1.93.2 tailwindcss: specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 pages/options: dependencies: @@ -428,7 +444,7 @@ importers: version: link:../../packages/ui react-resizable-panels: specifier: ^2.0.22 - version: 2.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.1.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) devDependencies: '@extension/tailwindcss-config': specifier: workspace:* @@ -441,16 +457,16 @@ importers: version: link:../../packages/vite-config '@tailwindcss/postcss': specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 '@vitejs/plugin-react-swc': specifier: ^3.10.2 - version: 3.11.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 3.11.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) tailwindcss: specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 pages/side-panel: dependencies: @@ -481,46 +497,19 @@ importers: version: link:../../packages/vite-config '@tailwindcss/postcss': specifier: ^4.1.11 - version: 4.1.12 + version: 4.1.15 '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 '@vitejs/plugin-react-swc': specifier: ^3.10.2 - version: 3.11.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)) + version: 3.11.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) tailwindcss: specifier: ^4.1.11 - version: 4.1.12 - - tests/e2e: - devDependencies: - '@extension/env': - specifier: workspace:* - version: link:../../packages/env - '@extension/tsconfig': - specifier: workspace:* - version: link:../../packages/tsconfig - '@wdio/cli': - specifier: ^9.14.0 - version: 9.19.1(@types/node@24.3.0)(expect-webdriverio@5.4.2) - '@wdio/globals': - specifier: ^9.14.0 - version: 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.1) - '@wdio/local-runner': - specifier: ^9.14.0 - version: 9.19.1(@wdio/globals@9.17.0)(webdriverio@9.19.1) - '@wdio/mocha-framework': - specifier: ^9.14.0 - version: 9.19.1 - '@wdio/spec-reporter': - specifier: ^9.14.0 - version: 9.19.1 - '@wdio/types': - specifier: ^9.14.0 - version: 9.19.1 + version: 4.1.15 packages: @@ -528,20 +517,12 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} - engines: {node: '>=6.9.0'} - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@dotenvx/dotenvx@1.48.4': - resolution: {integrity: sha512-GpJWpGVI5JGhNzFlWOjCD3KMiN3xU1US4oLKQ7SiiGru4LvR7sUf3pDMpfjtlgzHStL5ydq4ekfZcRxWpHaJkA==} + '@dotenvx/dotenvx@1.51.0': + resolution: {integrity: sha512-CbMGzyOYSyFF7d4uaeYwO9gpSBzLTnMmSmTVpCZjvpJFV69qYbjYPpzNnCz1mb2wIvEhjWjRwQWuBzTO0jITww==} hasBin: true '@ecies/ciphers@0.2.4': @@ -550,164 +531,168 @@ packages: peerDependencies: '@noble/ciphers': ^1.0.0 - '@esbuild/aix-ppc64@0.25.9': - resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.9': - resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.9': - resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.9': - resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.9': - resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.9': - resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.9': - resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.9': - resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.9': - resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.9': - resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.9': - resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.9': - resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.9': - resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.9': - resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.9': - resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.9': - resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.9': - resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.9': - resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.9': - resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.9': - resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.9': - resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.9': - resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.9': - resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.9': - resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.9': - resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.9': - resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@inquirer/checkbox@4.2.1': - resolution: {integrity: sha512-bevKGO6kX1eM/N+pdh9leS5L7TBF4ICrzi9a+cbWkrxeAeIcwlo/7OfWGCDERdRCI2/Q6tjltX4bt07ALHDwFw==} + '@inquirer/ansi@1.0.1': + resolution: {integrity: sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==} + engines: {node: '>=18'} + + '@inquirer/checkbox@4.3.0': + resolution: {integrity: sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -715,8 +700,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.14': - resolution: {integrity: sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==} + '@inquirer/confirm@5.1.19': + resolution: {integrity: sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -724,8 +709,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.1.15': - resolution: {integrity: sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==} + '@inquirer/core@10.3.0': + resolution: {integrity: sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -733,8 +718,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.17': - resolution: {integrity: sha512-r6bQLsyPSzbWrZZ9ufoWL+CztkSatnJ6uSxqd6N+o41EZC51sQeWOzI6s5jLb+xxTWxl7PlUppqm8/sow241gg==} + '@inquirer/editor@4.2.21': + resolution: {integrity: sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -742,8 +727,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.17': - resolution: {integrity: sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==} + '@inquirer/expand@4.0.21': + resolution: {integrity: sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -751,8 +736,8 @@ packages: '@types/node': optional: true - '@inquirer/external-editor@1.0.1': - resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} + '@inquirer/external-editor@1.0.2': + resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -760,12 +745,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.13': - resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} + '@inquirer/figures@1.0.14': + resolution: {integrity: sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==} engines: {node: '>=18'} - '@inquirer/input@4.2.1': - resolution: {integrity: sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==} + '@inquirer/input@4.2.5': + resolution: {integrity: sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -773,8 +758,8 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.17': - resolution: {integrity: sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==} + '@inquirer/number@3.0.21': + resolution: {integrity: sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -782,8 +767,8 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.17': - resolution: {integrity: sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==} + '@inquirer/password@4.0.21': + resolution: {integrity: sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -791,8 +776,8 @@ packages: '@types/node': optional: true - '@inquirer/prompts@7.8.2': - resolution: {integrity: sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==} + '@inquirer/prompts@7.9.0': + resolution: {integrity: sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -800,8 +785,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.1.5': - resolution: {integrity: sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==} + '@inquirer/rawlist@4.1.9': + resolution: {integrity: sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -809,8 +794,8 @@ packages: '@types/node': optional: true - '@inquirer/search@3.1.0': - resolution: {integrity: sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==} + '@inquirer/search@3.2.0': + resolution: {integrity: sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -818,8 +803,8 @@ packages: '@types/node': optional: true - '@inquirer/select@4.3.1': - resolution: {integrity: sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==} + '@inquirer/select@4.4.0': + resolution: {integrity: sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -827,8 +812,8 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.8': - resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} + '@inquirer/type@3.0.9': + resolution: {integrity: sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -848,34 +833,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@isaacs/fs-minipass@4.0.1': - resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} - engines: {node: '>=18.0.0'} - - '@jest/diff-sequences@30.0.1': - resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect-utils@30.0.5': - resolution: {integrity: sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/get-type@30.0.1': - resolution: {integrity: sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/pattern@30.0.1': - resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/types@30.0.5': - resolution: {integrity: sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -892,8 +849,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -1013,14 +970,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@promptbook/utils@0.69.5': - resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==} - - '@puppeteer/browsers@2.10.6': - resolution: {integrity: sha512-pHUn6ZRt39bP3698HFQlu2ZHCkS/lPcpv7fVQcGBSzNNygw171UXAKrCUhy+TEMw4lEttOKDgNpb04hwUAJeiQ==} - engines: {node: '>=18'} - hasBin: true - '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -1045,8 +994,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -1054,121 +1003,115 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.46.2': - resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.46.2': - resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.46.2': - resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.46.2': - resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.46.2': - resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.46.2': - resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': - resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.46.2': - resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.46.2': - resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.46.2': - resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': - resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.2': - resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.46.2': - resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.46.2': - resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.46.2': - resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.46.2': - resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.46.2': - resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.46.2': - resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.46.2': - resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.46.2': - resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@sinclair/typebox@0.34.39': - resolution: {integrity: sha512-keEoFsevmLwAedzacnTVmra66GViRH3fhWO1M+nZ8rUgpPJyN4mcvqlGr3QMrQXx4L8KNwW0q9/BeHSEoO4teg==} - - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - - '@swc/core-darwin-arm64@1.13.3': - resolution: {integrity: sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + cpu: [x64] + os: [win32] '@swc/core-darwin-arm64@1.13.5': resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} @@ -1176,123 +1119,60 @@ packages: cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.13.3': - resolution: {integrity: sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - '@swc/core-darwin-x64@1.13.5': resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.13.3': - resolution: {integrity: sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - '@swc/core-linux-arm-gnueabihf@1.13.5': resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.13.3': - resolution: {integrity: sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - '@swc/core-linux-arm64-gnu@1.13.5': resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.13.3': - resolution: {integrity: sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - '@swc/core-linux-arm64-musl@1.13.5': resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.13.3': - resolution: {integrity: sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - '@swc/core-linux-x64-gnu@1.13.5': resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.13.3': - resolution: {integrity: sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - '@swc/core-linux-x64-musl@1.13.5': resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.13.3': - resolution: {integrity: sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - '@swc/core-win32-arm64-msvc@1.13.5': resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.13.3': - resolution: {integrity: sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - '@swc/core-win32-ia32-msvc@1.13.5': resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.13.3': - resolution: {integrity: sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - '@swc/core-win32-x64-msvc@1.13.5': resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.13.3': - resolution: {integrity: sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==} - engines: {node: '>=10'} - peerDependencies: - '@swc/helpers': '>=0.5.17' - peerDependenciesMeta: - '@swc/helpers': - optional: true - '@swc/core@1.13.5': resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} engines: {node: '>=10'} @@ -1305,68 +1185,68 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/types@0.1.24': - resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@tailwindcss/node@4.1.12': - resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + '@tailwindcss/node@4.1.15': + resolution: {integrity: sha512-HF4+7QxATZWY3Jr8OlZrBSXmwT3Watj0OogeDvdUY/ByXJHQ+LBtqA2brDb3sBxYslIFx6UP94BJ4X6a4L9Bmw==} - '@tailwindcss/oxide-android-arm64@4.1.12': - resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + '@tailwindcss/oxide-android-arm64@4.1.15': + resolution: {integrity: sha512-TkUkUgAw8At4cBjCeVCRMc/guVLKOU1D+sBPrHt5uVcGhlbVKxrCaCW9OKUIBv1oWkjh4GbunD/u/Mf0ql6kEA==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.12': - resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + '@tailwindcss/oxide-darwin-arm64@4.1.15': + resolution: {integrity: sha512-xt5XEJpn2piMSfvd1UFN6jrWXyaKCwikP4Pidcf+yfHTSzSpYhG3dcMktjNkQO3JiLCp+0bG0HoWGvz97K162w==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.12': - resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + '@tailwindcss/oxide-darwin-x64@4.1.15': + resolution: {integrity: sha512-TnWaxP6Bx2CojZEXAV2M01Yl13nYPpp0EtGpUrY+LMciKfIXiLL2r/SiSRpagE5Fp2gX+rflp/Os1VJDAyqymg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.12': - resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + '@tailwindcss/oxide-freebsd-x64@4.1.15': + resolution: {integrity: sha512-quISQDWqiB6Cqhjc3iWptXVZHNVENsWoI77L1qgGEHNIdLDLFnw3/AfY7DidAiiCIkGX/MjIdB3bbBZR/G2aJg==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': - resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15': + resolution: {integrity: sha512-ObG76+vPlab65xzVUQbExmDU9FIeYLQ5k2LrQdR2Ud6hboR+ZobXpDoKEYXf/uOezOfIYmy2Ta3w0ejkTg9yxg==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': - resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.15': + resolution: {integrity: sha512-4WbBacRmk43pkb8/xts3wnOZMDKsPFyEH/oisCm2q3aLZND25ufvJKcDUpAu0cS+CBOL05dYa8D4U5OWECuH/Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.12': - resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.15': + resolution: {integrity: sha512-AbvmEiteEj1nf42nE8skdHv73NoR+EwXVSgPY6l39X12Ex8pzOwwfi3Kc8GAmjsnsaDEbk+aj9NyL3UeyHcTLg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.12': - resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.15': + resolution: {integrity: sha512-+rzMVlvVgrXtFiS+ES78yWgKqpThgV19ISKD58Ck+YO5pO5KjyxLt7AWKsWMbY0R9yBDC82w6QVGz837AKQcHg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.12': - resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + '@tailwindcss/oxide-linux-x64-musl@4.1.15': + resolution: {integrity: sha512-fPdEy7a8eQN9qOIK3Em9D3TO1z41JScJn8yxl/76mp4sAXFDfV4YXxsiptJcOwy6bGR+70ZSwFIZhTXzQeqwQg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.12': - resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + '@tailwindcss/oxide-wasm32-wasi@4.1.15': + resolution: {integrity: sha512-sJ4yd6iXXdlgIMfIBXuVGp/NvmviEoMVWMOAGxtxhzLPp9LOj5k0pMEMZdjeMCl4C6Up+RM8T3Zgk+BMQ0bGcQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1377,27 +1257,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': - resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.15': + resolution: {integrity: sha512-sJGE5faXnNQ1iXeqmRin7Ds/ru2fgCiaQZQQz3ZGIDtvbkeV85rAZ0QJFMDg0FrqsffZG96H1U9AQlNBRLsHVg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.12': - resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.15': + resolution: {integrity: sha512-NLeHE7jUV6HcFKS504bpOohyi01zPXi2PXmjFfkzTph8xRxDdxkRsXm/xDO5uV5K3brrE1cCwbUYmFUSHR3u1w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.12': - resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + '@tailwindcss/oxide@4.1.15': + resolution: {integrity: sha512-krhX+UOOgnsUuks2SR7hFafXmLQrKxB4YyRTERuCE59JlYL+FawgaAlSkOYmDRJdf1Q+IFNDMl9iRnBW7QBDfQ==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.12': - resolution: {integrity: sha512-5PpLYhCAwf9SJEeIsSmCDLgyVfdBhdBpzX1OJ87anT9IVR0Z9pjM0FNixCAUAHGnMBGB8K99SwAheXrT0Kh6QQ==} - - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tailwindcss/postcss@4.1.15': + resolution: {integrity: sha512-IZh8IT76KujRz6d15wZw4eoeViT4TqmzVWNNfpuNCTKiaZUwgr5vtPqO4HjuYDyx3MgGR5qgPt1HMzTeLJyA3g==} '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -1411,8 +1288,8 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@types/chrome@0.1.4': - resolution: {integrity: sha512-vfISO7SPppN3OKVUqWujtZ4vux3nhDqKaHYEHgfQuPARHuWJ3jjyc1s13H0ckzEc86/neTkCl1TeW72UK6jYKA==} + '@types/chrome@0.1.24': + resolution: {integrity: sha512-9iO9HL2bMeGS4C8m6gNFWUyuPE5HEUFk+rGh+7oriUjg+ata4Fc9PoVlu8xvGm7yoo3AmS3J6fAjoFj61NL2rw==} '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -1435,46 +1312,19 @@ packages: '@types/har-format@1.2.16': resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/mocha@10.0.10': - resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - - '@types/node@20.19.11': - resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} + '@types/node@24.9.1': + resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==} - '@types/node@24.3.0': - resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/react-dom@19.1.7': - resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==} + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} peerDependencies: - '@types/react': ^19.0.0 - - '@types/react@19.1.10': - resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==} - - '@types/sinonjs__fake-timers@8.1.5': - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + '@types/react': ^19.2.0 - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - - '@types/which@2.0.2': - resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1485,9 +1335,6 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@vitejs/plugin-react-swc@3.11.0': resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} peerDependencies: @@ -1499,84 +1346,6 @@ packages: peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - '@vitest/pretty-format@2.1.9': - resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - - '@vitest/snapshot@2.1.9': - resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - - '@wdio/cli@9.19.1': - resolution: {integrity: sha512-1JDvutIp1mYk2f3KaBdcHAMw9UQlAMFnXbB4byuOMgik75HIgF+mrsasHj8wzfJTm9BbLwQ2h/6yGLHPTXvc0g==} - engines: {node: '>=18.20.0'} - hasBin: true - - '@wdio/config@9.19.1': - resolution: {integrity: sha512-BeTB2paSjaij3cf1NXQzX9CZmdj5jz2/xdUhkJlCeGmGn1KjWu5BjMO+exuiy+zln7dOJjev8f0jlg8e8f1EbQ==} - engines: {node: '>=18.20.0'} - - '@wdio/dot-reporter@9.19.1': - resolution: {integrity: sha512-NwobLDl6LYKIkDgsfJP6cc5BIWBi3Mwfdub+ROv6CilCJozebXr0DqIYzLmsEzMZSuMKo31nkNx7sGL/kKJE/A==} - engines: {node: '>=18.20.0'} - - '@wdio/globals@9.17.0': - resolution: {integrity: sha512-i38o7wlipLllNrk2hzdDfAmk6nrqm3lR2MtAgWgtHbwznZAKkB84KpkNFfmUXw5Kg3iP1zKlSjwZpKqenuLc+Q==} - engines: {node: '>=18.20.0'} - peerDependencies: - expect-webdriverio: ^5.3.4 - webdriverio: ^9.0.0 - - '@wdio/local-runner@9.19.1': - resolution: {integrity: sha512-evAkWNE/5FTS7uT0yIrpgdVk0QyO38qRMSzpBmDzYk2HGJnIPnKUy1WOWX0ExOyL8MHDoBAuYdFHoDf5U5BghQ==} - engines: {node: '>=18.20.0'} - - '@wdio/logger@9.18.0': - resolution: {integrity: sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==} - engines: {node: '>=18.20.0'} - - '@wdio/mocha-framework@9.19.1': - resolution: {integrity: sha512-kmiw+ITVvqGMijMermYZGowcOvIgZzdy/2dLaVkcBa8Jvask34uk7lUO554cycw+EHsJ9KmHcC/862f+W5UVlg==} - engines: {node: '>=18.20.0'} - - '@wdio/protocols@9.16.2': - resolution: {integrity: sha512-h3k97/lzmyw5MowqceAuY3HX/wGJojXHkiPXA3WlhGPCaa2h4+GovV2nJtRvknCKsE7UHA1xB5SWeI8MzloBew==} - - '@wdio/repl@9.16.2': - resolution: {integrity: sha512-FLTF0VL6+o5BSTCO7yLSXocm3kUnu31zYwzdsz4n9s5YWt83sCtzGZlZpt7TaTzb3jVUfxuHNQDTb8UMkCu0lQ==} - engines: {node: '>=18.20.0'} - - '@wdio/reporter@9.19.1': - resolution: {integrity: sha512-nv5TZg+rUUlC3NGNDP2DGzpd2grU/3D27M9MsPV37TjGLccEVZYlbu2BnK3Y9mSqXWt7CZuFC4GXBF+5vQG6gw==} - engines: {node: '>=18.20.0'} - - '@wdio/runner@9.19.1': - resolution: {integrity: sha512-WawjlT72DVSS9PLrMggSsPOKhhB1TA1o3ASjX/KgqdAUmrg5PQdF+tcA15KePwwmLcu0OLulna8Pj+QO1ZMAAg==} - engines: {node: '>=18.20.0'} - peerDependencies: - expect-webdriverio: ^5.3.4 - webdriverio: ^9.0.0 - - '@wdio/spec-reporter@9.19.1': - resolution: {integrity: sha512-lxi/4g+kkQHRczbWPw36VYuJRDd1iotOeKbauWgz+IluJqyNw3G9M52hxn2JeAfdXob4Az0Ey3iFcfjMOCIETQ==} - engines: {node: '>=18.20.0'} - - '@wdio/types@9.19.1': - resolution: {integrity: sha512-Q1HVcXiWMHp3ze2NN1BvpsfEh/j6GtAeMHhHW4p2IWUfRZlZqTfiJ+95LmkwXOG2gw9yndT8NkJigAz8v7WVYQ==} - engines: {node: '>=18.20.0'} - - '@wdio/utils@9.19.1': - resolution: {integrity: sha512-wWx5uPCgdZQxFIemAFVk/aa3JLwqrTsvEJsPlV3lCRpLeQ67V8aUPvvNAzE+RhX67qvelwwsvX8RrPdLDfnnYw==} - engines: {node: '>=18.20.0'} - - '@wdio/xvfb@9.19.1': - resolution: {integrity: sha512-hQ711tIXUEYVwOFItnOd8x+X4YPJX0wYcJGf+4/niPOz5s7NNZwgDewO4TnoLTkmLiK0MSRw9nX4sv2v+uI+iw==} - engines: {node: '>=18'} - '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -1628,14 +1397,6 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - '@zip.js/zip.js@2.7.72': - resolution: {integrity: sha512-3/A4JwrgkvGBlCxtItjxs8HrNbuTAAl/zlGkV6tC5Fb5k5nk4x2Dqxwl/YnUys5Ch+QB01eJ8Q5K/J2uXfy9Vw==} - engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn-import-phases@1.0.4: resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} engines: {node: '>=10.13.0'} @@ -1651,10 +1412,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1671,32 +1428,20 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: @@ -1706,27 +1451,12 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - archiver-utils@5.0.2: - resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} - engines: {node: '>= 14'} - - archiver@7.0.1: - resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} - engines: {node: '>= 14'} - arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -1737,17 +1467,6 @@ packages: assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - - async-exit-hook@2.0.1: - resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} - engines: {node: '>=0.12.0'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - autoprefixer@10.4.21: resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} @@ -1759,48 +1478,15 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - b4a@1.6.7: - resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.6.1: - resolution: {integrity: sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==} - - bare-fs@4.2.0: - resolution: {integrity: sha512-oRfrw7gwwBVAWx9S5zPMo2iiOjxyiZE12DmblmMQREgcogbNO0AFaZ+QBxxkEXiPspcpvO/Qtqn8LabUx4uYXg==} - engines: {bare: '>=1.16.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-os@3.6.1: - resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} - engines: {bare: '>=1.14.0'} - - bare-path@3.0.0: - resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - - bare-stream@2.7.0: - resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} - peerDependencies: - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - bare-events: - optional: true - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} + baseline-browser-mapping@2.8.19: + resolution: {integrity: sha512-zoKGUdu6vb2jd3YOq0nnhEDQVbPcHhco3UImJrv5dSkvxTc2pl2WjOPsjZXDwPDSl5eghIMuY3R6J9NDKF3KcQ==} + hasBin: true binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -1812,12 +1498,6 @@ packages: bn.js@5.2.2: resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1831,9 +1511,6 @@ packages: browser-resolve@2.0.0: resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} - browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - browserify-aes@1.2.0: resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} @@ -1847,25 +1524,18 @@ packages: resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} engines: {node: '>= 0.10'} - browserify-sign@4.2.3: - resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} - engines: {node: '>= 0.12'} + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.25.2: - resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-crc32@1.0.0: - resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} - engines: {node: '>=8.0.0'} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1875,9 +1545,6 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - builtin-status-codes@3.0.0: resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} @@ -1897,31 +1564,16 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001735: - resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + caniuse-lite@1.0.30001751: + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.5.0: - resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} - cheerio-select@2.1.0: - resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - - cheerio@1.1.2: - resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} - engines: {node: '>=20.18.1'} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1930,37 +1582,22 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chownr@3.0.0: - resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} - engines: {node: '>=18'} - chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - ci-info@4.3.0: - resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} - engines: {node: '>=8'} - - cipher-base@1.0.6: - resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==} + cipher-base@1.0.7: + resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} engines: {node: '>= 0.10'} cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1976,10 +1613,6 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} - engines: {node: '>=20'} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -1991,13 +1624,6 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - compress-commons@6.0.2: - resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} - engines: {node: '>= 14'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -2007,21 +1633,9 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - - crc32-stream@6.0.0: - resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} - engines: {node: '>= 14'} - create-ecdh@4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - create-hash@1.1.3: - resolution: {integrity: sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==} - create-hash@1.2.0: resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} @@ -2031,11 +1645,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - create-wdio@9.18.2: - resolution: {integrity: sha512-atf81YJfyTNAJXsNu3qhpqF4OO43tHGTpr88duAc1Hk4a0uXJAPUYLnYxshOuMnfmeAxlWD+NqGU7orRiXEuJg==} - engines: {node: '>=12.0.0'} - hasBin: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2044,19 +1653,6 @@ packages: resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} engines: {node: '>= 0.10'} - css-select@5.2.2: - resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - - css-shorthand-properties@1.1.2: - resolution: {integrity: sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==} - - css-value@0.0.1: - resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} - - css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} - engines: {node: '>= 6'} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2065,46 +1661,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - - decamelize@6.0.0: - resolution: {integrity: sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - - deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} - engines: {node: '>=16.0.0'} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -2113,10 +1673,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - des.js@1.1.0: resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} @@ -2125,8 +1681,8 @@ packages: engines: {node: '>=0.10'} hasBin: true - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} didyoumean@1.2.2: @@ -2136,14 +1692,6 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - - diff@8.0.2: - resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} - engines: {node: '>=0.3.1'} - diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} @@ -2154,25 +1702,12 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - domain-browser@4.22.0: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - - dotenv@17.2.1: - resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -2182,29 +1717,12 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - easy-table@1.2.0: - resolution: {integrity: sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==} - - eciesjs@0.4.15: - resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} + eciesjs@0.4.16: + resolution: {integrity: sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} - edge-paths@3.0.5: - resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} - engines: {node: '>=14.0.0'} - - edgedriver@6.1.2: - resolution: {integrity: sha512-UvFqd/IR81iPyWMcxXbUNi+xKWR7JjfoHjfuwjqsj9UHQKn80RpQmS0jf+U25IPi+gKVPcpOSKm0XkqgGMq4zQ==} - engines: {node: '>=18.0.0'} - hasBin: true - - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-to-chromium@1.5.202: - resolution: {integrity: sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==} + electron-to-chromium@1.5.237: + resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -2215,27 +1733,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encoding-sniffer@0.2.1: - resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2251,8 +1752,8 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild@0.25.9: - resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} hasBin: true @@ -2260,19 +1761,10 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -2281,11 +1773,6 @@ packages: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -2301,14 +1788,6 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -2320,57 +1799,19 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} - engines: {node: ^18.19.0 || >=20.5.0} - - exit-hook@4.0.0: - resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==} - engines: {node: '>=18'} - - expect-webdriverio@5.4.2: - resolution: {integrity: sha512-7bc5I2dU3onKJaRhBdxKh/C+W+ot7R+RcRMCLTSR7cbfHM9Shk8ocbNDVvjrxaBdA52kbZONVSyhexp7cq2xNA==} - engines: {node: '>=18 || >=20 || >=22'} - peerDependencies: - '@wdio/globals': ^9.0.0 - '@wdio/logger': ^9.0.0 - webdriverio: ^9.0.0 - - expect@30.0.5: - resolution: {integrity: sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - - fast-deep-equal@2.0.1: - resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} - - fast-xml-parser@5.2.5: - resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} - hasBin: true + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2380,23 +1821,12 @@ packages: picomatch: optional: true - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2405,14 +1835,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -2421,16 +1843,9 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2439,10 +1854,9 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - geckodriver@5.0.0: - resolution: {integrity: sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ==} - engines: {node: '>=18.0.0'} - hasBin: true + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -2452,32 +1866,16 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-port@7.1.0: - resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} - engines: {node: '>=16'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - - get-tsconfig@4.10.1: - resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} - - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} + get-tsconfig@4.12.0: + resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2499,11 +1897,6 @@ packages: engines: {node: 20 || >=22} hasBin: true - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2515,9 +1908,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2533,13 +1923,14 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hash-base@2.0.2: - resolution: {integrity: sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==} - hash-base@3.0.5: resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} engines: {node: '>= 0.10'} + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} @@ -2547,48 +1938,18 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} - engines: {node: ^16.14.0 || >=18.0.0} - - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} - - htmlfy@0.8.1: - resolution: {integrity: sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==} - - htmlparser2@10.0.0: - resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - https-browserify@1.0.0: resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} - engines: {node: '>=18.18.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} idb@8.0.3: @@ -2601,42 +1962,16 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - - immutable@5.1.3: - resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} - - import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inquirer@12.9.2: - resolution: {integrity: sha512-XPukbomHpZc3GAajQdAcuqa5NCIFhUcLMcXXSpJLM2RW/u/5JHLxjLF206GNTJARib8XBBRqyMbaNrDzXROdoA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} - engines: {node: '>= 12'} - is-arguments@1.2.0: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -2645,10 +1980,6 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-ci@4.1.0: - resolution: {integrity: sha512-Ab9bQDQ11lWootZUI5qxgN2ZXwxNI5hTwnsvOc1wyxQ7zQ8OkEDw79mI0+9jI3x432NfwbVRru+3noJfXF6lSQ==} - hasBin: true - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -2661,8 +1992,8 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -2677,14 +2008,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2693,22 +2016,10 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -2733,35 +2044,6 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} - jake@10.9.4: - resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} - engines: {node: '>=10'} - hasBin: true - - jest-diff@30.0.5: - resolution: {integrity: sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-matcher-utils@30.0.5: - resolution: {integrity: sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-message-util@30.0.5: - resolution: {integrity: sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-mock@30.0.5: - resolution: {integrity: sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-util@30.0.5: - resolution: {integrity: sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -2770,99 +2052,84 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.5.1: - resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} - hasBin: true - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - jszip@3.10.1: - resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - - lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] - lightningcss-darwin-arm64@1.30.1: - resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.30.1: - resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.30.1: - resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.30.1: - resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.30.1: - resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.30.1: - resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.30.1: - resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.30.1: - resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.30.1: - resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.30.1: - resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.30.1: - resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} lilconfig@3.1.3: @@ -2872,71 +2139,27 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lines-and-columns@2.0.4: - resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - loader-runner@4.3.0: - resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} loader-utils@3.3.1: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} - locate-app@2.5.0: - resolution: {integrity: sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - - lodash.flattendeep@4.4.0: - resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} - - lodash.pickby@4.6.0: - resolution: {integrity: sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==} - - lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - - lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - loglevel-plugin-prefix@0.8.4: - resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} - - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} engines: {node: 20 || >=22} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -2985,13 +2208,6 @@ packages: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3000,30 +2216,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@3.0.2: - resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} - engines: {node: '>= 18'} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - - mocha@10.8.2: - resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} - engines: {node: '>= 14.0.0'} - hasBin: true - mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -3043,37 +2239,16 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.26: + resolution: {integrity: sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==} node-stdlib-browser@1.3.1: resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} engines: {node: '>=10'} - normalize-package-data@6.0.2: - resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} - engines: {node: ^16.14.0 || >=18.0.0} - - normalize-package-data@7.0.1: - resolution: {integrity: sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==} - engines: {node: ^18.17.0 || >=20.5.0} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3086,13 +2261,6 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3121,9 +2289,6 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -3135,53 +2300,20 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parse-asn1@5.1.7: - resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} engines: {node: '>= 0.10'} - parse-json@7.1.1: - resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} - engines: {node: '>=16'} - - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - - parse5-htmlparser2-tree-adapter@7.1.0: - resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} - - parse5-parser-stream@7.1.2: - resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -3189,18 +2321,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -3216,18 +2340,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - pbkdf2@3.1.3: - resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==} - engines: {node: '>=0.12'} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3266,22 +2381,28 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-nested@6.2.0: @@ -3301,14 +2422,6 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - pretty-format@30.0.5: - resolution: {integrity: sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - pretty-ms@9.2.0: - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} - engines: {node: '>=18'} - process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -3316,23 +2429,9 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - public-encrypt@4.0.3: resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -3340,9 +2439,6 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - query-selector-shadow-dom@1.0.1: - resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - querystring-es3@0.2.1: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} @@ -3360,13 +2456,10 @@ packages: randomfill@1.0.4: resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: - react: ^19.1.1 - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react: ^19.2.0 react-resizable-panels@2.1.9: resolution: {integrity: sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==} @@ -3380,21 +2473,13 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - read-pkg-up@10.1.0: - resolution: {integrity: sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==} - engines: {node: '>=16'} - - read-pkg@8.1.0: - resolution: {integrity: sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==} - engines: {node: '>=16'} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -3402,13 +2487,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -3417,10 +2495,6 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - recursive-readdir@2.2.3: - resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} - engines: {node: '>=6.0.0'} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3432,55 +2506,32 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true - resq@1.11.0: - resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} - - ret@0.5.0: - resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} - engines: {node: '>=10'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rgb2hex@0.2.5: - resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} - rimraf@6.0.1: resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} engines: {node: 20 || >=22} hasBin: true - ripemd160@2.0.1: - resolution: {integrity: sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==} + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} - ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - - rollup@4.46.2: - resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-async@4.0.6: - resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} - engines: {node: '>=0.12.0'} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - safaridriver@1.0.0: - resolution: {integrity: sha512-J92IFbskyo7OYB3Dt4aTdyhag1GlInrfbPCmMteb7aBK7PwlnGz1HI0+oyNN97j7pV9DqUAVoVgkNRMrfY47mQ==} - engines: {node: '>=18.0.0'} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -3491,33 +2542,26 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safe-regex2@5.0.0: - resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.90.0: - resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==} + sass@1.93.2: + resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} engines: {node: '>=14.0.0'} hasBin: true - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - schema-utils@4.3.2: - resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true - serialize-error@12.0.0: - resolution: {integrity: sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==} - engines: {node: '>=18'} - serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -3568,18 +2612,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3595,42 +2627,12 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} - spacetrim@0.11.59: - resolution: {integrity: sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - stream-buffers@3.0.3: - resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} - engines: {node: '>= 0.10.0'} - stream-http@3.2.0: resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} - streamx@2.22.1: - resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3649,25 +2651,14 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strnum@2.1.1: - resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} - sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -3688,28 +2679,18 @@ packages: tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.12: - resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + tailwindcss@4.1.15: + resolution: {integrity: sha512-k2WLnWkYFkdpRv+Oby3EBXIyQC8/s1HOFMBUViwtAh6Z5uAozeUSMQlIsn/c6Q2iJzqG6aJT3wdPaRNj70iYxQ==} - tapable@2.2.2: - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tar-fs@3.1.0: - resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} - - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - - tar@7.4.3: - resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} - engines: {node: '>=18'} - terser-webpack-plugin@5.3.14: resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} engines: {node: '>= 10.13.0'} @@ -3726,14 +2707,11 @@ packages: uglify-js: optional: true - terser@5.43.1: - resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} hasBin: true - text-decoder@1.2.3: - resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -3749,16 +2727,8 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - - to-buffer@1.2.1: - resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==} + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} engines: {node: '>= 0.4'} to-regex-range@5.0.1: @@ -3768,8 +2738,8 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-loader@9.5.2: - resolution: {integrity: sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==} + ts-loader@9.5.4: + resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} engines: {node: '>=12.0.0'} peerDependencies: typescript: '*' @@ -3794,63 +2764,48 @@ packages: engines: {node: '>=16.20.2'} hasBin: true - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.20.4: - resolution: {integrity: sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} engines: {node: '>=18.0.0'} hasBin: true tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - turbo-darwin-64@2.5.6: - resolution: {integrity: sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A==} + turbo-darwin-64@2.5.8: + resolution: {integrity: sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.5.6: - resolution: {integrity: sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA==} + turbo-darwin-arm64@2.5.8: + resolution: {integrity: sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.5.6: - resolution: {integrity: sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA==} + turbo-linux-64@2.5.8: + resolution: {integrity: sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.5.6: - resolution: {integrity: sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ==} + turbo-linux-arm64@2.5.8: + resolution: {integrity: sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ==} cpu: [arm64] os: [linux] - turbo-windows-64@2.5.6: - resolution: {integrity: sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg==} + turbo-windows-64@2.5.8: + resolution: {integrity: sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.5.6: - resolution: {integrity: sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q==} + turbo-windows-arm64@2.5.8: + resolution: {integrity: sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ==} cpu: [arm64] os: [win32] - turbo@2.5.6: - resolution: {integrity: sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w==} + turbo@2.5.8: + resolution: {integrity: sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w==} hasBin: true - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} - - type-fest@4.26.0: - resolution: {integrity: sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==} - engines: {node: '>=16'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -3859,28 +2814,13 @@ packages: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - undici-types@7.10.0: - resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - - undici@6.21.3: - resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} - engines: {node: '>=18.17'} - - undici@7.13.0: - resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==} - engines: {node: '>=20.18.1'} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} @@ -3892,13 +2832,6 @@ packages: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} - urlpattern-polyfill@10.1.0: - resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} - - userhome@1.0.1: - resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==} - engines: {node: '>= 0.8.0'} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3908,16 +2841,13 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vite-plugin-node-polyfills@0.22.0: resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} peerDependencies: vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - vite@7.1.9: - resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} + vite@7.1.11: + resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3959,35 +2889,10 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - wait-port@1.1.0: - resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} - engines: {node: '>=10'} - hasBin: true - watchpack@2.4.4: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - webdriver@9.19.1: - resolution: {integrity: sha512-cvccIZ3QaUZxxrA81a3rqqgxKt6VzVrZupMc+eX9J40qfGrV3NtdLb/m4AA1PmeTPGN5O3/4KrzDpnVZM4WUnA==} - engines: {node: '>=18.20.0'} - - webdriverio@9.19.1: - resolution: {integrity: sha512-hpGgK6d9QNi3AaLFWIPQaEMqJhXF048XAIsV5i5mkL0kjghV1opcuhKgbbG+7pcn8JSpiq6mh7o3MDYtapw90w==} - engines: {node: '>=18.20.0'} - peerDependencies: - puppeteer-core: '>=22.x || <=24.x' - peerDependenciesMeta: - puppeteer-core: - optional: true - webextension-polyfill@0.12.0: resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} @@ -3995,8 +2900,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.101.2: - resolution: {integrity: sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==} + webpack@5.102.1: + resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -4005,14 +2910,6 @@ packages: webpack-cli: optional: true - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} @@ -4027,14 +2924,6 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true - which@5.0.0: - resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} - engines: {node: ^18.17.0 || >=20.5.0} - hasBin: true - - workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -4047,9 +2936,6 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -4070,38 +2956,14 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@5.0.0: - resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} - engines: {node: '>=18'} - - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} - engines: {node: '>= 14.6'} - hasBin: true - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -4110,43 +2972,23 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.1: - resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} - engines: {node: '>=12.20'} - - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - - yoctocolors@2.1.1: - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} - zip-stream@6.0.1: - resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} - engines: {node: '>= 14'} - snapshots: '@alloc/quick-lru@5.2.0': {} - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/helper-validator-identifier@7.27.1': {} - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@dotenvx/dotenvx@1.48.4': + '@dotenvx/dotenvx@1.51.0': dependencies: commander: 11.1.0 - dotenv: 17.2.1 - eciesjs: 0.4.15 + dotenv: 17.2.3 + eciesjs: 0.4.16 execa: 5.1.1 fdir: 6.5.0(picomatch@4.0.3) ignore: 5.3.2 @@ -4158,206 +3000,208 @@ snapshots: dependencies: '@noble/ciphers': 1.3.0 - '@esbuild/aix-ppc64@0.25.9': + '@esbuild/aix-ppc64@0.25.11': optional: true - '@esbuild/android-arm64@0.25.9': + '@esbuild/android-arm64@0.25.11': optional: true - '@esbuild/android-arm@0.25.9': + '@esbuild/android-arm@0.25.11': optional: true - '@esbuild/android-x64@0.25.9': + '@esbuild/android-x64@0.25.11': optional: true - '@esbuild/darwin-arm64@0.25.9': + '@esbuild/darwin-arm64@0.25.11': optional: true - '@esbuild/darwin-x64@0.25.9': + '@esbuild/darwin-x64@0.25.11': optional: true - '@esbuild/freebsd-arm64@0.25.9': + '@esbuild/freebsd-arm64@0.25.11': optional: true - '@esbuild/freebsd-x64@0.25.9': + '@esbuild/freebsd-x64@0.25.11': optional: true - '@esbuild/linux-arm64@0.25.9': + '@esbuild/linux-arm64@0.25.11': optional: true - '@esbuild/linux-arm@0.25.9': + '@esbuild/linux-arm@0.25.11': optional: true - '@esbuild/linux-ia32@0.25.9': + '@esbuild/linux-ia32@0.25.11': optional: true - '@esbuild/linux-loong64@0.25.9': + '@esbuild/linux-loong64@0.25.11': optional: true - '@esbuild/linux-mips64el@0.25.9': + '@esbuild/linux-mips64el@0.25.11': optional: true - '@esbuild/linux-ppc64@0.25.9': + '@esbuild/linux-ppc64@0.25.11': optional: true - '@esbuild/linux-riscv64@0.25.9': + '@esbuild/linux-riscv64@0.25.11': optional: true - '@esbuild/linux-s390x@0.25.9': + '@esbuild/linux-s390x@0.25.11': optional: true - '@esbuild/linux-x64@0.25.9': + '@esbuild/linux-x64@0.25.11': optional: true - '@esbuild/netbsd-arm64@0.25.9': + '@esbuild/netbsd-arm64@0.25.11': optional: true - '@esbuild/netbsd-x64@0.25.9': + '@esbuild/netbsd-x64@0.25.11': optional: true - '@esbuild/openbsd-arm64@0.25.9': + '@esbuild/openbsd-arm64@0.25.11': optional: true - '@esbuild/openbsd-x64@0.25.9': + '@esbuild/openbsd-x64@0.25.11': optional: true - '@esbuild/openharmony-arm64@0.25.9': + '@esbuild/openharmony-arm64@0.25.11': optional: true - '@esbuild/sunos-x64@0.25.9': + '@esbuild/sunos-x64@0.25.11': optional: true - '@esbuild/win32-arm64@0.25.9': + '@esbuild/win32-arm64@0.25.11': optional: true - '@esbuild/win32-ia32@0.25.9': + '@esbuild/win32-ia32@0.25.11': optional: true - '@esbuild/win32-x64@0.25.9': + '@esbuild/win32-x64@0.25.11': optional: true - '@inquirer/checkbox@4.2.1(@types/node@24.3.0)': + '@inquirer/ansi@1.0.1': {} + + '@inquirer/checkbox@4.3.0(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@24.3.0) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 + '@inquirer/ansi': 1.0.1 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/figures': 1.0.14 + '@inquirer/type': 3.0.9(@types/node@24.9.1) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/confirm@5.1.14(@types/node@24.3.0)': + '@inquirer/confirm@5.1.19(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/core@10.1.15(@types/node@24.3.0)': + '@inquirer/core@10.3.0(@types/node@24.9.1)': dependencies: - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@24.3.0) - ansi-escapes: 4.3.2 + '@inquirer/ansi': 1.0.1 + '@inquirer/figures': 1.0.14 + '@inquirer/type': 3.0.9(@types/node@24.9.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/editor@4.2.17(@types/node@24.3.0)': + '@inquirer/editor@4.2.21(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/external-editor': 1.0.1(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/external-editor': 1.0.2(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/expand@4.0.17(@types/node@24.3.0)': + '@inquirer/expand@4.0.21(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/external-editor@1.0.1(@types/node@24.3.0)': + '@inquirer/external-editor@1.0.2(@types/node@24.9.1)': dependencies: chardet: 2.1.0 - iconv-lite: 0.6.3 + iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/figures@1.0.13': {} + '@inquirer/figures@1.0.14': {} - '@inquirer/input@4.2.1(@types/node@24.3.0)': + '@inquirer/input@4.2.5(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/number@3.0.17(@types/node@24.3.0)': + '@inquirer/number@3.0.21(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/password@4.0.17(@types/node@24.3.0)': + '@inquirer/password@4.0.21(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) - ansi-escapes: 4.3.2 + '@inquirer/ansi': 1.0.1 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 - - '@inquirer/prompts@7.8.2(@types/node@24.3.0)': - dependencies: - '@inquirer/checkbox': 4.2.1(@types/node@24.3.0) - '@inquirer/confirm': 5.1.14(@types/node@24.3.0) - '@inquirer/editor': 4.2.17(@types/node@24.3.0) - '@inquirer/expand': 4.0.17(@types/node@24.3.0) - '@inquirer/input': 4.2.1(@types/node@24.3.0) - '@inquirer/number': 3.0.17(@types/node@24.3.0) - '@inquirer/password': 4.0.17(@types/node@24.3.0) - '@inquirer/rawlist': 4.1.5(@types/node@24.3.0) - '@inquirer/search': 3.1.0(@types/node@24.3.0) - '@inquirer/select': 4.3.1(@types/node@24.3.0) + '@types/node': 24.9.1 + + '@inquirer/prompts@7.9.0(@types/node@24.9.1)': + dependencies: + '@inquirer/checkbox': 4.3.0(@types/node@24.9.1) + '@inquirer/confirm': 5.1.19(@types/node@24.9.1) + '@inquirer/editor': 4.2.21(@types/node@24.9.1) + '@inquirer/expand': 4.0.21(@types/node@24.9.1) + '@inquirer/input': 4.2.5(@types/node@24.9.1) + '@inquirer/number': 3.0.21(@types/node@24.9.1) + '@inquirer/password': 4.0.21(@types/node@24.9.1) + '@inquirer/rawlist': 4.1.9(@types/node@24.9.1) + '@inquirer/search': 3.2.0(@types/node@24.9.1) + '@inquirer/select': 4.4.0(@types/node@24.9.1) optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/rawlist@4.1.5(@types/node@24.3.0)': + '@inquirer/rawlist@4.1.9(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/type': 3.0.9(@types/node@24.9.1) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/search@3.1.0(@types/node@24.3.0)': + '@inquirer/search@3.2.0(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@24.3.0) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/figures': 1.0.14 + '@inquirer/type': 3.0.9(@types/node@24.9.1) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/select@4.3.1(@types/node@24.3.0)': + '@inquirer/select@4.4.0(@types/node@24.9.1)': dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@24.3.0) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 + '@inquirer/ansi': 1.0.1 + '@inquirer/core': 10.3.0(@types/node@24.9.1) + '@inquirer/figures': 1.0.14 + '@inquirer/type': 3.0.9(@types/node@24.9.1) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 - '@inquirer/type@3.0.8(@types/node@24.3.0)': + '@inquirer/type@3.0.9(@types/node@24.9.1)': optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 '@isaacs/balanced-match@4.0.1': {} @@ -4369,62 +3213,31 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@isaacs/fs-minipass@4.0.1': - dependencies: - minipass: 7.1.2 - - '@jest/diff-sequences@30.0.1': {} - - '@jest/expect-utils@30.0.5': - dependencies: - '@jest/get-type': 30.0.1 - - '@jest/get-type@30.0.1': {} - - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 24.3.0 - jest-regex-util: 30.0.1 - - '@jest/schemas@30.0.5': - dependencies: - '@sinclair/typebox': 0.34.39 - - '@jest/types@30.0.5': - dependencies: - '@jest/pattern': 30.0.1 - '@jest/schemas': 30.0.5 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 24.3.0 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - - '@jridgewell/gen-mapping@0.3.13': + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.11': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 @@ -4434,13 +3247,13 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@laynezh/vite-plugin-lib-assets@1.2.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1))': + '@laynezh/vite-plugin-lib-assets@1.2.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6))': dependencies: escape-string-regexp: 4.0.0 loader-utils: 3.3.1 mrmime: 1.0.1 - semver: 7.7.2 - vite: 7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) + semver: 7.7.3 + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6) '@noble/ciphers@1.3.0': {} @@ -4526,196 +3339,133 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@promptbook/utils@0.69.5': - dependencies: - spacetrim: 0.11.59 - - '@puppeteer/browsers@2.10.6': - dependencies: - debug: 4.4.1(supports-color@8.1.1) - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.5.0 - semver: 7.7.2 - tar-fs: 3.1.0 - yargs: 17.7.2 - transitivePeerDependencies: - - bare-buffer - - supports-color - '@rolldown/pluginutils@1.0.0-beta.27': {} '@rolldown/pluginutils@1.0.0-beta.35': {} - '@rollup/plugin-inject@5.0.5(rollup@4.46.2)': + '@rollup/plugin-inject@5.0.5(rollup@4.52.5)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.2) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) estree-walker: 2.0.2 - magic-string: 0.30.17 + magic-string: 0.30.19 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.5 - '@rollup/plugin-sucrase@5.0.2(rollup@4.46.2)': + '@rollup/plugin-sucrase@5.0.2(rollup@4.52.5)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.2) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) sucrase: 3.35.0 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.5 - '@rollup/pluginutils@5.2.0(rollup@4.46.2)': + '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.5 - '@rollup/rollup-android-arm-eabi@4.46.2': + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.46.2': + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.46.2': + '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.46.2': + '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.46.2': + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.46.2': + '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.46.2': + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.46.2': + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.46.2': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.46.2': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.46.2': + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.46.2': + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.46.2': + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.46.2': + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.46.2': + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.46.2': + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.46.2': + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.46.2': + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@sec-ant/readable-stream@0.4.1': {} - - '@sinclair/typebox@0.34.39': {} - - '@sindresorhus/merge-streams@4.0.0': {} - - '@swc/core-darwin-arm64@1.13.3': + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true - '@swc/core-darwin-arm64@1.13.5': + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true - '@swc/core-darwin-x64@1.13.3': + '@swc/core-darwin-arm64@1.13.5': optional: true '@swc/core-darwin-x64@1.13.5': optional: true - '@swc/core-linux-arm-gnueabihf@1.13.3': - optional: true - '@swc/core-linux-arm-gnueabihf@1.13.5': optional: true - '@swc/core-linux-arm64-gnu@1.13.3': - optional: true - '@swc/core-linux-arm64-gnu@1.13.5': optional: true - '@swc/core-linux-arm64-musl@1.13.3': - optional: true - '@swc/core-linux-arm64-musl@1.13.5': optional: true - '@swc/core-linux-x64-gnu@1.13.3': - optional: true - '@swc/core-linux-x64-gnu@1.13.5': optional: true - '@swc/core-linux-x64-musl@1.13.3': - optional: true - '@swc/core-linux-x64-musl@1.13.5': optional: true - '@swc/core-win32-arm64-msvc@1.13.3': - optional: true - '@swc/core-win32-arm64-msvc@1.13.5': optional: true - '@swc/core-win32-ia32-msvc@1.13.3': - optional: true - '@swc/core-win32-ia32-msvc@1.13.5': optional: true - '@swc/core-win32-x64-msvc@1.13.3': - optional: true - '@swc/core-win32-x64-msvc@1.13.5': optional: true - '@swc/core@1.13.3': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.24 - optionalDependencies: - '@swc/core-darwin-arm64': 1.13.3 - '@swc/core-darwin-x64': 1.13.3 - '@swc/core-linux-arm-gnueabihf': 1.13.3 - '@swc/core-linux-arm64-gnu': 1.13.3 - '@swc/core-linux-arm64-musl': 1.13.3 - '@swc/core-linux-x64-gnu': 1.13.3 - '@swc/core-linux-x64-musl': 1.13.3 - '@swc/core-win32-arm64-msvc': 1.13.3 - '@swc/core-win32-ia32-msvc': 1.13.3 - '@swc/core-win32-x64-msvc': 1.13.3 - '@swc/core@1.13.5': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.24 + '@swc/types': 0.1.25 optionalDependencies: '@swc/core-darwin-arm64': 1.13.5 '@swc/core-darwin-x64': 1.13.5 @@ -4730,83 +3480,78 @@ snapshots: '@swc/counter@0.1.3': {} - '@swc/types@0.1.24': + '@swc/types@0.1.25': dependencies: '@swc/counter': 0.1.3 - '@tailwindcss/node@4.1.12': + '@tailwindcss/node@4.1.15': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.18.3 - jiti: 2.5.1 - lightningcss: 1.30.1 - magic-string: 0.30.17 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.19 source-map-js: 1.2.1 - tailwindcss: 4.1.12 + tailwindcss: 4.1.15 - '@tailwindcss/oxide-android-arm64@4.1.12': + '@tailwindcss/oxide-android-arm64@4.1.15': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.12': + '@tailwindcss/oxide-darwin-arm64@4.1.15': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.12': + '@tailwindcss/oxide-darwin-x64@4.1.15': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.12': + '@tailwindcss/oxide-freebsd-x64@4.1.15': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.15': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + '@tailwindcss/oxide-linux-arm64-musl@4.1.15': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + '@tailwindcss/oxide-linux-x64-gnu@4.1.15': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.12': + '@tailwindcss/oxide-linux-x64-musl@4.1.15': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.12': + '@tailwindcss/oxide-wasm32-wasi@4.1.15': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.15': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + '@tailwindcss/oxide-win32-x64-msvc@4.1.15': optional: true - '@tailwindcss/oxide@4.1.12': - dependencies: - detect-libc: 2.0.4 - tar: 7.4.3 + '@tailwindcss/oxide@4.1.15': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.12 - '@tailwindcss/oxide-darwin-arm64': 4.1.12 - '@tailwindcss/oxide-darwin-x64': 4.1.12 - '@tailwindcss/oxide-freebsd-x64': 4.1.12 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 - '@tailwindcss/oxide-linux-x64-musl': 4.1.12 - '@tailwindcss/oxide-wasm32-wasi': 4.1.12 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - - '@tailwindcss/postcss@4.1.12': + '@tailwindcss/oxide-android-arm64': 4.1.15 + '@tailwindcss/oxide-darwin-arm64': 4.1.15 + '@tailwindcss/oxide-darwin-x64': 4.1.15 + '@tailwindcss/oxide-freebsd-x64': 4.1.15 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.15 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.15 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.15 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.15 + '@tailwindcss/oxide-linux-x64-musl': 4.1.15 + '@tailwindcss/oxide-wasm32-wasi': 4.1.15 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.15 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.15 + + '@tailwindcss/postcss@4.1.15': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.12 - '@tailwindcss/oxide': 4.1.12 + '@tailwindcss/node': 4.1.15 + '@tailwindcss/oxide': 4.1.15 postcss: 8.5.6 - tailwindcss: 4.1.12 - - '@tootallnate/quickjs-emscripten@0.23.0': {} + tailwindcss: 4.1.15 '@tsconfig/node10@1.0.11': {} @@ -4816,7 +3561,7 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@types/chrome@0.1.4': + '@types/chrome@0.1.24': dependencies: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 @@ -4843,47 +3588,23 @@ snapshots: '@types/har-format@1.2.16': {} - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - '@types/json-schema@7.0.15': {} - '@types/mocha@10.0.10': {} - - '@types/node@20.19.11': + '@types/node@24.9.1': dependencies: - undici-types: 6.21.0 + undici-types: 7.16.0 - '@types/node@24.3.0': + '@types/react-dom@19.2.2(@types/react@19.2.2)': dependencies: - undici-types: 7.10.0 + '@types/react': 19.2.2 - '@types/normalize-package-data@2.4.4': {} - - '@types/react-dom@19.1.7(@types/react@19.1.10)': - dependencies: - '@types/react': 19.1.10 - - '@types/react@19.1.10': + '@types/react@19.2.2': dependencies: csstype: 3.1.3 - '@types/sinonjs__fake-timers@8.1.5': {} - - '@types/stack-utils@2.0.3': {} - - '@types/which@2.0.2': {} - '@types/ws@8.18.1': dependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 '@types/yargs-parser@21.0.3': {} @@ -4891,211 +3612,22 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 24.3.0 - optional: true - - '@vitejs/plugin-react-swc@3.11.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1))': + '@vitejs/plugin-react-swc@3.11.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.27 - '@swc/core': 1.13.3 - vite: 7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) + '@swc/core': 1.13.5 + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react-swc@4.1.0(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1))': + '@vitejs/plugin-react-swc@4.1.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.35 '@swc/core': 1.13.5 - vite: 7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6) transitivePeerDependencies: - '@swc/helpers' - '@vitest/pretty-format@2.1.9': - dependencies: - tinyrainbow: 1.2.0 - - '@vitest/pretty-format@3.2.4': - dependencies: - tinyrainbow: 2.0.0 - - '@vitest/snapshot@2.1.9': - dependencies: - '@vitest/pretty-format': 2.1.9 - magic-string: 0.30.17 - pathe: 1.1.2 - - '@vitest/snapshot@3.2.4': - dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.17 - pathe: 2.0.3 - - '@wdio/cli@9.19.1(@types/node@24.3.0)(expect-webdriverio@5.4.2)': - dependencies: - '@vitest/snapshot': 2.1.9 - '@wdio/config': 9.19.1 - '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.1) - '@wdio/logger': 9.18.0 - '@wdio/protocols': 9.16.2 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - async-exit-hook: 2.0.1 - chalk: 5.5.0 - chokidar: 4.0.3 - create-wdio: 9.18.2(@types/node@24.3.0) - dotenv: 17.2.1 - import-meta-resolve: 4.1.0 - lodash.flattendeep: 4.4.0 - lodash.pickby: 4.6.0 - lodash.union: 4.6.0 - read-pkg-up: 10.1.0 - tsx: 4.20.4 - webdriverio: 9.19.1 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - bare-buffer - - bufferutil - - expect-webdriverio - - puppeteer-core - - supports-color - - utf-8-validate - - '@wdio/config@9.19.1': - dependencies: - '@wdio/logger': 9.18.0 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - deepmerge-ts: 7.1.5 - glob: 10.4.5 - import-meta-resolve: 4.1.0 - transitivePeerDependencies: - - bare-buffer - - supports-color - - '@wdio/dot-reporter@9.19.1': - dependencies: - '@wdio/reporter': 9.19.1 - '@wdio/types': 9.19.1 - chalk: 5.5.0 - - '@wdio/globals@9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.1)': - dependencies: - expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.1) - webdriverio: 9.19.1 - - '@wdio/local-runner@9.19.1(@wdio/globals@9.17.0)(webdriverio@9.19.1)': - dependencies: - '@types/node': 20.19.11 - '@wdio/logger': 9.18.0 - '@wdio/repl': 9.16.2 - '@wdio/runner': 9.19.1(expect-webdriverio@5.4.2)(webdriverio@9.19.1) - '@wdio/types': 9.19.1 - '@wdio/xvfb': 9.19.1 - exit-hook: 4.0.0 - expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.1) - split2: 4.2.0 - stream-buffers: 3.0.3 - transitivePeerDependencies: - - '@wdio/globals' - - bare-buffer - - bufferutil - - supports-color - - utf-8-validate - - webdriverio - - '@wdio/logger@9.18.0': - dependencies: - chalk: 5.5.0 - loglevel: 1.9.2 - loglevel-plugin-prefix: 0.8.4 - safe-regex2: 5.0.0 - strip-ansi: 7.1.0 - - '@wdio/mocha-framework@9.19.1': - dependencies: - '@types/mocha': 10.0.10 - '@types/node': 20.19.11 - '@wdio/logger': 9.18.0 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - mocha: 10.8.2 - transitivePeerDependencies: - - bare-buffer - - supports-color - - '@wdio/protocols@9.16.2': {} - - '@wdio/repl@9.16.2': - dependencies: - '@types/node': 20.19.11 - - '@wdio/reporter@9.19.1': - dependencies: - '@types/node': 20.19.11 - '@wdio/logger': 9.18.0 - '@wdio/types': 9.19.1 - diff: 8.0.2 - object-inspect: 1.13.4 - - '@wdio/runner@9.19.1(expect-webdriverio@5.4.2)(webdriverio@9.19.1)': - dependencies: - '@types/node': 20.19.11 - '@wdio/config': 9.19.1 - '@wdio/dot-reporter': 9.19.1 - '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.1) - '@wdio/logger': 9.18.0 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - deepmerge-ts: 7.1.5 - expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.1) - webdriver: 9.19.1 - webdriverio: 9.19.1 - transitivePeerDependencies: - - bare-buffer - - bufferutil - - supports-color - - utf-8-validate - - '@wdio/spec-reporter@9.19.1': - dependencies: - '@wdio/reporter': 9.19.1 - '@wdio/types': 9.19.1 - chalk: 5.5.0 - easy-table: 1.2.0 - pretty-ms: 9.2.0 - - '@wdio/types@9.19.1': - dependencies: - '@types/node': 20.19.11 - - '@wdio/utils@9.19.1': - dependencies: - '@puppeteer/browsers': 2.10.6 - '@wdio/logger': 9.18.0 - '@wdio/types': 9.19.1 - decamelize: 6.0.0 - deepmerge-ts: 7.1.5 - edgedriver: 6.1.2 - geckodriver: 5.0.0 - get-port: 7.1.0 - import-meta-resolve: 4.1.0 - locate-app: 2.5.0 - mitt: 3.0.1 - safaridriver: 1.0.0 - split2: 4.2.0 - wait-port: 1.1.0 - transitivePeerDependencies: - - bare-buffer - - supports-color - - '@wdio/xvfb@9.19.1': - dependencies: - '@wdio/logger': 9.18.0 - is-ci: 4.1.0 - '@webassemblyjs/ast@1.14.1': dependencies: '@webassemblyjs/helper-numbers': 1.13.2 @@ -5176,12 +3708,6 @@ snapshots: '@xtuc/long@4.2.2': {} - '@zip.js/zip.js@2.7.72': {} - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -5192,8 +3718,6 @@ snapshots: acorn@8.15.0: {} - agent-base@7.1.4: {} - ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -5206,27 +3730,19 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} any-promise@1.3.0: {} @@ -5235,34 +3751,10 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - archiver-utils@5.0.2: - dependencies: - glob: 10.4.5 - graceful-fs: 4.2.11 - is-stream: 2.0.1 - lazystream: 1.0.1 - lodash: 4.17.21 - normalize-path: 3.0.0 - readable-stream: 4.7.0 - - archiver@7.0.1: - dependencies: - archiver-utils: 5.0.2 - async: 3.2.6 - buffer-crc32: 1.0.0 - readable-stream: 4.7.0 - readdir-glob: 1.1.3 - tar-stream: 3.1.7 - zip-stream: 6.0.1 - arg@4.1.3: {} arg@5.0.2: {} - argparse@2.0.1: {} - - aria-query@5.3.2: {} - array-union@2.1.0: {} asn1.js@4.10.1: @@ -5279,18 +3771,10 @@ snapshots: object.assign: 4.1.7 util: 0.12.5 - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - - async-exit-hook@2.0.1: {} - - async@3.2.6: {} - autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.25.2 - caniuse-lite: 1.0.30001735 + browserslist: 4.26.3 + caniuse-lite: 1.0.30001751 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -5301,38 +3785,11 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - b4a@1.6.7: {} - balanced-match@1.0.2: {} - bare-events@2.6.1: - optional: true - - bare-fs@4.2.0: - dependencies: - bare-events: 2.6.1 - bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.6.1) - optional: true - - bare-os@3.6.1: - optional: true - - bare-path@3.0.0: - dependencies: - bare-os: 3.6.1 - optional: true - - bare-stream@2.7.0(bare-events@2.6.1): - dependencies: - streamx: 2.22.1 - optionalDependencies: - bare-events: 2.6.1 - optional: true - base64-js@1.5.1: {} - basic-ftp@5.0.5: {} + baseline-browser-mapping@2.8.19: {} binary-extensions@2.3.0: {} @@ -5340,13 +3797,6 @@ snapshots: bn.js@5.2.2: {} - boolbase@1.0.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -5359,14 +3809,12 @@ snapshots: browser-resolve@2.0.0: dependencies: - resolve: 1.22.10 - - browser-stdout@1.3.1: {} + resolve: 1.22.11 browserify-aes@1.2.0: dependencies: buffer-xor: 1.0.3 - cipher-base: 1.0.6 + cipher-base: 1.0.7 create-hash: 1.2.0 evp_bytestokey: 1.0.3 inherits: 2.0.4 @@ -5380,7 +3828,7 @@ snapshots: browserify-des@1.0.2: dependencies: - cipher-base: 1.0.6 + cipher-base: 1.0.7 des.js: 1.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 @@ -5391,16 +3839,15 @@ snapshots: randombytes: 2.1.0 safe-buffer: 5.2.1 - browserify-sign@4.2.3: + browserify-sign@4.2.5: dependencies: bn.js: 5.2.2 browserify-rsa: 4.1.1 create-hash: 1.2.0 create-hmac: 1.1.7 elliptic: 6.6.1 - hash-base: 3.0.5 inherits: 2.0.4 - parse-asn1: 5.1.7 + parse-asn1: 5.1.9 readable-stream: 2.3.8 safe-buffer: 5.2.1 @@ -5408,16 +3855,13 @@ snapshots: dependencies: pako: 1.0.11 - browserslist@4.25.2: + browserslist@4.26.3: dependencies: - caniuse-lite: 1.0.30001735 - electron-to-chromium: 1.5.202 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.2) - - buffer-crc32@0.2.13: {} - - buffer-crc32@1.0.0: {} + baseline-browser-mapping: 2.8.19 + caniuse-lite: 1.0.30001751 + electron-to-chromium: 1.5.237 + node-releases: 2.0.26 + update-browserslist-db: 1.1.3(browserslist@4.26.3) buffer-from@1.1.2: {} @@ -5428,11 +3872,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - builtin-status-codes@3.0.0: {} call-bind-apply-helpers@1.0.2: @@ -5454,42 +3893,15 @@ snapshots: camelcase-css@2.0.1: {} - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001735: {} + caniuse-lite@1.0.30001751: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.5.0: {} - chardet@2.1.0: {} - cheerio-select@2.1.0: - dependencies: - boolbase: 1.0.0 - css-select: 5.2.2 - css-what: 6.2.2 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - - cheerio@1.1.2: - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.2.2 - encoding-sniffer: 0.2.1 - htmlparser2: 10.0.0 - parse5: 7.3.0 - parse5-htmlparser2-tree-adapter: 7.1.0 - parse5-parser-stream: 7.1.2 - undici: 7.13.0 - whatwg-mimetype: 4.0.0 - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -5506,34 +3918,22 @@ snapshots: dependencies: readdirp: 4.1.2 - chownr@3.0.0: {} - chrome-trace-event@1.0.4: {} - ci-info@4.3.0: {} - - cipher-base@1.0.6: + cipher-base@1.0.7: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 + to-buffer: 1.2.2 cli-width@4.1.0: {} - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - clone@1.0.4: - optional: true - clsx@2.1.1: {} color-convert@2.0.1: @@ -5544,86 +3944,42 @@ snapshots: commander@11.1.0: {} - commander@14.0.0: {} - commander@2.20.3: {} commander@4.1.1: {} commander@9.5.0: {} - compress-commons@6.0.2: - dependencies: - crc-32: 1.2.2 - crc32-stream: 6.0.0 - is-stream: 2.0.1 - normalize-path: 3.0.0 - readable-stream: 4.7.0 - - concat-map@0.0.1: {} - console-browserify@1.2.0: {} constants-browserify@1.0.0: {} core-util-is@1.0.3: {} - crc-32@1.2.2: {} - - crc32-stream@6.0.0: - dependencies: - crc-32: 1.2.2 - readable-stream: 4.7.0 - create-ecdh@4.0.4: dependencies: bn.js: 4.12.2 elliptic: 6.6.1 - create-hash@1.1.3: - dependencies: - cipher-base: 1.0.6 - inherits: 2.0.4 - ripemd160: 2.0.1 - sha.js: 2.4.12 - create-hash@1.2.0: dependencies: - cipher-base: 1.0.6 + cipher-base: 1.0.7 inherits: 2.0.4 md5.js: 1.3.5 - ripemd160: 2.0.2 + ripemd160: 2.0.3 sha.js: 2.4.12 create-hmac@1.1.7: dependencies: - cipher-base: 1.0.6 + cipher-base: 1.0.7 create-hash: 1.2.0 inherits: 2.0.4 - ripemd160: 2.0.2 + ripemd160: 2.0.3 safe-buffer: 5.2.1 sha.js: 2.4.12 create-require@1.1.1: {} - create-wdio@9.18.2(@types/node@24.3.0): - dependencies: - chalk: 5.5.0 - commander: 14.0.0 - cross-spawn: 7.0.6 - ejs: 3.1.10 - execa: 9.6.0 - import-meta-resolve: 4.1.0 - inquirer: 12.9.2(@types/node@24.3.0) - normalize-package-data: 7.0.1 - read-pkg-up: 10.1.0 - recursive-readdir: 2.2.3 - semver: 7.7.2 - type-fest: 4.41.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5633,61 +3989,24 @@ snapshots: crypto-browserify@3.12.1: dependencies: browserify-cipher: 1.0.1 - browserify-sign: 4.2.3 + browserify-sign: 4.2.5 create-ecdh: 4.0.4 create-hash: 1.2.0 create-hmac: 1.1.7 diffie-hellman: 5.0.3 hash-base: 3.0.5 inherits: 2.0.4 - pbkdf2: 3.1.3 + pbkdf2: 3.1.5 public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 - css-select@5.2.2: - dependencies: - boolbase: 1.0.0 - css-what: 6.2.2 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-shorthand-properties@1.1.2: {} - - css-value@0.0.1: {} - - css-what@6.2.2: {} - cssesc@3.0.0: {} csstype@3.1.3: {} - data-uri-to-buffer@4.0.1: {} - - data-uri-to-buffer@6.0.2: {} - - debug@4.4.1(supports-color@8.1.1): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 - - decamelize@4.0.0: {} - - decamelize@6.0.0: {} - - deep-eql@5.0.2: {} - - deepmerge-ts@7.1.5: {} - deepmerge@4.3.1: {} - defaults@1.0.4: - dependencies: - clone: 1.0.4 - optional: true - define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -5700,12 +4019,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - des.js@1.1.0: dependencies: inherits: 2.0.4 @@ -5714,16 +4027,12 @@ snapshots: detect-libc@1.0.3: optional: true - detect-libc@2.0.4: {} + detect-libc@2.1.2: {} didyoumean@1.2.2: {} diff@4.0.2: {} - diff@5.2.0: {} - - diff@8.0.2: {} - diffie-hellman@5.0.3: dependencies: bn.js: 4.12.2 @@ -5736,27 +4045,9 @@ snapshots: dlv@1.1.3: {} - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - domain-browser@4.22.0: {} - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - - dotenv@17.2.1: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: dependencies: @@ -5766,43 +4057,14 @@ snapshots: eastasianwidth@0.2.0: {} - easy-table@1.2.0: - dependencies: - ansi-regex: 5.0.1 - optionalDependencies: - wcwidth: 1.0.1 - - eciesjs@0.4.15: + eciesjs@0.4.16: dependencies: '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0) '@noble/ciphers': 1.3.0 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 - edge-paths@3.0.5: - dependencies: - '@types/which': 2.0.2 - which: 2.0.2 - - edgedriver@6.1.2: - dependencies: - '@wdio/logger': 9.18.0 - '@zip.js/zip.js': 2.7.72 - decamelize: 6.0.0 - edge-paths: 3.0.5 - fast-xml-parser: 5.2.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - node-fetch: 3.3.2 - which: 5.0.0 - transitivePeerDependencies: - - supports-color - - ejs@3.1.10: - dependencies: - jake: 10.9.4 - - electron-to-chromium@1.5.202: {} + electron-to-chromium@1.5.237: {} elliptic@6.6.1: dependencies: @@ -5818,27 +4080,10 @@ snapshots: emoji-regex@9.2.2: {} - encoding-sniffer@0.2.1: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding: 3.1.1 - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.2 - - entities@4.5.0: {} - - entities@6.0.1: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 + tapable: 2.3.0 es-define-property@1.0.1: {} @@ -5850,49 +4095,39 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild@0.25.9: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -5900,8 +4135,6 @@ snapshots: esm@3.2.25: {} - esprima@4.0.1: {} - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -5912,10 +4145,6 @@ snapshots: estree-walker@2.0.2: {} - esutils@2.0.3: {} - - event-target-shim@5.0.1: {} - events@3.3.0: {} evp_bytestokey@1.0.3: @@ -5935,58 +4164,8 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - execa@9.6.0: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.2.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 - - exit-hook@4.0.0: {} - - expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.1): - dependencies: - '@vitest/snapshot': 3.2.4 - '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.1) - '@wdio/logger': 9.18.0 - deep-eql: 5.0.2 - expect: 30.0.5 - jest-matcher-utils: 30.0.5 - webdriverio: 9.19.1 - - expect@30.0.5: - dependencies: - '@jest/expect-utils': 30.0.5 - '@jest/get-type': 30.0.1 - jest-matcher-utils: 30.0.5 - jest-message-util: 30.0.5 - jest-mock: 30.0.5 - jest-util: 30.0.5 - - extract-zip@2.0.1: - dependencies: - debug: 4.4.1(supports-color@8.1.1) - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - - fast-deep-equal@2.0.1: {} - fast-deep-equal@3.1.3: {} - fast-fifo@1.3.2: {} - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5995,41 +4174,20 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-uri@3.0.6: {} - - fast-xml-parser@5.2.5: - dependencies: - strnum: 2.1.1 + fast-uri@3.1.0: {} fastq@1.19.1: dependencies: reusify: 1.1.0 - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - fflate@0.8.2: {} - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 - file-saver@2.0.5: {} - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6039,13 +4197,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@6.3.0: - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - - flat@5.0.2: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -6055,32 +4206,14 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - fraction.js@4.3.7: {} - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true function-bind@1.1.2: {} - geckodriver@5.0.0: - dependencies: - '@wdio/logger': 9.18.0 - '@zip.js/zip.js': 2.7.72 - decamelize: 6.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - node-fetch: 3.3.2 - tar-fs: 3.1.0 - which: 5.0.0 - transitivePeerDependencies: - - bare-buffer - - supports-color + generator-function@2.0.1: {} get-caller-file@2.0.5: {} @@ -6097,36 +4230,17 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-port@7.1.0: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - get-stream@6.0.1: {} - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - - get-tsconfig@4.10.1: + get-tsconfig@4.12.0: dependencies: resolve-pkg-maps: 1.0.0 - get-uri@6.0.5: - dependencies: - basic-ftp: 5.0.5 - data-uri-to-buffer: 6.0.2 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6155,14 +4269,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.0 - glob@8.1.0: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -6176,8 +4282,6 @@ snapshots: graceful-fs@4.2.11: {} - grapheme-splitter@1.0.4: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -6190,14 +4294,17 @@ snapshots: dependencies: has-symbols: 1.1.0 - hash-base@2.0.2: + hash-base@3.0.5: dependencies: inherits: 2.0.4 + safe-buffer: 5.2.1 - hash-base@3.0.5: + hash-base@3.1.2: dependencies: inherits: 2.0.4 + readable-stream: 2.3.8 safe-buffer: 5.2.1 + to-buffer: 1.2.2 hash.js@1.1.7: dependencies: @@ -6208,52 +4315,17 @@ snapshots: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - hosted-git-info@7.0.2: - dependencies: - lru-cache: 10.4.3 - - hosted-git-info@8.1.0: - dependencies: - lru-cache: 10.4.3 - - htmlfy@0.8.1: {} - - htmlparser2@10.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 6.0.1 - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - https-browserify@1.0.0: {} - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - human-signals@2.1.0: {} - human-signals@8.0.1: {} - - iconv-lite@0.6.3: + iconv-lite@0.7.0: dependencies: safer-buffer: 2.1.2 @@ -6263,50 +4335,21 @@ snapshots: ignore@5.3.2: {} - immediate@3.0.6: {} - - immutable@5.1.3: {} - - import-meta-resolve@4.1.0: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 + immutable@5.1.4: {} inherits@2.0.4: {} - inquirer@12.9.2(@types/node@24.3.0): - dependencies: - '@inquirer/core': 10.1.15(@types/node@24.3.0) - '@inquirer/prompts': 7.8.2(@types/node@24.3.0) - '@inquirer/type': 3.0.8(@types/node@24.3.0) - ansi-escapes: 4.3.2 - mute-stream: 2.0.0 - run-async: 4.0.6 - rxjs: 7.8.2 - optionalDependencies: - '@types/node': 24.3.0 - - ip-address@10.0.1: {} - is-arguments@1.2.0: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-arrayish@0.2.1: {} - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 is-callable@1.2.7: {} - is-ci@4.1.0: - dependencies: - ci-info: 4.3.0 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -6315,9 +4358,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 + generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -6333,10 +4377,6 @@ snapshots: is-number@7.0.0: {} - is-plain-obj@2.1.0: {} - - is-plain-obj@4.1.0: {} - is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -6346,16 +4386,10 @@ snapshots: is-stream@2.0.1: {} - is-stream@4.0.1: {} - is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 - is-unicode-supported@0.1.0: {} - - is-unicode-supported@2.1.0: {} - isarray@1.0.0: {} isarray@2.0.5: {} @@ -6376,189 +4410,86 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 - jake@10.9.4: - dependencies: - async: 3.2.6 - filelist: 1.0.4 - picocolors: 1.1.1 - - jest-diff@30.0.5: - dependencies: - '@jest/diff-sequences': 30.0.1 - '@jest/get-type': 30.0.1 - chalk: 4.1.2 - pretty-format: 30.0.5 - - jest-matcher-utils@30.0.5: - dependencies: - '@jest/get-type': 30.0.1 - chalk: 4.1.2 - jest-diff: 30.0.5 - pretty-format: 30.0.5 - - jest-message-util@30.0.5: - dependencies: - '@babel/code-frame': 7.27.1 - '@jest/types': 30.0.5 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 30.0.5 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@30.0.5: - dependencies: - '@jest/types': 30.0.5 - '@types/node': 24.3.0 - jest-util: 30.0.5 - - jest-regex-util@30.0.1: {} - - jest-util@30.0.5: - dependencies: - '@jest/types': 30.0.5 - '@types/node': 24.3.0 - chalk: 4.1.2 - ci-info: 4.3.0 - graceful-fs: 4.2.11 - picomatch: 4.0.3 - jest-worker@27.5.1: dependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@1.21.7: {} - jiti@2.5.1: {} - - js-tokens@4.0.0: {} - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 + jiti@2.6.1: {} json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@3.0.2: {} - json-schema-traverse@1.0.0: {} - jszip@3.10.1: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - - lazystream@1.0.1: - dependencies: - readable-stream: 2.3.8 - - lie@3.3.0: - dependencies: - immediate: 3.0.6 + lightningcss-android-arm64@1.30.2: + optional: true - lightningcss-darwin-arm64@1.30.1: + lightningcss-darwin-arm64@1.30.2: optional: true - lightningcss-darwin-x64@1.30.1: + lightningcss-darwin-x64@1.30.2: optional: true - lightningcss-freebsd-x64@1.30.1: + lightningcss-freebsd-x64@1.30.2: optional: true - lightningcss-linux-arm-gnueabihf@1.30.1: + lightningcss-linux-arm-gnueabihf@1.30.2: optional: true - lightningcss-linux-arm64-gnu@1.30.1: + lightningcss-linux-arm64-gnu@1.30.2: optional: true - lightningcss-linux-arm64-musl@1.30.1: + lightningcss-linux-arm64-musl@1.30.2: optional: true - lightningcss-linux-x64-gnu@1.30.1: + lightningcss-linux-x64-gnu@1.30.2: optional: true - lightningcss-linux-x64-musl@1.30.1: + lightningcss-linux-x64-musl@1.30.2: optional: true - lightningcss-win32-arm64-msvc@1.30.1: + lightningcss-win32-arm64-msvc@1.30.2: optional: true - lightningcss-win32-x64-msvc@1.30.1: + lightningcss-win32-x64-msvc@1.30.2: optional: true - lightningcss@1.30.1: + lightningcss@1.30.2: dependencies: - detect-libc: 2.0.4 + detect-libc: 2.1.2 optionalDependencies: - lightningcss-darwin-arm64: 1.30.1 - lightningcss-darwin-x64: 1.30.1 - lightningcss-freebsd-x64: 1.30.1 - lightningcss-linux-arm-gnueabihf: 1.30.1 - lightningcss-linux-arm64-gnu: 1.30.1 - lightningcss-linux-arm64-musl: 1.30.1 - lightningcss-linux-x64-gnu: 1.30.1 - lightningcss-linux-x64-musl: 1.30.1 - lightningcss-win32-arm64-msvc: 1.30.1 - lightningcss-win32-x64-msvc: 1.30.1 + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} - lines-and-columns@2.0.4: {} - - loader-runner@4.3.0: {} + loader-runner@4.3.1: {} loader-utils@3.3.1: {} - locate-app@2.5.0: - dependencies: - '@promptbook/utils': 0.69.5 - type-fest: 4.26.0 - userhome: 1.0.1 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: - dependencies: - p-locate: 6.0.0 - - lodash.clonedeep@4.5.0: {} - - lodash.flattendeep@4.4.0: {} - - lodash.pickby@4.6.0: {} - - lodash.union@4.6.0: {} - - lodash.zip@4.2.0: {} - - lodash@4.17.21: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - loglevel-plugin-prefix@0.8.4: {} - - loglevel@1.9.2: {} - lru-cache@10.4.3: {} - lru-cache@11.1.0: {} + lru-cache@11.2.2: {} - lru-cache@7.18.3: {} - - magic-string@0.30.17: + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6602,55 +4533,14 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.0 - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 minipass@7.1.2: {} - minizlib@3.0.2: - dependencies: - minipass: 7.1.2 - - mitt@3.0.1: {} - - mkdirp@3.0.1: {} - - mocha@10.8.2: - dependencies: - ansi-colors: 4.1.3 - browser-stdout: 1.3.1 - chokidar: 3.6.0 - debug: 4.4.1(supports-color@8.1.1) - diff: 5.2.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 8.1.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.1.6 - ms: 2.1.3 - serialize-javascript: 6.0.2 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.5.1 - yargs: 16.2.0 - yargs-parser: 20.2.9 - yargs-unparser: 2.0.0 - mrmime@1.0.1: {} - ms@2.1.3: {} - mute-stream@2.0.0: {} mylas@2.1.13: {} @@ -6665,20 +4555,10 @@ snapshots: neo-async@2.6.2: {} - netmask@2.0.2: {} - node-addon-api@7.1.1: optional: true - node-domexception@1.0.0: {} - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - - node-releases@2.0.19: {} + node-releases@2.0.26: {} node-stdlib-browser@1.3.1: dependencies: @@ -6710,18 +4590,6 @@ snapshots: util: 0.12.5 vm-browserify: 1.1.2 - normalize-package-data@6.0.2: - dependencies: - hosted-git-info: 7.0.2 - semver: 7.7.2 - validate-npm-package-license: 3.0.4 - - normalize-package-data@7.0.1: - dependencies: - hosted-git-info: 8.1.0 - semver: 7.7.2 - validate-npm-package-license: 3.0.4 - normalize-path@3.0.0: {} normalize-range@0.1.2: {} @@ -6730,15 +4598,6 @@ snapshots: dependencies: path-key: 3.1.1 - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -6763,10 +4622,6 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 - once@1.4.0: - dependencies: - wrappy: 1.0.2 - onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -6777,82 +4632,28 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.2.1 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-locate@6.0.0: - dependencies: - p-limit: 4.0.0 - - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - package-json-from-dist@1.0.1: {} pako@1.0.11: {} - parse-asn1@5.1.7: + parse-asn1@5.1.9: dependencies: asn1.js: 4.10.1 browserify-aes: 1.2.0 evp_bytestokey: 1.0.3 - hash-base: 3.0.5 - pbkdf2: 3.1.3 + pbkdf2: 3.1.5 safe-buffer: 5.2.1 - parse-json@7.1.1: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.2 - json-parse-even-better-errors: 3.0.2 - lines-and-columns: 2.0.4 - type-fest: 3.13.1 - - parse-ms@4.0.0: {} - - parse5-htmlparser2-tree-adapter@7.1.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.3.0 - - parse5-parser-stream@7.1.2: - dependencies: - parse5: 7.3.0 - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - path-browserify@1.0.1: {} path-exists@4.0.0: {} - path-exists@5.0.0: {} - path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -6862,25 +4663,19 @@ snapshots: path-scurry@2.0.0: dependencies: - lru-cache: 11.1.0 + lru-cache: 11.2.2 minipass: 7.1.2 path-type@4.0.0: {} - pathe@1.1.2: {} - - pathe@2.0.3: {} - - pbkdf2@3.1.3: + pbkdf2@3.1.5: dependencies: - create-hash: 1.1.3 + create-hash: 1.2.0 create-hmac: 1.1.7 - ripemd160: 2.0.1 + ripemd160: 2.0.3 safe-buffer: 5.2.1 sha.js: 2.4.12 - to-buffer: 1.2.1 - - pend@1.2.0: {} + to-buffer: 1.2.2 picocolors@1.1.1: {} @@ -6907,20 +4702,20 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.11 - postcss-js@4.0.1(postcss@8.5.6): + postcss-js@4.1.0(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2)): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6): dependencies: lilconfig: 3.1.3 - yaml: 2.8.1 optionalDependencies: + jiti: 1.21.7 postcss: 8.5.6 - ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2) + tsx: 4.20.6 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -6940,59 +4735,25 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - pretty-format@30.0.5: - dependencies: - '@jest/schemas': 30.0.5 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - pretty-ms@9.2.0: - dependencies: - parse-ms: 4.0.0 - process-nextick-args@2.0.1: {} process@0.11.10: {} - progress@2.0.3: {} - - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} - public-encrypt@4.0.3: dependencies: bn.js: 4.12.2 browserify-rsa: 4.1.1 create-hash: 1.2.0 - parse-asn1: 5.1.7 + parse-asn1: 5.1.9 randombytes: 2.1.0 safe-buffer: 5.2.1 - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode@1.4.1: {} qs@6.14.0: dependencies: side-channel: 1.1.0 - query-selector-shadow-dom@1.0.1: {} - querystring-es3@0.2.1: {} queue-lit@1.5.2: {} @@ -7008,42 +4769,27 @@ snapshots: randombytes: 2.1.0 safe-buffer: 5.2.1 - react-dom@19.1.1(react@19.1.1): + react-dom@19.2.0(react@19.2.0): dependencies: - react: 19.1.1 - scheduler: 0.26.0 - - react-is@18.3.1: {} + react: 19.2.0 + scheduler: 0.27.0 - react-resizable-panels@2.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-resizable-panels@2.1.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - react-spinners@0.17.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-spinners@0.17.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - react@19.1.1: {} + react@19.2.0: {} read-cache@1.0.0: dependencies: pify: 2.3.0 - read-pkg-up@10.1.0: - dependencies: - find-up: 6.3.0 - read-pkg: 8.1.0 - type-fest: 4.41.0 - - read-pkg@8.1.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 6.0.2 - parse-json: 7.1.1 - type-fest: 4.41.0 - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -7060,103 +4806,68 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readdir-glob@1.1.3: - dependencies: - minimatch: 5.1.6 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 readdirp@4.1.2: {} - recursive-readdir@2.2.3: - dependencies: - minimatch: 3.1.2 - require-directory@2.1.1: {} require-from-string@2.0.2: {} resolve-pkg-maps@1.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resq@1.11.0: - dependencies: - fast-deep-equal: 2.0.1 - - ret@0.5.0: {} - reusify@1.1.0: {} - rgb2hex@0.2.5: {} - rimraf@6.0.1: dependencies: glob: 11.0.3 package-json-from-dist: 1.0.1 - ripemd160@2.0.1: - dependencies: - hash-base: 2.0.2 - inherits: 2.0.4 - - ripemd160@2.0.2: + ripemd160@2.0.3: dependencies: - hash-base: 3.0.5 + hash-base: 3.1.2 inherits: 2.0.4 - rollup@4.46.2: + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.46.2 - '@rollup/rollup-android-arm64': 4.46.2 - '@rollup/rollup-darwin-arm64': 4.46.2 - '@rollup/rollup-darwin-x64': 4.46.2 - '@rollup/rollup-freebsd-arm64': 4.46.2 - '@rollup/rollup-freebsd-x64': 4.46.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 - '@rollup/rollup-linux-arm-musleabihf': 4.46.2 - '@rollup/rollup-linux-arm64-gnu': 4.46.2 - '@rollup/rollup-linux-arm64-musl': 4.46.2 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 - '@rollup/rollup-linux-ppc64-gnu': 4.46.2 - '@rollup/rollup-linux-riscv64-gnu': 4.46.2 - '@rollup/rollup-linux-riscv64-musl': 4.46.2 - '@rollup/rollup-linux-s390x-gnu': 4.46.2 - '@rollup/rollup-linux-x64-gnu': 4.46.2 - '@rollup/rollup-linux-x64-musl': 4.46.2 - '@rollup/rollup-win32-arm64-msvc': 4.46.2 - '@rollup/rollup-win32-ia32-msvc': 4.46.2 - '@rollup/rollup-win32-x64-msvc': 4.46.2 + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 - run-async@4.0.6: {} - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - safaridriver@1.0.0: {} - safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} @@ -7167,34 +4878,26 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safe-regex2@5.0.0: - dependencies: - ret: 0.5.0 - safer-buffer@2.1.2: {} - sass@1.90.0: + sass@1.93.2: dependencies: chokidar: 4.0.3 - immutable: 5.1.3 + immutable: 5.1.4 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.1 - scheduler@0.26.0: {} + scheduler@0.27.0: {} - schema-utils@4.3.2: + schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 ajv: 8.17.1 ajv-formats: 2.1.1(ajv@8.17.1) ajv-keywords: 5.1.0(ajv@8.17.1) - semver@7.7.2: {} - - serialize-error@12.0.0: - dependencies: - type-fest: 4.41.0 + semver@7.7.3: {} serialize-javascript@6.0.2: dependencies: @@ -7215,7 +4918,7 @@ snapshots: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - to-buffer: 1.2.1 + to-buffer: 1.2.2 shebang-command@2.0.0: dependencies: @@ -7257,21 +4960,6 @@ snapshots: slash@3.0.0: {} - smart-buffer@4.2.0: {} - - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.0.1 - smart-buffer: 4.2.0 - source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -7283,35 +4971,11 @@ snapshots: source-map@0.7.6: {} - spacetrim@0.11.59: {} - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.22 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 - - spdx-license-ids@3.0.22: {} - - split2@4.2.0: {} - - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - stream-browserify@3.0.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 - stream-buffers@3.0.3: {} - stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 @@ -7319,13 +4983,6 @@ snapshots: readable-stream: 3.6.2 xtend: 4.0.2 - streamx@2.22.1: - dependencies: - fast-fifo: 1.3.2 - text-decoder: 1.2.3 - optionalDependencies: - bare-events: 2.6.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -7336,7 +4993,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 string_decoder@1.1.1: dependencies: @@ -7350,18 +5007,12 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-final-newline@2.0.0: {} - strip-final-newline@4.0.0: {} - - strip-json-comments@3.1.1: {} - - strnum@2.1.1: {} - sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -7384,7 +5035,7 @@ snapshots: tailwind-merge@3.3.1: {} - tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2)): + tailwindcss@3.4.18(tsx@4.20.6): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -7402,64 +5053,36 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.0.1(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2)) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 - resolve: 1.22.10 + resolve: 1.22.11 sucrase: 3.35.0 transitivePeerDependencies: - - ts-node - - tailwindcss@4.1.12: {} - - tapable@2.2.2: {} - - tar-fs@3.1.0: - dependencies: - pump: 3.0.3 - tar-stream: 3.1.7 - optionalDependencies: - bare-fs: 4.2.0 - bare-path: 3.0.0 - transitivePeerDependencies: - - bare-buffer + - tsx + - yaml - tar-stream@3.1.7: - dependencies: - b4a: 1.6.7 - fast-fifo: 1.3.2 - streamx: 2.22.1 + tailwindcss@4.1.15: {} - tar@7.4.3: - dependencies: - '@isaacs/fs-minipass': 4.0.1 - chownr: 3.0.0 - minipass: 7.1.2 - minizlib: 3.0.2 - mkdirp: 3.0.1 - yallist: 5.0.0 + tapable@2.3.0: {} - terser-webpack-plugin@5.3.14(webpack@5.101.2): + terser-webpack-plugin@5.3.14(webpack@5.102.1): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.43.1 - webpack: 5.101.2 + terser: 5.44.0 + webpack: 5.102.1 - terser@5.43.1: + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 - text-decoder@1.2.3: - dependencies: - b4a: 1.6.7 - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -7477,11 +5100,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinyrainbow@1.2.0: {} - - tinyrainbow@2.0.0: {} - - to-buffer@1.2.1: + to-buffer@1.2.2: dependencies: isarray: 2.0.5 safe-buffer: 5.2.1 @@ -7493,31 +5112,31 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-loader@9.5.2(typescript@5.9.2)(webpack@5.101.2): + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.102.1): dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 micromatch: 4.0.8 - semver: 7.7.2 + semver: 7.7.3 source-map: 0.7.6 - typescript: 5.9.2 - webpack: 5.101.2 + typescript: 5.9.3 + webpack: 5.102.1 - ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.0)(typescript@5.9.2): + ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.9.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.3.0 + '@types/node': 24.9.1 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.2 + typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -7527,55 +5146,47 @@ snapshots: dependencies: chokidar: 3.6.0 commander: 9.5.0 - get-tsconfig: 4.10.1 + get-tsconfig: 4.12.0 globby: 11.1.0 mylas: 2.1.13 normalize-path: 3.0.0 plimit-lit: 1.6.1 - tslib@2.8.1: {} - - tsx@4.20.4: + tsx@4.20.6: dependencies: - esbuild: 0.25.9 - get-tsconfig: 4.10.1 + esbuild: 0.25.11 + get-tsconfig: 4.12.0 optionalDependencies: fsevents: 2.3.3 tty-browserify@0.0.1: {} - turbo-darwin-64@2.5.6: + turbo-darwin-64@2.5.8: optional: true - turbo-darwin-arm64@2.5.6: + turbo-darwin-arm64@2.5.8: optional: true - turbo-linux-64@2.5.6: + turbo-linux-64@2.5.8: optional: true - turbo-linux-arm64@2.5.6: + turbo-linux-arm64@2.5.8: optional: true - turbo-windows-64@2.5.6: + turbo-windows-64@2.5.8: optional: true - turbo-windows-arm64@2.5.6: + turbo-windows-arm64@2.5.8: optional: true - turbo@2.5.6: + turbo@2.5.8: optionalDependencies: - turbo-darwin-64: 2.5.6 - turbo-darwin-arm64: 2.5.6 - turbo-linux-64: 2.5.6 - turbo-linux-arm64: 2.5.6 - turbo-windows-64: 2.5.6 - turbo-windows-arm64: 2.5.6 - - type-fest@0.21.3: {} - - type-fest@3.13.1: {} - - type-fest@4.26.0: {} + turbo-darwin-64: 2.5.8 + turbo-darwin-arm64: 2.5.8 + turbo-linux-64: 2.5.8 + turbo-linux-arm64: 2.5.8 + turbo-windows-64: 2.5.8 + turbo-windows-arm64: 2.5.8 type-fest@4.41.0: {} @@ -7585,21 +5196,13 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.15 - typescript@5.9.2: {} - - undici-types@6.21.0: {} + typescript@5.9.3: {} - undici-types@7.10.0: {} + undici-types@7.16.0: {} - undici@6.21.3: {} - - undici@7.13.0: {} - - unicorn-magic@0.3.0: {} - - update-browserslist-db@1.1.3(browserslist@4.25.2): + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: - browserslist: 4.25.2 + browserslist: 4.26.3 escalade: 3.2.0 picocolors: 1.1.1 @@ -7608,132 +5211,55 @@ snapshots: punycode: 1.4.1 qs: 6.14.0 - urlpattern-polyfill@10.1.0: {} - - userhome@1.0.1: {} - util-deprecate@1.0.2: {} util@0.12.5: dependencies: inherits: 2.0.4 is-arguments: 1.2.0 - is-generator-function: 1.1.0 + is-generator-function: 1.1.2 is-typed-array: 1.1.15 which-typed-array: 1.1.19 v8-compile-cache-lib@3.0.1: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vite-plugin-node-polyfills@0.22.0(rollup@4.46.2)(vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)): + vite-plugin-node-polyfills@0.22.0(rollup@4.52.5)(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.46.2) + '@rollup/plugin-inject': 5.0.5(rollup@4.52.5) node-stdlib-browser: 1.3.1 - vite: 7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6) transitivePeerDependencies: - rollup - vite@7.1.9(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1): + vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.2 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.9.1 fsevents: 2.3.3 - jiti: 2.5.1 - lightningcss: 1.30.1 - sass: 1.90.0 - terser: 5.43.1 - tsx: 4.20.4 - yaml: 2.8.1 + jiti: 2.6.1 + lightningcss: 1.30.2 + sass: 1.93.2 + terser: 5.44.0 + tsx: 4.20.6 vm-browserify@1.1.2: {} - wait-port@1.1.0: - dependencies: - chalk: 4.1.2 - commander: 9.5.0 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - optional: true - - web-streams-polyfill@3.3.3: {} - - webdriver@9.19.1: - dependencies: - '@types/node': 20.19.11 - '@types/ws': 8.18.1 - '@wdio/config': 9.19.1 - '@wdio/logger': 9.18.0 - '@wdio/protocols': 9.16.2 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - deepmerge-ts: 7.1.5 - https-proxy-agent: 7.0.6 - undici: 6.21.3 - ws: 8.18.3 - transitivePeerDependencies: - - bare-buffer - - bufferutil - - supports-color - - utf-8-validate - - webdriverio@9.19.1: - dependencies: - '@types/node': 20.19.11 - '@types/sinonjs__fake-timers': 8.1.5 - '@wdio/config': 9.19.1 - '@wdio/logger': 9.18.0 - '@wdio/protocols': 9.16.2 - '@wdio/repl': 9.16.2 - '@wdio/types': 9.19.1 - '@wdio/utils': 9.19.1 - archiver: 7.0.1 - aria-query: 5.3.2 - cheerio: 1.1.2 - css-shorthand-properties: 1.1.2 - css-value: 0.0.1 - grapheme-splitter: 1.0.4 - htmlfy: 0.8.1 - is-plain-obj: 4.1.0 - jszip: 3.10.1 - lodash.clonedeep: 4.5.0 - lodash.zip: 4.2.0 - query-selector-shadow-dom: 1.0.1 - resq: 1.11.0 - rgb2hex: 0.2.5 - serialize-error: 12.0.0 - urlpattern-polyfill: 10.1.0 - webdriver: 9.19.1 - transitivePeerDependencies: - - bare-buffer - - bufferutil - - supports-color - - utf-8-validate - webextension-polyfill@0.12.0: {} webpack-sources@3.3.3: {} - webpack@5.101.2: + webpack@5.102.1: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -7743,7 +5269,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.25.2 + browserslist: 4.26.3 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -7752,12 +5278,12 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 + loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 4.3.2 - tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(webpack@5.101.2) + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -7765,12 +5291,6 @@ snapshots: - esbuild - uglify-js - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} - which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -7789,12 +5309,6 @@ snapshots: dependencies: isexe: 3.1.1 - which@5.0.0: - dependencies: - isexe: 3.1.1 - - workerpool@6.5.1: {} - wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -7809,11 +5323,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 - - wrappy@1.0.2: {} + strip-ansi: 7.1.2 ws@8.18.3: {} @@ -7821,31 +5333,8 @@ snapshots: y18n@5.0.8: {} - yallist@5.0.0: {} - - yaml@2.8.1: {} - - yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} - yargs-unparser@2.0.0: - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -7856,23 +5345,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - yn@3.1.1: {} yocto-queue@0.1.0: {} - yocto-queue@1.2.1: {} - - yoctocolors-cjs@2.1.2: {} - - yoctocolors@2.1.1: {} - - zip-stream@6.0.1: - dependencies: - archiver-utils: 5.0.2 - compress-commons: 6.0.2 - readable-stream: 4.7.0 + yoctocolors-cjs@2.1.3: {} diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 860178b5..b15317b7 100755 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1406", + "version": "0.27.1419", "description": "The Pyodide JavaScript package", "keywords": [ "python", From bd8c456ab3afc9bacc3ec92dda89aa86200fd590 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Wed, 22 Oct 2025 06:26:59 +0500 Subject: [PATCH 10/15] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE,=20=D0=B7=D0=B0=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=B9=D1=80=D0=BE=D1=81=D0=B5=D1=82=D0=B5=D0=B9,=20=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=90=D0=9F=D0=98-=D0=BA=D0=BB=D1=8E=D1=87=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=20=D0=B1=D0=B5=D1=80=D1=91=D1=82=D1=81=D1=8F?= =?UTF-8?q?=20=D0=B8=D0=B7=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5?= =?UTF-8?q?=D0=BA=20=D0=BF=D0=BB=D0=B0=D1=82=D1=84=D0=BE=D1=80=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- .../plugins/ozon-analyzer/mcp_server.py | 77 ++++++++++++++++++- chrome-extension/public/pyodide/package.json | 2 +- .../side-panel/assets/index-B8ToW130.js.map | 1 - .../{index-B8ToW130.js => index-DdVzImwV.js} | 4 +- .../side-panel/assets/index-DdVzImwV.js.map | 1 + chrome-extension/public/side-panel/index.html | 2 +- chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/options/src/components/LLMSelector.tsx | 5 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- 32 files changed, 109 insertions(+), 35 deletions(-) delete mode 100644 chrome-extension/public/side-panel/assets/index-B8ToW130.js.map rename chrome-extension/public/side-panel/assets/{index-B8ToW130.js => index-DdVzImwV.js} (71%) create mode 100644 chrome-extension/public/side-panel/assets/index-DdVzImwV.js.map diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 6c32d54a..426d5485 100755 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1394", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1394", + "version": "1.0.1395", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1395", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 7e10fed9..ea1bb95e 100755 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py index 8d618102..84f59ee4 100755 --- a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py +++ b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py @@ -2577,7 +2577,6 @@ def get_api_key_for_analysis(plugin_settings: Optional[Dict[str, Any]] = None, Returns: ID ключа для использования в js.llm_call - console_log(f"OZON_ANALYZER_LLM_DEBUG: ===== НАЧАЛО get_api_key_for_analysis =======") """ console_log(f"[API_KEY] Получение API-ключа для {analysis_type}.{content_language}, LLM: {selected_llm}") @@ -2624,6 +2623,70 @@ def get_selected_llm_for_analysis(plugin_settings: Optional[Dict[str, Any]] = No return 'default' +def get_api_key_for_llm(plugin_settings: Optional[Dict[str, Any]] = None, + selected_llm: str = 'default', + analysis_type: str = 'basic_analysis', + content_language: str = 'ru') -> str: + """ + Получить API-ключ для выбранной LLM. + + Args: + plugin_settings: Настройки плагина + selected_llm: Выбранная LLM + analysis_type: Тип анализа ('basic_analysis' или 'deep_analysis') + content_language: Язык контента ('ru' или 'en') + + Returns: + API-ключ для выбранной LLM + """ + console_log(f"[API_KEY] Получение API-ключа для {selected_llm} ({analysis_type}.{content_language})") + + if selected_llm == 'default': + # Используем специфичный ключ для комбинации + key_id = f'ozon-analyzer-{analysis_type}-{content_language}' + api_key = APIKeyManager.get_decrypted_key(key_id) + console_log(f"[API_KEY] Специфичный ключ для {key_id}: {'найден' if api_key else 'не найден'}") + return api_key or '' + else: + # Используем ключ платформы для данной LLM + platform_key = get_platform_api_key(selected_llm) + console_log(f"[API_KEY] Платформенный ключ для {selected_llm}: {'найден' if platform_key else 'не найден'}") + return platform_key or '' + + +def get_platform_api_key(llm_id: str) -> str: + """ + Получить API-ключ платформы для указанной LLM. + + Args: + llm_id: ID LLM модели + + Returns: + API-ключ платформы или пустая строка + """ + try: + # Получаем глобальные AI ключи из pyodide + global_ai_keys = get_pyodide_var('globalAIKeys', []) + + if not isinstance(global_ai_keys, list): + console_log(f"[PLATFORM_API_KEY] globalAIKeys не является списком: {type(global_ai_keys)}") + return '' + + # Ищем ключ для указанной LLM + for key_info in global_ai_keys: + if isinstance(key_info, dict) and key_info.get('id') == llm_id: + api_key = key_info.get('apiKey', '') + console_log(f"[PLATFORM_API_KEY] Найден ключ для {llm_id}: {'есть' if api_key else 'пустой'}") + return api_key + + console_log(f"[PLATFORM_API_KEY] Ключ для {llm_id} не найден в глобальных ключах") + return '' + + except Exception as e: + console_log(f"[PLATFORM_API_KEY] Ошибка получения платформенного ключа: {str(e)}") + return '' + + def get_safe_content_language(plugin_settings: Dict[str, Any]) -> str: """ Безопасная функция определения языка контента для сообщений чата. @@ -2733,12 +2796,22 @@ def get_default_model_id_from_manifest(analysis_type: str = 'basic_analysis', co analysis_prompts = prompts.get(analysis_type, {}) lang_prompts = analysis_prompts.get(content_language, {}) default_llm = lang_prompts.get('LLM', {}).get('default', None) + + # Проверяем, есть ли curl_file в default LLM + if isinstance(default_llm, dict) and 'curl_file' in default_llm: + # Для Default LLM с curl_file используем fallback модели + if analysis_type == 'basic_analysis': + return 'gemini-flash-lite' + elif analysis_type == 'deep_analysis': + return 'gemini-pro' + if isinstance(default_llm, dict) and 'model' in default_llm: return default_llm['model'] if isinstance(default_llm, str): return default_llm except Exception as e: - pass + console_log(f"[DEFAULT_MODEL] Ошибка получения модели из manifest: {str(e)}") + # Fallback if analysis_type == 'basic_analysis': return 'gemini-flash-lite' diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index b15317b7..9e838ad2 100755 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1419", + "version": "0.27.1420", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map b/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map deleted file mode 100644 index 6ac1e45a..00000000 --- a/chrome-extension/public/side-panel/assets/index-B8ToW130.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index-B8ToW130.js","sources":["../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react-jsx-runtime.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js","../../../../pages/side-panel/src/components/DraftStatus.tsx","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/index.js","../../../../pages/options/src/hooks/useTranslations.ts","../../../../pages/options/src/components/ToggleButton.tsx","../../../../pages/options/src/components/ErrorDisplay.tsx","../../../../pages/options/src/components/LocalErrorBoundary.tsx","../../../../pages/options/src/utils/encryption.ts","../../../../pages/options/src/hooks/usePluginSettings.ts","../../../../pages/options/src/components/LLMSelector.tsx","../../../../pages/options/src/hooks/useAIKeys.ts","../../../../pages/options/src/components/PluginDetails.tsx","../../../../pages/side-panel/src/components/PluginDetails.tsx","../../../../packages/shared/lib/utils/helpers.ts","../../../../pages/side-panel/src/hooks/useLazyChatSync.ts","../../../../node_modules/.pnpm/file-saver@2.0.5/node_modules/file-saver/dist/FileSaver.min.js","../../../../packages/storage/dist/lib/base/enums.js","../../../../packages/storage/dist/lib/base/base.js","../../../../packages/storage/dist/lib/impl/example-theme-storage.js","../../../../packages/storage/dist/lib/impl/example-chat-alignment-storage.js","../../../../pages/side-panel/src/components/PluginControlPanel.tsx","../../../../pages/side-panel/src/components/ToastNotifications.tsx","../../../../pages/options/src/components/ThemeSwitcher.tsx","../../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../../node_modules/.pnpm/tailwind-merge@3.3.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs","../../../../packages/ui/dist/lib/utils.js","../../../../node_modules/.pnpm/deepmerge@4.3.1/node_modules/deepmerge/dist/cjs.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.production.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom-client.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/client.js","../../../../pages/side-panel/src/components/ErrorDisplay.tsx","../../../../pages/side-panel/src/components/LocalErrorBoundary.tsx","../../../../pages/side-panel/src/components/PluginCard.tsx","../../../../pages/side-panel/src/SidePanel.tsx","../../../../pages/side-panel/src/index.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","import './DraftStatus.css';\n\ninterface DraftStatusProps {\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n messageLength: number;\n minLength: number;\n maxLength: number;\n}\n\nexport const DraftStatus: React.FC = ({\n isDraftSaved,\n isDraftLoading,\n draftError,\n messageLength,\n minLength,\n maxLength,\n}) => {\n const getStatusIcon = () => {\n if (isDraftLoading) {\n // CSS-анимированный круг для лоадера\n return
;\n }\n\n if (draftError) {\n return (\n \n \n \n \n \n );\n }\n\n if (isDraftSaved) {\n return (\n \n \n \n );\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return (\n \n \n \n \n );\n }\n\n return null;\n };\n\n const getStatusText = () => {\n if (isDraftLoading) {\n return 'Загрузка черновика...';\n }\n\n if (draftError) {\n return draftError;\n }\n\n if (isDraftSaved) {\n return 'Черновик сохранен';\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return `Еще ${minLength - messageLength} символов для сохранения`;\n }\n\n if (messageLength >= minLength && messageLength <= maxLength) {\n return 'Черновик будет сохранен автоматически';\n }\n\n if (messageLength > maxLength) {\n return 'Превышен лимит символов';\n }\n\n return '';\n };\n\n const getStatusClass = () => {\n if (isDraftLoading) return 'draft-status loading';\n if (draftError) return 'draft-status error';\n if (isDraftSaved) return 'draft-status saved';\n if (messageLength > 0 && messageLength < minLength) return 'draft-status pending';\n if (messageLength >= minLength && messageLength <= maxLength) return 'draft-status ready';\n if (messageLength > maxLength) return 'draft-status error';\n return 'draft-status';\n };\n\n if (messageLength === 0 && !isDraftLoading && !draftError) {\n return null; // Не показываем статус, если нет текста\n }\n\n return (\n
\n {getStatusIcon()}\n {getStatusText()}\n
\n );\n};\n","/**\n * @license React\n * react.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\"),\n REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\"),\n MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar ReactNoopUpdateQueue = {\n isMounted: function () {\n return !1;\n },\n enqueueForceUpdate: function () {},\n enqueueReplaceState: function () {},\n enqueueSetState: function () {}\n },\n assign = Object.assign,\n emptyObject = {};\nfunction Component(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nComponent.prototype.isReactComponent = {};\nComponent.prototype.setState = function (partialState, callback) {\n if (\n \"object\" !== typeof partialState &&\n \"function\" !== typeof partialState &&\n null != partialState\n )\n throw Error(\n \"takes an object of state variables to update or a function which returns an object of state variables.\"\n );\n this.updater.enqueueSetState(this, partialState, callback, \"setState\");\n};\nComponent.prototype.forceUpdate = function (callback) {\n this.updater.enqueueForceUpdate(this, callback, \"forceUpdate\");\n};\nfunction ComponentDummy() {}\nComponentDummy.prototype = Component.prototype;\nfunction PureComponent(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nvar pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());\npureComponentPrototype.constructor = PureComponent;\nassign(pureComponentPrototype, Component.prototype);\npureComponentPrototype.isPureReactComponent = !0;\nvar isArrayImpl = Array.isArray;\nfunction noop() {}\nvar ReactSharedInternals = { H: null, A: null, T: null, S: null },\n hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction ReactElement(type, key, props) {\n var refProp = props.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== refProp ? refProp : null,\n props: props\n };\n}\nfunction cloneAndReplaceKey(oldElement, newKey) {\n return ReactElement(oldElement.type, newKey, oldElement.props);\n}\nfunction isValidElement(object) {\n return (\n \"object\" === typeof object &&\n null !== object &&\n object.$$typeof === REACT_ELEMENT_TYPE\n );\n}\nfunction escape(key) {\n var escaperLookup = { \"=\": \"=0\", \":\": \"=2\" };\n return (\n \"$\" +\n key.replace(/[=:]/g, function (match) {\n return escaperLookup[match];\n })\n );\n}\nvar userProvidedKeyEscapeRegex = /\\/+/g;\nfunction getElementKey(element, index) {\n return \"object\" === typeof element && null !== element && null != element.key\n ? escape(\"\" + element.key)\n : index.toString(36);\n}\nfunction resolveThenable(thenable) {\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n default:\n switch (\n (\"string\" === typeof thenable.status\n ? thenable.then(noop, noop)\n : ((thenable.status = \"pending\"),\n thenable.then(\n function (fulfilledValue) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"fulfilled\"),\n (thenable.value = fulfilledValue));\n },\n function (error) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"rejected\"), (thenable.reason = error));\n }\n )),\n thenable.status)\n ) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n }\n }\n throw thenable;\n}\nfunction mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {\n var type = typeof children;\n if (\"undefined\" === type || \"boolean\" === type) children = null;\n var invokeCallback = !1;\n if (null === children) invokeCallback = !0;\n else\n switch (type) {\n case \"bigint\":\n case \"string\":\n case \"number\":\n invokeCallback = !0;\n break;\n case \"object\":\n switch (children.$$typeof) {\n case REACT_ELEMENT_TYPE:\n case REACT_PORTAL_TYPE:\n invokeCallback = !0;\n break;\n case REACT_LAZY_TYPE:\n return (\n (invokeCallback = children._init),\n mapIntoArray(\n invokeCallback(children._payload),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n )\n );\n }\n }\n if (invokeCallback)\n return (\n (callback = callback(children)),\n (invokeCallback =\n \"\" === nameSoFar ? \".\" + getElementKey(children, 0) : nameSoFar),\n isArrayImpl(callback)\n ? ((escapedPrefix = \"\"),\n null != invokeCallback &&\n (escapedPrefix =\n invokeCallback.replace(userProvidedKeyEscapeRegex, \"$&/\") + \"/\"),\n mapIntoArray(callback, array, escapedPrefix, \"\", function (c) {\n return c;\n }))\n : null != callback &&\n (isValidElement(callback) &&\n (callback = cloneAndReplaceKey(\n callback,\n escapedPrefix +\n (null == callback.key ||\n (children && children.key === callback.key)\n ? \"\"\n : (\"\" + callback.key).replace(\n userProvidedKeyEscapeRegex,\n \"$&/\"\n ) + \"/\") +\n invokeCallback\n )),\n array.push(callback)),\n 1\n );\n invokeCallback = 0;\n var nextNamePrefix = \"\" === nameSoFar ? \".\" : nameSoFar + \":\";\n if (isArrayImpl(children))\n for (var i = 0; i < children.length; i++)\n (nameSoFar = children[i]),\n (type = nextNamePrefix + getElementKey(nameSoFar, i)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (((i = getIteratorFn(children)), \"function\" === typeof i))\n for (\n children = i.call(children), i = 0;\n !(nameSoFar = children.next()).done;\n\n )\n (nameSoFar = nameSoFar.value),\n (type = nextNamePrefix + getElementKey(nameSoFar, i++)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (\"object\" === type) {\n if (\"function\" === typeof children.then)\n return mapIntoArray(\n resolveThenable(children),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n );\n array = String(children);\n throw Error(\n \"Objects are not valid as a React child (found: \" +\n (\"[object Object]\" === array\n ? \"object with keys {\" + Object.keys(children).join(\", \") + \"}\"\n : array) +\n \"). If you meant to render a collection of children, use an array instead.\"\n );\n }\n return invokeCallback;\n}\nfunction mapChildren(children, func, context) {\n if (null == children) return children;\n var result = [],\n count = 0;\n mapIntoArray(children, result, \"\", \"\", function (child) {\n return func.call(context, child, count++);\n });\n return result;\n}\nfunction lazyInitializer(payload) {\n if (-1 === payload._status) {\n var ctor = payload._result;\n ctor = ctor();\n ctor.then(\n function (moduleObject) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 1), (payload._result = moduleObject);\n },\n function (error) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 2), (payload._result = error);\n }\n );\n -1 === payload._status && ((payload._status = 0), (payload._result = ctor));\n }\n if (1 === payload._status) return payload._result.default;\n throw payload._result;\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n Children = {\n map: mapChildren,\n forEach: function (children, forEachFunc, forEachContext) {\n mapChildren(\n children,\n function () {\n forEachFunc.apply(this, arguments);\n },\n forEachContext\n );\n },\n count: function (children) {\n var n = 0;\n mapChildren(children, function () {\n n++;\n });\n return n;\n },\n toArray: function (children) {\n return (\n mapChildren(children, function (child) {\n return child;\n }) || []\n );\n },\n only: function (children) {\n if (!isValidElement(children))\n throw Error(\n \"React.Children.only expected to receive a single React element child.\"\n );\n return children;\n }\n };\nexports.Activity = REACT_ACTIVITY_TYPE;\nexports.Children = Children;\nexports.Component = Component;\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.Profiler = REACT_PROFILER_TYPE;\nexports.PureComponent = PureComponent;\nexports.StrictMode = REACT_STRICT_MODE_TYPE;\nexports.Suspense = REACT_SUSPENSE_TYPE;\nexports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n ReactSharedInternals;\nexports.__COMPILER_RUNTIME = {\n __proto__: null,\n c: function (size) {\n return ReactSharedInternals.H.useMemoCache(size);\n }\n};\nexports.cache = function (fn) {\n return function () {\n return fn.apply(null, arguments);\n };\n};\nexports.cacheSignal = function () {\n return null;\n};\nexports.cloneElement = function (element, config, children) {\n if (null === element || void 0 === element)\n throw Error(\n \"The argument must be a React element, but you passed \" + element + \".\"\n );\n var props = assign({}, element.props),\n key = element.key;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n !hasOwnProperty.call(config, propName) ||\n \"key\" === propName ||\n \"__self\" === propName ||\n \"__source\" === propName ||\n (\"ref\" === propName && void 0 === config.ref) ||\n (props[propName] = config[propName]);\n var propName = arguments.length - 2;\n if (1 === propName) props.children = children;\n else if (1 < propName) {\n for (var childArray = Array(propName), i = 0; i < propName; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n return ReactElement(element.type, key, props);\n};\nexports.createContext = function (defaultValue) {\n defaultValue = {\n $$typeof: REACT_CONTEXT_TYPE,\n _currentValue: defaultValue,\n _currentValue2: defaultValue,\n _threadCount: 0,\n Provider: null,\n Consumer: null\n };\n defaultValue.Provider = defaultValue;\n defaultValue.Consumer = {\n $$typeof: REACT_CONSUMER_TYPE,\n _context: defaultValue\n };\n return defaultValue;\n};\nexports.createElement = function (type, config, children) {\n var propName,\n props = {},\n key = null;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n hasOwnProperty.call(config, propName) &&\n \"key\" !== propName &&\n \"__self\" !== propName &&\n \"__source\" !== propName &&\n (props[propName] = config[propName]);\n var childrenLength = arguments.length - 2;\n if (1 === childrenLength) props.children = children;\n else if (1 < childrenLength) {\n for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n if (type && type.defaultProps)\n for (propName in ((childrenLength = type.defaultProps), childrenLength))\n void 0 === props[propName] &&\n (props[propName] = childrenLength[propName]);\n return ReactElement(type, key, props);\n};\nexports.createRef = function () {\n return { current: null };\n};\nexports.forwardRef = function (render) {\n return { $$typeof: REACT_FORWARD_REF_TYPE, render: render };\n};\nexports.isValidElement = isValidElement;\nexports.lazy = function (ctor) {\n return {\n $$typeof: REACT_LAZY_TYPE,\n _payload: { _status: -1, _result: ctor },\n _init: lazyInitializer\n };\n};\nexports.memo = function (type, compare) {\n return {\n $$typeof: REACT_MEMO_TYPE,\n type: type,\n compare: void 0 === compare ? null : compare\n };\n};\nexports.startTransition = function (scope) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = scope(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n returnValue.then(noop, reportGlobalError);\n } catch (error) {\n reportGlobalError(error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n};\nexports.unstable_useCacheRefresh = function () {\n return ReactSharedInternals.H.useCacheRefresh();\n};\nexports.use = function (usable) {\n return ReactSharedInternals.H.use(usable);\n};\nexports.useActionState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useActionState(action, initialState, permalink);\n};\nexports.useCallback = function (callback, deps) {\n return ReactSharedInternals.H.useCallback(callback, deps);\n};\nexports.useContext = function (Context) {\n return ReactSharedInternals.H.useContext(Context);\n};\nexports.useDebugValue = function () {};\nexports.useDeferredValue = function (value, initialValue) {\n return ReactSharedInternals.H.useDeferredValue(value, initialValue);\n};\nexports.useEffect = function (create, deps) {\n return ReactSharedInternals.H.useEffect(create, deps);\n};\nexports.useEffectEvent = function (callback) {\n return ReactSharedInternals.H.useEffectEvent(callback);\n};\nexports.useId = function () {\n return ReactSharedInternals.H.useId();\n};\nexports.useImperativeHandle = function (ref, create, deps) {\n return ReactSharedInternals.H.useImperativeHandle(ref, create, deps);\n};\nexports.useInsertionEffect = function (create, deps) {\n return ReactSharedInternals.H.useInsertionEffect(create, deps);\n};\nexports.useLayoutEffect = function (create, deps) {\n return ReactSharedInternals.H.useLayoutEffect(create, deps);\n};\nexports.useMemo = function (create, deps) {\n return ReactSharedInternals.H.useMemo(create, deps);\n};\nexports.useOptimistic = function (passthrough, reducer) {\n return ReactSharedInternals.H.useOptimistic(passthrough, reducer);\n};\nexports.useReducer = function (reducer, initialArg, init) {\n return ReactSharedInternals.H.useReducer(reducer, initialArg, init);\n};\nexports.useRef = function (initialValue) {\n return ReactSharedInternals.H.useRef(initialValue);\n};\nexports.useState = function (initialState) {\n return ReactSharedInternals.H.useState(initialState);\n};\nexports.useSyncExternalStore = function (\n subscribe,\n getSnapshot,\n getServerSnapshot\n) {\n return ReactSharedInternals.H.useSyncExternalStore(\n subscribe,\n getSnapshot,\n getServerSnapshot\n );\n};\nexports.useTransition = function () {\n return ReactSharedInternals.H.useTransition();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react.production.js');\n} else {\n module.exports = require('./cjs/react.development.js');\n}\n","import * as React from 'react';\n\n// Импортируем переводы\nimport enTranslations from '../locales/en.json';\nimport ruTranslations from '../locales/ru.json';\n\nconst translations = {\n en: enTranslations,\n ru: ruTranslations,\n};\n\nexport type Locale = 'en' | 'ru';\n\nexport const useTranslations = (locale: Locale = 'en') => {\n const t = React.useMemo(() => {\n const dict: any = translations[locale] || {};\n\n // Функция для получения значения по пути с точками\n const getNestedValue = (obj: any, path: string): string => {\n return path.split('.').reduce((current, key) => {\n return current && current[key] ? current[key] : undefined;\n }, obj);\n };\n\n return (key: string) => {\n const value = getNestedValue(dict, key);\n return value !== undefined ? value : key;\n };\n }, [locale, translations]);\n\n return { t, locale };\n};\n","import React from 'react';\n\ninterface ToggleButtonProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n label?: React.ReactNode;\n iconOn?: React.ReactNode; // AI-First: иконка для состояния ON\n iconOff?: React.ReactNode; // AI-First: иконка для состояния OFF\n}\n\nconst ToggleButton: React.FC = ({ checked, onChange, disabled, label, iconOn, iconOff }) => (\n \n);\n\nexport default ToggleButton;\n","import React from 'react';\n\nconst ErrorDisplay: React.FC<{ error?: Error; resetError?: () => void }> = ({ error, resetError }) => (\n
\n

Произошла ошибка

\n {error &&
{error.message}
}\n {resetError && }\n
\n);\n\nexport default ErrorDisplay;\n","import React from 'react';\nimport ErrorDisplay from './ErrorDisplay';\n\nconst LocalErrorBoundary: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [error, setError] = React.useState(null);\n const resetError = React.useCallback(() => setError(null), []);\n\n if (error) {\n return ;\n }\n\n try {\n return <>{children};\n } catch (err) {\n setError(err as Error);\n return null;\n }\n};\n\nexport default LocalErrorBoundary;\n","/**\n * Утилиты для шифрования API-ключей\n * Использует Web Crypto API для безопасного хранения в chrome.storage.local\n */\n\nexport class APIKeyEncryption {\n private static readonly ALGORITHM = 'AES-GCM';\n private static readonly KEY_LENGTH = 256;\n private static readonly IV_LENGTH = 12;\n\n /**\n * Получает или создает ключ шифрования из chrome.storage.local\n */\n private static async getEncryptionKey(): Promise {\n try {\n // Проверяем, есть ли уже ключ в хранилище\n const result = await chrome.storage.local.get(['encryptionKey']);\n if (result.encryptionKey) {\n // Восстанавливаем ключ из массива байтов\n const keyData = new Uint8Array(result.encryptionKey);\n return await crypto.subtle.importKey(\n 'raw',\n keyData,\n this.ALGORITHM,\n false,\n ['encrypt', 'decrypt']\n );\n }\n\n // Создаем новый ключ\n const key = await crypto.subtle.generateKey(\n {\n name: this.ALGORITHM,\n length: this.KEY_LENGTH,\n },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Сохраняем ключ в хранилище\n const exportedKey = await crypto.subtle.exportKey('raw', key);\n const keyArray = new Uint8Array(exportedKey);\n await chrome.storage.local.set({\n encryptionKey: Array.from(keyArray)\n });\n\n return key;\n } catch (error) {\n console.error('Failed to get/create encryption key:', error);\n throw new Error('Не удалось инициализировать шифрование');\n }\n }\n\n /**\n * Шифрует текст\n */\n static async encrypt(text: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n const encodedText = new TextEncoder().encode(text);\n\n const encrypted = await crypto.subtle.encrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n encodedText\n );\n\n // Объединяем IV и зашифрованные данные\n const encryptedArray = new Uint8Array(encrypted);\n const resultArray = new Uint8Array(iv.length + encryptedArray.length);\n resultArray.set(iv);\n resultArray.set(encryptedArray, iv.length);\n\n // Кодируем в base64 для хранения в storage\n return btoa(String.fromCharCode(...resultArray));\n } catch (error) {\n console.error('Encryption failed:', error);\n throw new Error('Ошибка шифрования');\n }\n }\n\n /**\n * Расшифровывает текст\n */\n static async decrypt(encryptedText: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n\n // Декодируем из base64\n const encryptedArray = new Uint8Array(\n atob(encryptedText).split('').map(char => char.charCodeAt(0))\n );\n\n // Извлекаем IV и зашифрованные данные\n const iv = encryptedArray.slice(0, this.IV_LENGTH);\n const data = encryptedArray.slice(this.IV_LENGTH);\n\n const decrypted = await crypto.subtle.decrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n data\n );\n\n return new TextDecoder().decode(decrypted);\n } catch (error) {\n console.error('Decryption failed:', error);\n throw new Error('Ошибка расшифрования');\n }\n }\n\n /**\n * Валидация API ключа\n */\n static validateAPIKey(key: string): { isValid: boolean; error?: string } {\n if (!key || typeof key !== 'string') {\n return { isValid: false, error: 'Ключ не может быть пустым' };\n }\n\n if (key.length < 10) {\n return { isValid: false, error: 'Ключ слишком короткий' };\n }\n\n if (key.length > 200) {\n return { isValid: false, error: 'Ключ слишком длинный' };\n }\n\n // Проверяем на наличие потенциально опасных символов\n if (/[<>\\\"'&]/.test(key)) {\n return { isValid: false, error: 'Ключ содержит недопустимые символы' };\n }\n\n return { isValid: true };\n }\n}\n\n/**\n * Утилиты для безопасной работы с API ключами\n */\nexport class APIKeyManager {\n /**\n * Сохраняет API ключ с шифрованием\n */\n static async saveEncryptedKey(keyId: string, apiKey: string): Promise {\n try {\n const validation = APIKeyEncryption.validateAPIKey(apiKey);\n if (!validation.isValid) {\n throw new Error(validation.error);\n }\n\n const encryptedKey = await APIKeyEncryption.encrypt(apiKey);\n const keys = await this.getAllEncryptedKeys();\n\n keys[keyId] = encryptedKey;\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to save encrypted API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает расшифрованный API ключ\n */\n static async getDecryptedKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n const encryptedKey = keys[keyId];\n\n if (!encryptedKey) {\n return null;\n }\n\n return await APIKeyEncryption.decrypt(encryptedKey);\n } catch (error) {\n console.error('Failed to get decrypted API key:', error);\n return null;\n }\n }\n\n /**\n * Удаляет API ключ\n */\n static async removeKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n delete keys[keyId];\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to remove API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает все зашифрованные ключи\n */\n private static async getAllEncryptedKeys(): Promise> {\n try {\n const result = await chrome.storage.local.get(['encryptedApiKeys']);\n return result.encryptedApiKeys || {};\n } catch (error) {\n console.error('Failed to get encrypted keys:', error);\n return {};\n }\n }\n\n /**\n * Получает все ID ключей (без расшифровки)\n */\n static async getAllKeyIds(): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return Object.keys(keys);\n } catch (error) {\n console.error('Failed to get key IDs:', error);\n return [];\n }\n }\n\n /**\n * Проверяет, существует ли ключ\n */\n static async keyExists(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return keyId in keys;\n } catch (error) {\n console.error('Failed to check key existence:', error);\n return false;\n }\n }\n}","import * as React from 'react';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface PluginSettings {\n basic_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n deep_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n api_keys: {\n default: string; // encrypted\n };\n}\n\nexport interface PluginPromptSettings {\n llm: string;\n custom_prompt: string;\n}\n\nconst STORAGE_KEY = 'plugin-ozon-analyzer-settings';\n\nconst DEFAULT_SETTINGS: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.',\n },\n en: {\n llm: '',\n custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.',\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.',\n },\n en: {\n llm: '',\n custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.',\n },\n },\n api_keys: {\n default: '',\n },\n};\n\nexport const usePluginSettings = () => {\n const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);\n const [isLoading, setIsLoading] = React.useState(true);\n\n React.useEffect(() => {\n loadSettings();\n }, []);\n\n const loadSettings = async () => {\n try {\n setIsLoading(true);\n const result = await chrome.storage.local.get([STORAGE_KEY]);\n const storedSettings = result[STORAGE_KEY];\n\n if (storedSettings) {\n // Расшифровываем API ключи\n const decryptedApiKeys = { ...storedSettings.api_keys };\n if (storedSettings.api_keys?.default) {\n decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || '';\n }\n\n setSettings({\n ...DEFAULT_SETTINGS,\n ...storedSettings,\n basic_analysis: {\n ru: {\n ...DEFAULT_SETTINGS.basic_analysis.ru,\n ...storedSettings.basic_analysis?.ru,\n },\n en: {\n ...DEFAULT_SETTINGS.basic_analysis.en,\n ...storedSettings.basic_analysis?.en,\n },\n },\n deep_analysis: {\n ru: {\n ...DEFAULT_SETTINGS.deep_analysis.ru,\n ...storedSettings.deep_analysis?.ru,\n },\n en: {\n ...DEFAULT_SETTINGS.deep_analysis.en,\n ...storedSettings.deep_analysis?.en,\n },\n },\n api_keys: decryptedApiKeys,\n });\n } else {\n setSettings(DEFAULT_SETTINGS);\n }\n } catch (error) {\n console.error('Failed to load plugin settings:', error);\n setSettings(DEFAULT_SETTINGS);\n } finally {\n setIsLoading(false);\n }\n };\n\n const saveSettings = async (newSettings: PluginSettings) => {\n try {\n // Шифруем API ключи перед сохранением\n const settingsToSave = { ...newSettings };\n if (newSettings.api_keys?.default) {\n await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default);\n } else {\n await APIKeyManager.removeKey('ozon-analyzer-default');\n }\n\n // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON\n settingsToSave.api_keys = { default: '' };\n\n await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave });\n setSettings(newSettings);\n } catch (error) {\n console.error('Failed to save plugin settings:', error);\n throw error;\n }\n };\n\n const updateBasicAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n basic_analysis: {\n ...settings.basic_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateDeepAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n deep_analysis: {\n ...settings.deep_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateAPIKey = async (key: string) => {\n const newSettings = {\n ...settings,\n api_keys: {\n default: key,\n },\n };\n await saveSettings(newSettings);\n };\n\n const getBasicAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.basic_analysis[language];\n };\n\n const getDeepAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.deep_analysis[language];\n };\n\n const getAPIKey = (): string => {\n return settings.api_keys?.default || '';\n };\n\n const resetToDefaults = async () => {\n await saveSettings(DEFAULT_SETTINGS);\n };\n\n return {\n settings,\n isLoading,\n updateBasicAnalysisSettings,\n updateDeepAnalysisSettings,\n updateAPIKey,\n getBasicAnalysisSettings,\n getDeepAnalysisSettings,\n getAPIKey,\n resetToDefaults,\n saveSettings,\n loadSettings,\n };\n};","import React, { useState, useEffect, useRef } from 'react';\nimport { usePluginSettings } from '../hooks/usePluginSettings';\nimport { AIKey } from '../hooks/useAIKeys';\nimport { APIKeyManager } from '../utils/encryption';\n\ninterface LLMSelectorProps {\n promptType: 'basic_analysis' | 'deep_analysis';\n language: 'ru' | 'en';\n globalAIKeys: AIKey[];\n defaultLLMCurl: string;\n hasDefaultLLM: boolean;\n onLLMChange: (llm: string, apiKey?: string) => void;\n}\n\nconst LLMSelector: React.FC = ({\n promptType,\n language,\n globalAIKeys,\n defaultLLMCurl,\n hasDefaultLLM,\n onLLMChange,\n}) => {\n const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings();\n const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl);\n const [apiKey, setApiKey] = useState('');\n const saveTimeoutRef = useRef();\n\n // Загружаем API-ключ при монтировании компонента\n useEffect(() => {\n const loadApiKey = async () => {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n const key = await APIKeyManager.getDecryptedKey(keyId) || '';\n setApiKey(key);\n };\n loadApiKey();\n }, [promptType, language]);\n\n // Загружаем LLM при изменении promptType/language\n useEffect(() => {\n const currentSettings = promptType === 'basic_analysis'\n ? settings.basic_analysis[language]\n : settings.deep_analysis[language];\n\n let initialLLM = '';\n\n if (currentSettings) {\n const savedLLM = currentSettings.llm;\n initialLLM = savedLLM || (hasDefaultLLM ? 'default' : '');\n } else {\n initialLLM = hasDefaultLLM ? 'default' : '';\n }\n\n setSelectedLLM(initialLLM);\n }, [promptType, language, settings, hasDefaultLLM]);\n\n // Сохраняем выбор LLM\n const handleLLMChange = async (newLLM: string) => {\n setSelectedLLM(newLLM);\n\n const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings;\n await updateFn(language, {\n llm: newLLM,\n custom_prompt: promptType === 'basic_analysis'\n ? settings.basic_analysis[language].custom_prompt\n : settings.deep_analysis[language].custom_prompt,\n });\n\n onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined);\n };\n\n // Сохраняем API ключ\n const handleApiKeyChange = (newApiKey: string) => {\n setApiKey(newApiKey);\n\n if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);\n\n saveTimeoutRef.current = setTimeout(async () => {\n try {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n await APIKeyManager.saveEncryptedKey(keyId, newApiKey);\n onLLMChange(selectedLLM, newApiKey);\n } catch (error) {\n console.error('Failed to save API key:', error);\n }\n }, 500);\n };\n\n // Получаем список опций для селекта\n const getLLMOptions = () => {\n const options = [\n { value: 'default', label: 'Default LLM' },\n ...globalAIKeys.map(key => ({\n value: key.id,\n label: key.name,\n })),\n ];\n return options;\n };\n\n return (\n
\n \n\n handleLLMChange(e.target.value)}\n placeholder=\"Выберите нейросеть\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px',\n marginBottom: '8px'\n }}\n >\n {getLLMOptions().map(option => (\n \n ))}\n \n\n {selectedLLM === 'default' && (\n
\n \n handleApiKeyChange(e.target.value)}\n placeholder=\"Введите API ключ\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n />\n
\n )}\n
\n );\n};\n\nexport default LLMSelector;","import * as React from 'react';\nimport { useTranslations } from './useTranslations';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface AIKey {\n id: string;\n name: string;\n key: string;\n status: 'configured' | 'not_configured' | 'testing';\n isFixed?: boolean;\n isFree?: boolean;\n}\n\nexport const useAIKeys = () => {\n const { t } = useTranslations('ru');\n const [aiKeys, setAiKeys] = React.useState([\n {\n id: 'gemini-flash-lite',\n name: 'Google Gemini Flash Lite - Базовый анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n {\n id: 'gemini-pro',\n name: 'Gemini 2.5 Pro - Глубокий анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n ]);\n const [customKeys, setCustomKeys] = React.useState([]);\n\n // Рефы для отложенного сохранения ключей\n const saveTimeoutsRef = React.useRef>({});\n\n // Load AI keys on mount and cleanup timeouts on unmount\n React.useEffect(() => {\n loadAIKeys();\n\n // Cleanup function\n return () => {\n // Очищаем все активные таймауты\n Object.values(saveTimeoutsRef.current).forEach(timeout => {\n clearTimeout(timeout);\n });\n saveTimeoutsRef.current = {};\n };\n }, []);\n\n const loadAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to load AI keys...');\n\n // Загружаем зашифрованные ключи\n const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro'];\n const fixedKeysPromises = fixedKeyIds.map(async (keyId) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(keyId);\n console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty');\n return { keyId, decryptedKey };\n });\n\n const fixedKeysResults = await Promise.all(fixedKeysPromises);\n console.log('[useAIKeys] Fixed keys results:', fixedKeysResults);\n\n setAiKeys(prev =>\n prev.map(key => {\n const result = fixedKeysResults.find(r => r.keyId === key.id);\n console.log(`[useAIKeys] Setting key ${key.id}:`, result?.decryptedKey ? 'configured' : 'not_configured');\n return {\n ...key,\n key: result?.decryptedKey || '',\n status: (result?.decryptedKey ? 'configured' : 'not_configured') as AIKey['status'],\n };\n }),\n );\n\n // Загружаем пользовательские ключи (метаданные)\n const result = await chrome.storage.local.get(['customKeys']);\n console.log('[useAIKeys] Custom keys metadata:', result.customKeys);\n if (result.customKeys) {\n // Для пользовательских ключей также используем шифрование\n const customKeysWithDecryption = await Promise.all(\n (result.customKeys as AIKey[]).map(async (key: AIKey) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(key.id);\n console.log(`[useAIKeys] Custom key ${key.id}:`, decryptedKey ? 'present' : 'empty');\n return {\n ...key,\n key: decryptedKey || '',\n status: (decryptedKey ? 'configured' : 'not_configured') as AIKey['status']\n };\n })\n );\n console.log('[useAIKeys] Setting custom keys:', customKeysWithDecryption);\n setCustomKeys(customKeysWithDecryption);\n } else {\n console.log('[useAIKeys] No custom keys found');\n setCustomKeys([]);\n }\n\n console.log('[useAIKeys] AI keys loaded successfully');\n } catch (error) {\n console.error('Failed to load AI keys:', error);\n // В случае ошибки шифрования показываем пустые ключи\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n key: '',\n status: 'not_configured' as AIKey['status'],\n })),\n );\n setCustomKeys([]);\n }\n };\n\n const saveAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to save AI keys...');\n console.log('[useAIKeys] Current aiKeys:', aiKeys);\n console.log('[useAIKeys] Current customKeys:', customKeys);\n\n // Сохраняем фиксированные ключи с шифрованием\n const saveFixedKeysPromises = aiKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving fixed key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing fixed key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveFixedKeysPromises);\n\n // Сохраняем пользовательские ключи с шифрованием\n const saveCustomKeysPromises = customKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving custom key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing custom key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveCustomKeysPromises);\n\n // Сохраняем метаданные пользовательских ключей (без самих ключей)\n const customKeysMetadata = customKeys.map(key => ({\n id: key.id,\n name: key.name,\n isFixed: false,\n isFree: false,\n // key и status не сохраняем в метаданных для безопасности\n }));\n\n console.log('[useAIKeys] Saving custom keys metadata:', customKeysMetadata);\n await chrome.storage.local.set({\n customKeys: customKeysMetadata,\n });\n\n // Обновляем статусы в состоянии\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status'],\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status']\n }))\n );\n\n console.log('[useAIKeys] AI keys saved successfully');\n alert(t('options.settings.aiKeys.messages.saved'));\n } catch (error) {\n console.error('Failed to save AI keys:', error);\n alert(t('options.settings.aiKeys.messages.saveError'));\n }\n };\n\n const testAIKeys = async () => {\n // Set testing status\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n // Simulate API testing with timeout\n setTimeout(() => {\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n alert(t('options.settings.aiKeys.messages.testComplete'));\n }, 2000);\n };\n\n const addCustomKey = () => {\n const newKey: AIKey = {\n id: `custom-${Date.now()}`,\n name: `Пользовательский ключ ${customKeys.length + 1}`,\n key: '',\n status: 'not_configured' as const,\n };\n setCustomKeys(prev => [...prev, newKey]);\n };\n\n const removeCustomKey = async (id: string) => {\n try {\n // Удаляем зашифрованный ключ\n await APIKeyManager.removeKey(id);\n\n // Удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n } catch (error) {\n console.error('Failed to remove custom key:', error);\n // Даже если удаление зашифрованного ключа не удалось, удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n }\n };\n\n const updateKey = (id: string, value: string, isCustom = false) => {\n console.log(`[useAIKeys] Updating key ${id} with value:`, value ? 'present' : 'empty');\n\n // Обновляем состояние немедленно для лучшего UX\n if (isCustom) {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n } else {\n setAiKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n }\n\n // Отменяем предыдущий таймаут для этого ключа\n if (saveTimeoutsRef.current[id]) {\n clearTimeout(saveTimeoutsRef.current[id]);\n }\n\n // Устанавливаем новый таймаут для отложенного сохранения\n saveTimeoutsRef.current[id] = setTimeout(async () => {\n try {\n console.log(`[useAIKeys] Auto-saving key ${id} after delay`);\n if (value) {\n await APIKeyManager.saveEncryptedKey(id, value);\n } else {\n await APIKeyManager.removeKey(id);\n }\n\n // Для пользовательских ключей также обновляем метаданные\n if (isCustom) {\n const result = await chrome.storage.local.get(['customKeys']);\n const customKeysMetadata = result.customKeys || [];\n const updatedMetadata = customKeysMetadata.map((key: any) =>\n key.id === id ? { ...key, name: key.name } : key\n );\n\n // Если ключ не существует в метаданных, добавляем его\n const existingKeyIndex = updatedMetadata.findIndex((key: any) => key.id === id);\n if (existingKeyIndex === -1 && value) {\n updatedMetadata.push({\n id,\n name: `Пользовательский ключ ${customKeysMetadata.length + 1}`,\n isFixed: false,\n isFree: false,\n });\n }\n\n await chrome.storage.local.set({\n customKeys: updatedMetadata.filter((key: any) => {\n // Убираем из метаданных ключи, которые были удалены\n return value || key.id !== id;\n }),\n });\n }\n\n console.log(`[useAIKeys] Key ${id} auto-saved successfully`);\n } catch (error) {\n console.error(`[useAIKeys] Failed to auto-save key ${id}:`, error);\n } finally {\n // Убираем таймаут из рефа\n delete saveTimeoutsRef.current[id];\n }\n }, 1000); // 1 секунда задержки перед сохранением\n };\n\n const updateCustomKeyName = (id: string, name: string) => {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n name,\n status: key.key ? 'configured' : 'not_configured'\n } : key)));\n };\n\n // Функция для получения текста статуса с поддержкой локализации\n const getStatusText = (status: string) => {\n switch (status) {\n case 'configured':\n return t('options.settings.aiKeys.status.configured');\n case 'not_configured':\n return t('options.settings.aiKeys.status.notConfigured');\n case 'testing':\n return t('options.settings.aiKeys.status.testing');\n default:\n return t('options.settings.aiKeys.status.notConfigured');\n }\n };\n\n const getStatusClass = (status: string) => {\n switch (status) {\n case 'configured':\n return 'status-configured';\n case 'not_configured':\n return 'status-not-configured';\n case 'testing':\n return 'status-testing';\n default:\n return '';\n }\n };\n\n // Функция для получения API ключа для использования в MCP-серверах\n const getAPIKeyForMCP = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.getDecryptedKey(keyId);\n } catch (error) {\n console.error('Failed to get API key for MCP:', error);\n return null;\n }\n };\n\n // Функция для проверки, настроен ли определенный ключ\n const isKeyConfigured = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.keyExists(keyId);\n } catch (error) {\n console.error('Failed to check if key is configured:', error);\n return false;\n }\n };\n\n return {\n aiKeys,\n customKeys,\n saveAIKeys,\n testAIKeys,\n addCustomKey,\n removeCustomKey,\n updateKey,\n updateCustomKeyName,\n getStatusText,\n getStatusClass,\n getAPIKeyForMCP,\n isKeyConfigured,\n };\n};\n","import { useTranslations } from '../hooks/useTranslations';\nimport type { Plugin } from '../hooks/usePlugins';\nimport type { PluginSettings } from '@extension/storage';\nimport ToggleButton from './ToggleButton';\nimport LocalErrorBoundary from './LocalErrorBoundary';\nimport LLMSelector from './LLMSelector';\nimport { useAIKeys, AIKey } from '../hooks/useAIKeys';\nimport { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings';\nimport { useState, useEffect } from 'react';\nimport type { ReactNode } from 'react';\n\n// Типы для структуры промптов\ninterface PromptData {\n [key: string]: any;\n}\n\ninterface LanguagePrompts {\n ru: PromptData;\n en: PromptData;\n}\n\ninterface PromptsStructure {\n basic_analysis: LanguagePrompts;\n deep_analysis: LanguagePrompts;\n}\n\nconst cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' ');\n\ninterface PluginDetailsProps {\n selectedPlugin: Plugin | null;\n locale?: 'en' | 'ru';\n onUpdateSetting?: (pluginId: string, setting: keyof PluginSettings, value: boolean) => Promise;\n}\n\ninterface CustomSetting {\n type: 'boolean' | 'select' | 'text' | 'number' | 'prompts';\n default: boolean | string | number | PromptsStructure;\n label: string | { ru: string; en: string };\n description?: string | { ru: string; en: string };\n values?: string[];\n labels?: Record;\n min?: number;\n max?: number;\n step?: number;\n}\n\n// Компонент для редактирования промптов\ninterface PromptsEditorProps {\n value: PromptsStructure;\n manifest: any; // manifest.json структура\n disabled: boolean;\n onSave: (value: PromptsStructure) => void;\n locale: 'en' | 'ru';\n t: (key: string) => string; // функция перевода\n globalAIKeys: AIKey[];\n pluginSettings: PluginSettingsType;\n}\n\nconst PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => {\n const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis');\n const [language, setLanguage] = useState<'ru' | 'en'>('ru');\n const [customPrompt, setCustomPrompt] = useState('');\n\n // Получить дефолтную LLM для конкретного промпта и языка\n const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return 'default';\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n const llmConfig = langPrompts.LLM?.default;\n\n if (!llmConfig) return 'default';\n\n // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro\n if (type === 'basic_analysis') {\n return 'gemini-flash-lite';\n } else if (type === 'deep_analysis') {\n return 'gemini-pro';\n }\n\n return 'default';\n } catch {\n return 'default';\n }\n };\n\n // Проверить наличие дефолтной LLM для конкретного промпта и языка\n const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return false;\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n return !!langPrompts.LLM?.default;\n } catch {\n return false;\n }\n };\n\n // Получаем оригинальный промпт из manifest\n const getOriginalPrompt = (): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return '';\n\n const typePrompts = promptsConfig[promptType] || {};\n const langPrompts = typePrompts[language] || {};\n const defaultPrompt = langPrompts.default || '';\n\n // Если defaultPrompt - это объект, преобразуем его\n if (typeof defaultPrompt === 'object') {\n return JSON.stringify(defaultPrompt, null, 2);\n }\n\n return defaultPrompt;\n } catch {\n return '';\n }\n };\n\n // Получаем кастомный промпт\n const getCustomPrompt = (): string => {\n try {\n const prompts = value || {};\n const typePrompts = (prompts as any)[promptType] || {};\n const langPrompts = (typePrompts as any)[language];\n\n // If stored as plain text, show as-is. Only stringify objects.\n if (typeof langPrompts === 'string') return langPrompts;\n if (langPrompts == null) return '';\n return JSON.stringify(langPrompts, null, 2);\n } catch {\n return '';\n }\n };\n\n // Загружаем кастомный промпт при изменении типа или языка\n useEffect(() => {\n setCustomPrompt(getCustomPrompt());\n }, [promptType, language, value]);\n\n const handleCopyToCustom = () => {\n setCustomPrompt(getOriginalPrompt());\n };\n\n const handleSave = () => {\n try {\n const newValue: any = { ...value };\n\n // Ensure container objects exist\n if (!newValue[promptType]) newValue[promptType] = { ru: '', en: '' };\n\n // Store verbatim text (plain string), no JSON requirement\n newValue[promptType][language] = customPrompt;\n\n onSave(newValue);\n } catch (error) {\n console.error('Failed to save custom prompt:', error);\n // Можно добавить уведомление об ошибке\n }\n };\n\n return (\n
\n
\n \n\n {/* Переключатели */}\n
\n
\n \n setPromptType(e.target.value as 'basic_analysis' | 'deep_analysis')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n\n
\n \n setLanguage(e.target.value as 'ru' | 'en')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n
\n\n {/* LLM Selector для текущего промпта и языка */}\n {\n console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey);\n // Сохраняем выбранную LLM в pluginSettings для передачи в mcp_server.py\n const updatedPluginSettings = { ...pluginSettings } as any;\n if (!updatedPluginSettings.selected_llms) {\n updatedPluginSettings.selected_llms = {};\n }\n if (!updatedPluginSettings.selected_llms[promptType]) {\n updatedPluginSettings.selected_llms[promptType] = {};\n }\n // Сохраняем только выбранную LLM (без api_key, так как он уже сохранен через APIKeyManager)\n updatedPluginSettings.selected_llms[promptType][language] = llm;\n // Обновляем pluginSettings через глобальный объект\n if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n }\n }}\n />\n\n {/* Textarea */}\n
\n
\n \n \n
\n\n
\n \n {t('options.plugins.prompts.copyToCustom')}\n \n
\n\n
\n \n setCustomPrompt(e.target.value)}\n disabled={disabled}\n style={{\n width: '100%',\n height: '300px',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '12px',\n fontFamily: 'monospace',\n resize: 'vertical'\n }}\n />\n
\n
\n\n {/* Кнопка сохранения */}\n
\n \n {t('options.plugins.prompts.save')}\n \n
\n
\n
\n );\n};\n\nconst PluginDetails = (props: PluginDetailsProps) => {\n const { selectedPlugin, locale = 'en', onUpdateSetting } = props;\n const { t } = useTranslations(locale);\n const { aiKeys } = useAIKeys();\n const { settings: pluginSettings } = usePluginSettings();\n const [isUpdating, setIsUpdating] = useState(null);\n const [customSettings, setCustomSettings] = useState | null>(null);\n\n // Хелперы для работы с локализацией\n const getLocalizedText = (text: string | { ru: string; en: string } | undefined): string => {\n if (!text) return '';\n if (typeof text === 'string') return text;\n return text[locale] || text.ru || text.en || '';\n };\n\n // Загружаем пользовательские настройки при выборе плагина\n useEffect(() => {\n if (selectedPlugin?.manifest?.options) {\n loadCustomSettings();\n }\n }, [selectedPlugin?.id]);\n\n // Передаем выбранные LLM в mcp_server.py через pyodide.globals\n useEffect(() => {\n if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n // Создаем структуру selected_llms из pluginSettings\n const selected_llms = {\n basic_analysis: {\n ru: pluginSettings.basic_analysis?.ru?.llm || 'default',\n en: pluginSettings.basic_analysis?.en?.llm || 'default',\n },\n deep_analysis: {\n ru: pluginSettings.deep_analysis?.ru?.llm || 'default',\n en: pluginSettings.deep_analysis?.en?.llm || 'default',\n }\n };\n \n // Обновляем pluginSettings в pyodide.globals\n const updatedPluginSettings = {\n ...(window as any).pyodide.globals.pluginSettings || {},\n selected_llms: selected_llms\n };\n \n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings);\n }\n }, [pluginSettings]);\n\n if (!selectedPlugin || typeof selectedPlugin !== 'object') {\n return (\n
\n

{t('options_plugins_details_title')}

\n

{t('options.plugins.details.selectPlugin')}

\n
\n );\n }\n\n const settings = selectedPlugin.settings || { enabled: true, autorun: false };\n const hostPermissions = selectedPlugin.manifest?.host_permissions || [];\n\n // Функции для работы с chrome.storage.local\n const loadCustomSettings = async () => {\n if (!selectedPlugin || !selectedPlugin.manifest?.options || customSettings !== null) return;\n\n try {\n // Проверяем доступность chrome.storage\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const optionKeys = Object.keys(selectedPlugin.manifest.options);\n const keys = optionKeys.map(key => `${selectedPlugin.id}_${key}`);\n\n const result = await chrome.storage.local.get(keys);\n const loadedSettings: Record = {};\n\n // Преобразуем ключи обратно и применяем значения\n Object.entries(result).forEach(([key, value]) => {\n const settingName = key.replace(`${selectedPlugin.id}_`, '');\n if (value !== undefined) {\n loadedSettings[settingName] = value;\n }\n });\n\n setCustomSettings(loadedSettings);\n }\n } catch (error) {\n console.warn('Failed to load custom settings from chrome.storage.local:', error);\n // Fallback: используем дефолтные значения из manifest\n }\n };\n\n const saveCustomSetting = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n try {\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const key = `${selectedPlugin.id}_${setting}`;\n await chrome.storage.local.set({ [key]: value });\n\n // Обновляем локальное состояние\n setCustomSettings(prev => ({\n ...prev,\n [setting]: value\n }));\n\n // Диагностика: проверяем правильность сохранения промптов\n if (setting === 'prompts') {\n await verifyPromptStorage();\n }\n } else {\n console.warn('chrome.storage.local is not available');\n }\n } catch (error) {\n console.error(`Failed to save setting ${setting} to chrome.storage.local:`, error);\n throw error; // Пробрасываем ошибку для обработки в handleSettingChange\n }\n };\n\n // Диагностическая функция для проверки сохранения промптов\n const verifyPromptStorage = async () => {\n if (!selectedPlugin) return;\n\n try {\n const key = `${selectedPlugin.id}_prompts`;\n const stored = await chrome.storage.local.get([key]);\n console.log('🔍 Диагностика промптов:');\n console.log(` Plugin ID: ${selectedPlugin.id}`);\n console.log(` Storage key: ${key}`);\n console.log(' Сохраненные промпты:', stored[key]);\n\n if (stored[key]) {\n const prompts = stored[key] as PromptsStructure;\n console.log(' Структура промптов:');\n console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`);\n console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`);\n console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`);\n console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`);\n }\n } catch (error) {\n console.error('Ошибка диагностики промптов:', error);\n }\n };\n\n\n // Хелпер для получения значения настройки с приоритетом: chrome.storage -> manifest\n const getCustomSettingValue = (settingName: string, defaultValue: boolean | string | number | PromptsStructure): boolean | string | number | PromptsStructure => {\n if (customSettings && customSettings[settingName] !== undefined) {\n return customSettings[settingName];\n }\n\n // Специальная обработка для промптов: преобразуем структуру из manifest в PromptsStructure\n if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) {\n const promptsConfig = defaultValue as any;\n const result: PromptsStructure = {\n basic_analysis: { ru: {}, en: {} },\n deep_analysis: { ru: {}, en: {} }\n };\n\n // Извлекаем default значения из структуры manifest\n if (promptsConfig.basic_analysis?.ru?.default) {\n result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default;\n }\n if (promptsConfig.basic_analysis?.en?.default) {\n result.basic_analysis.en = promptsConfig.basic_analysis.en.default;\n }\n if (promptsConfig.deep_analysis?.ru?.default) {\n result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default;\n }\n if (promptsConfig.deep_analysis?.en?.default) {\n result.deep_analysis.en = promptsConfig.deep_analysis.en.default;\n }\n\n return result;\n }\n\n return defaultValue;\n };\n\n const renderCustomSetting = (key: string, config: CustomSetting): ReactNode | null => {\n const value = getCustomSettingValue(key, config.default);\n const disabled = isUpdating === key || !(settings.enabled ?? true);\n const localizedLabel = getLocalizedText(config.label);\n const localizedDescription = getLocalizedText(config.description);\n\n // Специальная обработка для промптов\n if (key === 'prompts') {\n return (\n
\n handleSettingChange(key, newValue)}\n locale={locale}\n t={t}\n globalAIKeys={aiKeys}\n pluginSettings={pluginSettings}\n />\n
\n );\n }\n\n if (config.type === 'boolean') {\n return (\n
\n handleSettingChange(key, val)}\n label={\n <>\n {localizedLabel}\n {localizedDescription && (\n \n i\n \n )}\n \n }\n />\n
\n );\n }\n\n if (config.type === 'select') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'text') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'number') {\n const handleNumberChange = (e: React.ChangeEvent) => {\n const numValue = parseFloat(e.target.value);\n if (isNaN(numValue)) return;\n\n // Валидация диапазона\n let validatedValue = numValue;\n if (config.min !== undefined && validatedValue < config.min) {\n validatedValue = config.min;\n }\n if (config.max !== undefined && validatedValue > config.max) {\n validatedValue = config.max;\n }\n\n handleSettingChange(key, validatedValue);\n };\n\n return (\n
\n \n
\n );\n }\n\n return null;\n };\n\n const handleSettingChange = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n // Проверяем, является ли настройка пользовательской\n const options = selectedPlugin.manifest?.options;\n if (options && options[setting as keyof typeof options]) {\n // Ленивая загрузка: загружаем настройки только при первом взаимодействии\n if (customSettings === null) {\n await loadCustomSettings();\n }\n\n try {\n setIsUpdating(setting);\n await saveCustomSetting(setting, value);\n } catch (error) {\n console.error(`Failed to update custom setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n } else {\n // Стандартные настройки (enabled/autorun) используем через callback\n if (onUpdateSetting) {\n try {\n setIsUpdating(setting);\n await onUpdateSetting(selectedPlugin.id, setting as keyof PluginSettings, value as boolean);\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n }\n }\n };\n\n // Precompute custom settings elements to satisfy TypeScript\n const optionEntries = Object.entries(selectedPlugin.manifest?.options ?? {}) as [string, CustomSetting][];\n const customSettingElements: ReactNode[] = optionEntries\n .map(([key, config]) => renderCustomSetting(key, config))\n .filter((item): item is ReactNode => item !== null);\n\n // Render helper to avoid union/unknown in JSX for plugin settings section\n const PluginSettingsSection = () => (\n
\n

Настройки плагина

\n
\n handleSettingChange('enabled', val)}\n label={\n <>\n Включен\n \n i\n \n \n }\n />\n
\n
\n handleSettingChange('autorun', val)}\n label={\n <>\n Автоматический запуск\n \n i\n \n \n }\n />\n
\n
\n );\n\n return (\n \n
\n

{selectedPlugin.name}

\n
\n
\n
\n

\n Версия: v{selectedPlugin.version}\n

\n

\n Статус:\n \n {settings.enabled ? 'Активен' : 'Неактивен'}\n \n

\n

\n Автор: {selectedPlugin.manifest?.author || 'Не указан'}\n

\n

\n Последнее обновление: {selectedPlugin.manifest?.last_updated || 'Неизвестно'}\n

\n
\n\n
\n

Описание

\n

{selectedPlugin.description}

\n
\n\n {/* Сайты/домены, на которых работает плагин */}\n {hostPermissions.length > 0 && (\n
\n

Сайты/домены

\n
    \n {hostPermissions.map((host: string, idx: number) => (\n
  • {host}
  • \n ))}\n
\n
\n )}\n\n {Array.isArray(selectedPlugin.manifest?.permissions) ? (\n
\n

Разрешения

\n
    \n {(selectedPlugin.manifest?.permissions ?? []).map((permission: string, idx: number) => (\n
  • {permission}
  • \n ))}\n
\n
\n ) : null}\n\n \n\n {/* Пользовательские настройки */}\n {(selectedPlugin.manifest?.options && Object.keys(selectedPlugin.manifest.options).length > 0) ? (\n
\n

Дополнительные настройки

\n {customSettingElements}\n
\n ) : null}\n\n\n
\n
\n
\n );\n};\n\nexport { PluginDetails };\n\nexport default PluginDetails;\n","import '../../../options/src/Options.css'; // Импорт стилей из options для идентичности\nimport type React from 'react';\nimport OptionsPluginDetails from '../../../options/src/components/PluginDetails';\nimport type { PluginSettings } from '@extension/storage';\n\ninterface PluginDetailsProps {\n plugin: Plugin;\n onUpdateSetting?: (pluginId: string, setting: string, value: boolean) => Promise;\n}\n\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\nexport const PluginDetails: React.FC = ({ plugin, onUpdateSetting }) => {\n // Адаптируем onUpdateSetting для совместимости с OptionsPluginDetails\n const adaptedOnUpdateSetting = onUpdateSetting ? async (pluginId: string, setting: keyof PluginSettings, value: boolean): Promise => {\n try {\n await onUpdateSetting(pluginId, setting as string, value);\n return true; // Успешно\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n return false; // Неудача\n }\n } : undefined;\n\n const adaptedProps = {\n selectedPlugin: {\n ...plugin,\n description: plugin.description || 'Описание не указано',\n icon: plugin.icon || '',\n manifest: plugin.manifest || {} as any\n },\n locale: 'ru' as const,\n onUpdateSetting: adaptedOnUpdateSetting\n };\n\n return ;\n};\n","import type { ExcludeValuesFromBaseArrayType } from './types.js';\n\nexport const excludeValuesFromBaseArray = (\n baseArray: B,\n excludeArray: E,\n) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType;\n\nexport const sleep = async (time: number) => new Promise(r => setTimeout(r, time));\n\n/**\n * Унифицированное получение pageKey из URL страницы.\n * Удаляет search/hash, возвращает нормализованный URL или 'unknown-page'.\n */\nexport const getPageKey = function (currentTabUrl: string | null): string {\n if (!currentTabUrl) return 'unknown-page';\n try {\n const url = new URL(currentTabUrl);\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return currentTabUrl;\n }\n};\n","import { useState, useEffect, useRef, useCallback } from 'react';\n\ninterface UseLazyChatSyncOptions {\n pluginId: string;\n pageKey: string;\n debounceMs?: number; // Задержка перед синхронизацией (по умолчанию 1000ms)\n}\n\ninterface UseLazyChatSyncReturn {\n message: string;\n setMessage: (text: string) => void;\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n loadDraft: () => Promise;\n clearDraft: () => Promise;\n draftText: string;\n}\n\nexport const useLazyChatSync = ({\n pluginId,\n pageKey,\n debounceMs = 1000,\n}: UseLazyChatSyncOptions): UseLazyChatSyncReturn => {\n const [message, setMessageState] = useState('');\n const [isDraftSaved, setIsDraftSaved] = useState(false);\n const [isDraftLoading, setIsDraftLoading] = useState(false);\n const [draftError, setDraftError] = useState(null);\n const [draftText, setDraftText] = useState('');\n\n const debounceRef = useRef(null);\n const lastSavedText = useRef('');\n\n // Функция для сохранения черновика\n const saveDraft = useCallback(\n async (text: string) => {\n if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился\n console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: text,\n });\n lastSavedText.current = text;\n setIsDraftSaved(true);\n setDraftError(null);\n setDraftText(text);\n console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text });\n } catch (error) {\n console.error('[useLazyChatSync] Error saving draft:', error);\n setDraftError('Ошибка сохранения черновика');\n setIsDraftSaved(false);\n }\n },\n [pluginId, pageKey],\n );\n\n // Функция для загрузки черновика\n const loadDraft = useCallback(async () => {\n setIsDraftLoading(true);\n setDraftError(null);\n console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey });\n try {\n const response = await chrome.runtime.sendMessage({\n type: 'GET_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n });\n if (response?.draftText) {\n setMessageState(response.draftText);\n lastSavedText.current = response.draftText;\n setIsDraftSaved(true);\n setDraftText(response.draftText);\n console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText });\n } else {\n setMessageState('');\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftText('');\n console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey });\n }\n } catch (error) {\n console.error('[useLazyChatSync] Error loading draft:', error);\n setDraftError('Ошибка загрузки черновика');\n } finally {\n setIsDraftLoading(false);\n }\n }, [pluginId, pageKey]);\n\n // Функция для очистки черновика\n const clearDraft = useCallback(async () => {\n console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: '',\n });\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftError(null);\n setDraftText('');\n setMessageState('');\n console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey });\n } catch (error) {\n console.error('[useLazyChatSync] Error clearing draft:', error);\n setDraftError('Ошибка очистки черновика');\n }\n }, [pluginId, pageKey]);\n\n // Обновленная функция setMessage с ленивой синхронизацией\n const setMessage = useCallback(\n (text: string) => {\n setMessageState(text);\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (text.length === 0) {\n clearDraft();\n } else {\n debounceRef.current = setTimeout(() => {\n saveDraft(text);\n }, debounceMs);\n }\n console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text });\n },\n [debounceMs, saveDraft, clearDraft, pluginId, pageKey],\n );\n\n // Очистка таймера при размонтировании\n useEffect(\n () => () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n },\n [],\n );\n\n // Автоматическая загрузка черновика при монтировании\n useEffect(() => {\n loadDraft();\n }, [loadDraft]);\n\n // При смене pageKey всегда загружаем draft, не сбрасываем message вручную\n useEffect(() => {\n console.log('[useLazyChatSync] pageKey изменился:', pageKey);\n setIsDraftSaved(false);\n setIsDraftLoading(false);\n setDraftError(null);\n lastSavedText.current = '';\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n loadDraft();\n }, [pageKey, loadDraft]);\n\n return {\n message,\n setMessage,\n isDraftSaved,\n isDraftLoading,\n draftError,\n loadDraft,\n clearDraft,\n draftText,\n };\n};\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","/**\n * Storage area type for persisting and exchanging data.\n * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview\n */\nexport var StorageEnum;\n(function (StorageEnum) {\n /**\n * Persist data locally against browser restarts. Will be deleted by uninstalling the extension.\n * @default\n */\n StorageEnum[\"Local\"] = \"local\";\n /**\n * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply.\n */\n StorageEnum[\"Sync\"] = \"sync\";\n /**\n * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a\n * json schema for company wide config.\n */\n StorageEnum[\"Managed\"] = \"managed\";\n /**\n * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and\n * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access.\n * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session)\n */\n StorageEnum[\"Session\"] = \"session\";\n})(StorageEnum || (StorageEnum = {}));\n/**\n * Global access level requirement for the {@link StorageEnum.Session} Storage Area.\n * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel)\n */\nexport var SessionAccessLevelEnum;\n(function (SessionAccessLevelEnum) {\n /**\n * Storage can only be accessed by Extension pages (not Content scripts).\n * @default\n */\n SessionAccessLevelEnum[\"ExtensionPagesOnly\"] = \"TRUSTED_CONTEXTS\";\n /**\n * Storage can be accessed by both Extension pages and Content scripts.\n */\n SessionAccessLevelEnum[\"ExtensionPagesAndContentScripts\"] = \"TRUSTED_AND_UNTRUSTED_CONTEXTS\";\n})(SessionAccessLevelEnum || (SessionAccessLevelEnum = {}));\n","import { SessionAccessLevelEnum, StorageEnum } from './enums.js';\n/**\n * Chrome reference error while running `processTailwindFeatures` in tailwindcss.\n * To avoid this, we need to check if globalThis.chrome is available and add fallback logic.\n */\nconst chrome = globalThis.chrome;\n/**\n * Sets or updates an arbitrary cache with a new value or the result of an update function.\n */\nconst updateCache = async (valueOrUpdate, cache) => {\n // Type guard to check if our value or update is a function\n const isFunction = (value) => typeof value === 'function';\n // Type guard to check in case of a function if it's a Promise\n const returnsPromise = (func) => \n // Use ReturnType to infer the return type of the function and check if it's a Promise\n func instanceof Promise;\n if (isFunction(valueOrUpdate)) {\n // Check if the function returns a Promise\n if (returnsPromise(valueOrUpdate)) {\n return valueOrUpdate(cache);\n }\n else {\n return valueOrUpdate(cache);\n }\n }\n else {\n return valueOrUpdate;\n }\n};\n/**\n * If one session storage needs access from content scripts, we need to enable it globally.\n * @default false\n */\nlet globalSessionAccessLevelFlag = false;\n/**\n * Checks if the storage permission is granted in the manifest.json.\n */\nconst checkStoragePermission = (storageEnum) => {\n if (!chrome) {\n return;\n }\n if (!chrome.storage[storageEnum]) {\n throw new Error(`\"storage\" permission in manifest.ts: \"storage ${storageEnum}\" isn't defined`);\n }\n};\n/**\n * Creates a storage area for persisting and exchanging data.\n */\nexport const createStorage = (key, fallback, config) => {\n let cache = null;\n let initialCache = false;\n let listeners = [];\n const storageEnum = config?.storageEnum ?? StorageEnum.Local;\n const liveUpdate = config?.liveUpdate ?? false;\n const serialize = config?.serialization?.serialize ?? ((v) => v);\n const deserialize = config?.serialization?.deserialize ?? (v => v);\n // Set global session storage access level for StoryType.Session, only when not already done but needed.\n if (globalSessionAccessLevelFlag === false &&\n storageEnum === StorageEnum.Session &&\n config?.sessionAccessForContentScripts === true) {\n checkStoragePermission(storageEnum);\n chrome?.storage[storageEnum]\n .setAccessLevel({\n accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts,\n })\n .catch(error => {\n console.error(error);\n console.error('Please call .setAccessLevel() into different context, like a background script.');\n });\n globalSessionAccessLevelFlag = true;\n }\n // Register life cycle methods\n const get = async () => {\n checkStoragePermission(storageEnum);\n const value = await chrome?.storage[storageEnum].get([key]);\n if (!value) {\n return fallback;\n }\n return deserialize(value[key]) ?? fallback;\n };\n const set = async (valueOrUpdate) => {\n if (!initialCache) {\n cache = await get();\n }\n cache = await updateCache(valueOrUpdate, cache);\n await chrome?.storage[storageEnum].set({ [key]: serialize(cache) });\n _emitChange();\n };\n const subscribe = (listener) => {\n listeners = [...listeners, listener];\n return () => {\n listeners = listeners.filter(l => l !== listener);\n };\n };\n const getSnapshot = () => cache;\n const _emitChange = () => {\n listeners.forEach(listener => listener());\n };\n // Listener for live updates from the browser\n const _updateFromStorageOnChanged = async (changes) => {\n // Check if the key we are listening for is in the changes object\n if (changes[key] === undefined)\n return;\n const valueOrUpdate = deserialize(changes[key].newValue);\n if (cache === valueOrUpdate)\n return;\n cache = await updateCache(valueOrUpdate, cache);\n _emitChange();\n };\n get().then(data => {\n cache = data;\n initialCache = true;\n _emitChange();\n });\n // Register listener for live updates for our storage area\n if (liveUpdate) {\n chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);\n }\n return {\n get,\n set,\n getSnapshot,\n subscribe,\n };\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('theme-storage-key', {\n theme: 'system',\n isLight: getSystemTheme(),\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\n// Функция для определения системной темы\nfunction getSystemTheme() {\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: light)').matches;\n }\n return true; // По умолчанию светлая тема\n}\nexport const exampleThemeStorage = {\n ...storage,\n toggle: async () => {\n await storage.set(currentState => {\n let newTheme;\n switch (currentState.theme) {\n case 'light':\n newTheme = 'dark';\n break;\n case 'dark':\n newTheme = 'system';\n break;\n case 'system':\n default:\n newTheme = 'light';\n break;\n }\n const isLight = newTheme === 'system' ? getSystemTheme() : newTheme === 'light';\n return {\n theme: newTheme,\n isLight,\n };\n });\n },\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('chat-alignment-storage-key', {\n alignment: 'left',\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\nexport const exampleChatAlignmentStorage = {\n ...storage,\n setAlignment: async (alignment) => {\n await storage.set({ alignment });\n },\n getAlignment: async () => {\n const state = await storage.get();\n return state.alignment;\n },\n};\n","/**\n * PluginControlPanel.tsx - Панель управления плагином с чатом\n *\n * ИСПРАВЛЕНИЕ ПРОБЛЕМЫ С ПОЛУЧЕНИЕМ ОТВЕТА GET_PLUGIN_CHAT:\n * ========================================================\n *\n * Проблема: Background отправлял ответ через sendResponse() callback, но компонент\n * ожидал ответ через chrome.runtime.sendMessage() с типом 'GET_PLUGIN_CHAT_RESPONSE'.\n *\n * Решение: Изменен механизм коммуникации на использование Promise-based подхода\n * с помощью sendMessageToBackgroundAsync(), который правильно работает с sendResponse().\n *\n * Теперь:\n * 1. Компонент отправляет GET_PLUGIN_CHAT через chrome.runtime.sendMessage()\n * 2. Background получает сообщение и отвечает через sendResponse()\n * 3. Компонент получает ответ через Promise и обрабатывает его\n *\n * Диагностика:\n * - Логи sendMessageToBackgroundAsync покажут отправку и получение ответа\n * - Логи loadChat покажут обработку ответа\n * - Логи processChatResponse покажут разбор данных чата\n */\n\nimport { DraftStatus } from './DraftStatus';\nimport { PluginDetails } from './PluginDetails';\nimport { getPageKey } from '../../../../packages/shared/lib/utils/helpers';\nimport { useLazyChatSync } from '../hooks/useLazyChatSync';\nimport { saveAs } from 'file-saver';\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport './PluginControlPanel.css';\nimport type React from 'react';\nimport { exampleChatAlignmentStorage, type ChatAlignment } from '@extension/storage';\n\n// Определение типа Plugin для PluginControlPanel\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Новый тип для сообщений чата\ninterface ChatMessage {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: number;\n}\n\ninterface PluginControlPanelProps {\n plugin: Plugin;\n currentView: PanelView;\n isRunning: boolean;\n isPaused: boolean;\n currentTabUrl: string | null;\n onStart: () => void;\n onPause: () => void;\n onStop: () => void;\n onClose: () => void;\n}\n\nexport type PanelView = 'chat' | 'details';\n\nexport const PluginControlPanel: React.FC = ({\n plugin,\n currentView,\n isRunning,\n isPaused,\n currentTabUrl,\n onStart,\n onPause,\n onStop,\n onClose,\n}) => {\n // Состояние для активной вкладки в панели управления\n const [activeTab, setActiveTab] = useState('chat');\n // Состояние для текущего pageKey с динамическим обновлением\n const [currentPageKey, setCurrentPageKey] = useState(getPageKey(currentTabUrl));\n\n const [chatTextAlign, setChatTextAlign] = useState('left');\n\n // useEffect для обновления pageKey при изменении currentTabUrl\n useEffect(() => {\n const newPageKey = getPageKey(currentTabUrl);\n console.log('[PluginControlPanel] currentTabUrl изменился:', {\n oldPageKey: currentPageKey,\n newPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n setCurrentPageKey(newPageKey);\n }, [currentTabUrl]);\n\n useEffect(() => {\n const loadAlignment = async () => {\n const alignment = await exampleChatAlignmentStorage.getAlignment();\n setChatTextAlign(alignment);\n };\n\n loadAlignment();\n\n const unsubscribe = exampleChatAlignmentStorage.subscribe(() => {\n loadAlignment();\n });\n\n return unsubscribe;\n }, []);\n // Используем хук для ленивой синхронизации\n const { message, setMessage, isDraftSaved, isDraftLoading, draftError, loadDraft, clearDraft, draftText } =\n useLazyChatSync({\n pluginId: plugin.id,\n pageKey: currentPageKey, // <-- Теперь динамический pageKey\n debounceMs: 1000, // 1 секунда задержки\n });\n\n const [messages, setMessages] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n const [inputHeight, setInputHeight] = useState(60); // Начальная высота поля ввода\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef(null);\n const textareaRef = useRef(null);\n\n useEffect(() => {\n if (!isRunning) {\n // Удалить все вызовы setStopped(...)\n }\n }, [isRunning]);\n\n const handleStart = () => {\n // Удалить все вызовы setStopped(...)\n onStart();\n };\n\n const pluginName =\n plugin.name || (typeof plugin.manifest?.name === 'string' ? plugin.manifest.name : '') || plugin.id;\n\n // Получаем ключ чата для текущего плагина и страницы\n const pluginId = plugin.id;\n\n // Вспомогательная функция для отправки сообщений в background с ожиданием ответа\n const sendMessageToBackgroundAsync = useCallback(async (message: any): Promise => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n try {\n const response = await chrome.runtime.sendMessage(messageWithId);\n return response;\n } catch (error) {\n console.error('[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:', error);\n throw error;\n }\n }, []);\n\n // Вспомогательная функция для отправки сообщений в background без ожидания ответа (для обратной совместимости)\n const sendMessageToBackground = useCallback((message: any): void => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n chrome.runtime.sendMessage(messageWithId);\n }, []);\n\n // Функция для тестирования обработки сообщений с проблемными данными\n const testMessageProcessing = useCallback(() => {\n console.log('[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными');\n\n // Тест 1: Сообщение с объектом вместо строки в text\n const testMessageWithObject = {\n messages: [{\n id: 'test_obj_1',\n text: { content: 'Это объект вместо строки', type: 'object' }, // Объект вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 2: Сообщение с null в text\n const testMessageWithNull = {\n messages: [{\n id: 'test_null_1',\n text: null, // null вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 3: Сообщение с undefined в text\n const testMessageWithUndefined = {\n messages: [{\n id: 'test_undef_1',\n text: undefined, // undefined вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Объявляем processChatResponse локально для избежания проблем с temporal dead zone\n const localProcessChatResponse = (response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(msg.timestamp || Date.now() + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: msg.timestamp || Date.now(),\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n };\n\n try {\n console.log('[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text');\n localProcessChatResponse(testMessageWithObject);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 1 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 2: Обработка сообщения с null в text');\n localProcessChatResponse(testMessageWithNull);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 2 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text');\n localProcessChatResponse(testMessageWithUndefined);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 3 провалился:', error);\n }\n\n console.log('[PluginControlPanel] ✅ Тестирование обработки сообщений завершено');\n }, []); // Убрали processChatResponse из зависимостей\n\n // Вспомогательная функция для обработки ответа чата\n const processChatResponse = useCallback((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n\n console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${index}:`, textContent);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n\n console.log(`[PluginControlPanel] TextContent after JSON parse for message ${index}:`, textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n console.log(`[PluginControlPanel] Final converted text for message ${index}:`, convertedMsg.text);\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n }, []);\n\n // Загрузка истории чата при монтировании или смене плагина/страницы\n const loadChat = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n console.log('[PluginControlPanel] ===== НАЧАЛО loadChat =====', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString(),\n isRunning,\n isPaused\n });\n\n try {\n console.log('[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT');\n const response = await sendMessageToBackgroundAsync({\n type: 'GET_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n console.log('[PluginControlPanel] loadChat - получен ответ от background:', response);\n console.log('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:', {\n loading,\n messagesCount: messages.length,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку при получении ответа\n console.log('[PluginControlPanel] loadChat - loading сброшен в false');\n\n if (response?.error) {\n console.error('[PluginControlPanel] loadChat - ошибка в ответе:', response.error);\n setError(`Ошибка загрузки чата: ${response.error}`);\n setMessages([]);\n } else {\n console.log('[PluginControlPanel] loadChat - обрабатываем успешный ответ');\n // Используем анонимную функцию вместо прямого вызова processChatResponse\n // чтобы избежать проблемы с temporal dead zone\n ((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n \n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n \n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распаршен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n }\n \n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n })(response);\n console.log('[PluginControlPanel] loadChat: чат успешно загружен');\n }\n\n } catch (error) {\n console.error('[PluginControlPanel] loadChat - ошибка при получении ответа:', error);\n console.error('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:', {\n loading,\n messagesCount: messages.length,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString()\n });\n\n // Детальное логирование ошибки с трассировкой стека\n console.error('[PluginControlPanel] loadChat - ERROR DETAILS:', {\n error,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : 'No stack trace',\n errorName: error instanceof Error ? error.name : 'Unknown error type',\n timestamp: new Date().toISOString(),\n pluginId,\n pageKey: currentPageKey\n });\n\n setLoading(false);\n console.log('[PluginControlPanel] loadChat - loading сброшен в false в catch блоке');\n\n // Улучшенная обработка ошибок с проверкой типа\n let errorMessage = 'Ошибка связи с background';\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Специальная обработка для TypeError с substring\n if (error.name === 'TypeError' && error.message.includes('substring')) {\n errorMessage += ' (ошибка обработки текста - проверьте тип данных)';\n console.error('[PluginControlPanel] CRITICAL: substring error detected:', {\n originalError: error,\n stack: error.stack,\n context: { pluginId, pageKey: currentPageKey }\n });\n }\n } else {\n errorMessage += `: ${String(error)}`;\n }\n\n setError(errorMessage);\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====');\n }, [pluginId, currentPageKey, sendMessageToBackgroundAsync, currentTabUrl, isRunning, isPaused]);\n\n // Добавить useEffect для вызова loadChat при монтировании и смене pluginId/pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n\n // Вызываем асинхронную функцию без await, так как useEffect не может быть async\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:', error);\n });\n }, [loadChat]);\n\n // useEffect для перезагрузки чата при смене pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик');\n // Перезагружаем чат для новой страницы\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] Ошибка при перезагрузке чата:', error);\n });\n // Перезагружаем черновик для новой страницы\n loadDraft();\n }, [currentPageKey, loadChat, loadDraft]);\n\n // Событийная синхронизация чата между вкладками и обработка результатов сохранения сообщений\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений', {\n pluginId,\n pageKey: currentPageKey,\n timestamp: new Date().toISOString()\n });\n\n const handleChatUpdate = (event: { type: string; pluginId: string; pageKey: string; messages?: ChatMessage[]; messageId?: string; response?: any; success?: boolean; error?: string; message?: any; timestamp?: number }) => {\n console.log('[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====', {\n type: event?.type,\n pluginId: event?.pluginId,\n pageKey: event?.pageKey,\n messageId: event?.messageId,\n hasResponse: !!event?.response,\n responseType: event?.response ? typeof event.response : 'none',\n timestamp: new Date().toISOString()\n });\n\n // Обработка обновлений чата от других компонентов/вкладок\n if (event?.type === 'PLUGIN_CHAT_UPDATED' && event.pluginId === pluginId && event.pageKey === currentPageKey) {\n console.log('[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные');\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:', {\n loading,\n messagesCount: messages.length,\n currentPageKey,\n pluginId,\n timestamp: new Date().toISOString()\n });\n setLoading(false); // Гарантированный сброс loading перед загрузкой чата\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat');\n // Запрашиваем актуальные данные чата асинхронно\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:', error);\n });\n }\n\n // NOTE: GET_PLUGIN_CHAT_RESPONSE больше не обрабатывается здесь,\n // поскольку ответы на GET_PLUGIN_CHAT теперь обрабатываются через Promise в loadChat()\n\n // Логируем все сообщения, которые приходят, но не обрабатываются\n if (event?.type !== 'PLUGIN_CHAT_UPDATED' && event?.type !== 'GET_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:', {\n type: event?.type,\n fullEvent: event,\n timestamp: new Date().toISOString()\n });\n }\n\n // Обработка результатов сохранения сообщений\n if (event?.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:', event);\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения', event.error);\n setError(`Ошибка сохранения сообщения: ${event.error}`);\n }\n }\n\n // Обработка результатов удаления чата\n if (event?.type === 'DELETE_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат удаления чата:', event);\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:', {\n loading,\n messagesCount: messages.length,\n eventSuccess: event.success,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE');\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: чат успешно удален');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка удаления чата', event.error);\n setError(`Ошибка удаления чата: ${event.error}`);\n }\n }\n\n // === PYODIDE MESSAGE HANDLER ===\n if (event?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', event.message);\n\n if (event.message?.content) {\n try {\n // Строгая проверка типа content для Pyodide сообщений\n let content = event.message.content;\n let messageTimestamp = event.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] PYODIDE content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] PYODIDE content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из PYODIDE content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: event.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:', pyodideError, event);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:', event.message);\n }\n }\n };\n\n // Слушатель для обработки результатов операций с чатом\n const handleChatOperationResult = (message: any) => {\n if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения', message);\n\n if (message.success) {\n console.log('[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено');\n // Не нужно ничего делать дополнительно - обновление придет через PLUGIN_CHAT_UPDATED\n } else {\n console.error('[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения', message.error);\n setError(`Ошибка сохранения сообщения: ${message.error}`);\n }\n }\n };\n\n chrome.runtime.onMessage.addListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы');\n\n return () => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений');\n chrome.runtime.onMessage.removeListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n };\n }, [pluginId, currentPageKey, sendMessageToBackground, loadChat]);\n\n // Восстановление черновика при возврате на вкладку 'Чат'\n useEffect(() => {\n if (currentView === 'chat') {\n loadDraft(); // Явно загружаем черновик при возврате на вкладку чата\n }\n }, [currentView, loadDraft]);\n\n // Логирование каждого рендера и ключевых параметров\n useEffect(() => {\n console.log('[PluginControlPanel] === РЕНДЕР ===', {\n pluginId,\n pageKey: currentPageKey,\n draftText,\n message,\n currentView,\n isRunning,\n isPaused,\n });\n });\n\n // Глобальный обработчик ошибок для ловли проблем с substring\n useEffect(() => {\n const originalConsoleError = console.error;\n console.error = (...args) => {\n // Перехватываем ошибки substring\n const errorMessage = args.join(' ');\n if (errorMessage.includes('substring') && errorMessage.includes('is not a function')) {\n console.error('[PluginControlPanel] 🔴 CRITICAL: substring error detected!');\n console.error('[PluginControlPanel] Error details:', args);\n console.error('[PluginControlPanel] Stack trace:', new Error().stack);\n // Не блокируем оригинальную обработку ошибки\n }\n originalConsoleError.apply(console, args);\n };\n\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring активирован');\n\n return () => {\n console.error = originalConsoleError;\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован');\n };\n }, []);\n\n // Слушатель для Pyodide сообщений через custom events\n useEffect(() => {\n console.log('[PluginControlPanel] Настройка слушателя для pyodide messages');\n\n const handlePyodideCustomEvent = (event: any) => {\n const data = event.detail;\n console.log('[PluginControlPanel] Получен Pyodide custom event:', data);\n\n if (data?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', data.message);\n\n if (data.message?.content) {\n try {\n // Строгая проверка типа content\n let content = data.message.content;\n let messageTimestamp = data.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] Pyodide content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] Pyodide content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из Pyodide content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Pyodide content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: data.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки Pyodide сообщения:', pyodideError, data);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] Pyodide сообщение без content:', data.message);\n }\n }\n };\n\n window.addEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован');\n\n return () => {\n window.removeEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events удален');\n };\n }, []);\n\n // --- Синхронизация message с draftText после загрузки черновика ---\n useEffect(() => {\n if (typeof draftText === 'string') {\n setMessage(draftText);\n console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText);\n }\n }, [draftText, setMessage]);\n\n const handleSendMessage = (): void => {\n console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message });\n if (!message.trim()) return;\n\n const newMessage: ChatMessage = {\n id: Date.now().toString(),\n text: message.trim(),\n isUser: true,\n timestamp: Date.now(),\n };\n\n // Очищаем сообщение через хук\n setMessage('');\n setError(null); // Сбрасываем предыдущие ошибки\n\n console.log('[PluginControlPanel] handleSendMessage: отправка сообщения в background');\n\n sendMessageToBackground({\n type: 'SAVE_PLUGIN_CHAT_MESSAGE',\n pluginId,\n pageKey: currentPageKey,\n message: {\n role: 'user',\n content: newMessage.text,\n timestamp: newMessage.timestamp,\n },\n });\n\n // Очищаем черновик сразу после отправки\n clearDraft();\n };\n\n // Обработка изменения размера разделителя\n useEffect(() => {\n const handleResizeMove = (event: MouseEvent): void => {\n if (!isResizing) return;\n\n const container = document.querySelector('.chat-view') as HTMLElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n const newHeight = containerRect.bottom - event.clientY;\n const minHeight = 100; // Минимальная высота чата\n const maxHeight = containerRect.height - 80; // Максимальная высота чата\n\n if (newHeight >= minHeight && newHeight <= maxHeight) {\n setInputHeight(containerRect.height - newHeight);\n }\n };\n\n const handleResizeEnd = (): void => {\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n if (isResizing) {\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n }\n\n return () => {\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isResizing]);\n\n // Автоскролл к последнему сообщению\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[messages] - состояние messages обновлено:', {\n messagesCount: messages.length,\n firstMessage: messages[0] ? {\n id: messages[0].id,\n text: typeof messages[0].text === 'string' ? messages[0].text.substring(0, 50) : String(messages[0].text || '').substring(0, 50),\n isUser: messages[0].isUser,\n timestamp: messages[0].timestamp,\n textType: typeof messages[0].text\n } : null,\n // Безопасная обработка всех сообщений с проверкой типов\n allMessages: messages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 30) : String(m.text || '').substring(0, 30);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (msgError) {\n console.warn('[PluginControlPanel] Error in message logging:', msgError, m);\n return { id: m.id, text: '[LOGGING ERROR]', isUser: m.isUser, textType: typeof m.text };\n }\n }),\n timestamp: new Date().toISOString()\n });\n }, [messages]);\n\n // Фокус на поле ввода при открытии чата\n useEffect(() => {\n if (currentView === 'chat') {\n setTimeout(() => textareaRef.current?.focus(), 100);\n }\n }, [currentView]);\n\n const handleTextareaChange = (event: React.ChangeEvent): void => {\n setMessage(event.target.value); // Используем хук вместо setMessage\n // Автоматическое изменение высоты\n const textarea = event.target;\n textarea.style.height = 'auto';\n const newHeight = Math.min(Math.max(textarea.scrollHeight, 60), 200); // Минимум 60px, максимум 200px\n textarea.style.height = `${newHeight}px`;\n setInputHeight(newHeight);\n };\n\n const handleKeyPress = (event: React.KeyboardEvent): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n // Очистка чата (удаление всей истории)\n const handleClearChat = (): void => {\n setLoading(true);\n setError(null);\n\n sendMessageToBackground({\n type: 'DELETE_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n // Очищаем локальное состояние сразу\n setMessages([]);\n clearDraft(); // Очищаем черновик\n };\n\n // Экспорт чата в JSON\n const handleExportChat = (): void => {\n const data = JSON.stringify(messages, null, 2);\n const blob = new Blob([data], { type: 'application/json' });\n saveAs(blob, `plugin-chat-${pluginId}.json`);\n };\n\n return (\n
\n
\n
\n {\n const firstChar = typeof pluginName === 'string' && pluginName.length > 0 ? pluginName.charAt(0) : 'P';\n (event.currentTarget as HTMLImageElement).src =\n `data:image/svg+xml;utf8,${firstChar}`;\n }}\n />\n

{pluginName}

\n
\n
\n \n {isRunning ? '⏹️' : '▶️'}\n \n \n ⏸️\n \n \n ⏹️\n \n \n ✕\n \n
\n
\n
\n setActiveTab('chat')}\n >\n Чат\n \n setActiveTab('details')}\n >\n Детали\n \n
\n
\n {activeTab === 'chat' && (\n
\n
\n

Чат

\n
\n \n \n \n 🧪 Тест\n \n
\n
\n
\n {loading &&
Загрузка сообщений...
}\n {error &&
{error}
}\n {!loading && !error && messages.length === 0 && (\n
\n

Нет сообщений

\n

Напишите первое сообщение!

\n
\n )}\n {/* Отображение сообщений чата */}\n
\n {messages.map((msg, idx) => {\n console.log('[PluginControlPanel] render message:', idx, msg);\n\n // Парсинг JSON в тексте сообщения перед рендерингом\n let displayText = msg.text;\n let displayTimestamp = msg.timestamp;\n\n console.log('[PluginControlPanel] Raw message text before parsing for message', idx, ':', msg.text);\n\n try {\n const parsed = JSON.parse(displayText);\n if (typeof parsed === 'object' && parsed !== null && 'content' in parsed) {\n console.log('[PluginControlPanel] Парсинг JSON в рендере:', parsed);\n let content = parsed.content;\n if (typeof content === 'object') {\n displayText = JSON.stringify(content);\n } else {\n displayText = String(content || '');\n }\n if (parsed.timestamp && typeof parsed.timestamp === 'number') {\n displayTimestamp = parsed.timestamp;\n }\n } else if (typeof parsed === 'string') {\n // Если JSON содержит просто строку\n displayText = parsed;\n } else if (typeof parsed === 'object' && parsed !== null) {\n // Если JSON содержит объект без поля content, берем первое строковое поле\n const stringFields = Object.values(parsed).filter(val => typeof val === 'string');\n if (stringFields.length > 0) {\n displayText = String(stringFields[0]);\n } else {\n displayText = JSON.stringify(parsed);\n }\n }\n } catch (parseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Текст не является JSON, рендерим как есть');\n }\n\n console.log('[PluginControlPanel] Display text after parsing for message', idx, ':', displayText);\n\n return (\n \n
\n {displayText}\n \n {new Date(displayTimestamp).toLocaleTimeString()}\n \n
\n
\n );\n })}\n
\n
\n
\n
\n \n \n 📤\n \n \n
\n
\n )}\n {activeTab === 'details' && (\n \n )}\n
\n
\n );\n};","import React, { useState, useEffect, useCallback } from 'react';\nimport './ToastNotifications.css';\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n message: string;\n type: ToastType;\n duration: number;\n timestamp: number;\n}\n\ninterface ToastNotificationsProps {\n toasts: Toast[];\n onRemove: (id: string) => void;\n}\n\nexport const ToastNotifications: React.FC = ({ toasts, onRemove }) => {\n return (\n
\n {toasts.map((toast) => (\n \n ))}\n
\n );\n};\n\ninterface ToastItemProps {\n toast: Toast;\n onRemove: (id: string) => void;\n}\n\nconst ToastItem: React.FC = ({ toast, onRemove }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [isHiding, setIsHiding] = useState(false);\n\n useEffect(() => {\n // Animation in\n requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n // Auto remove\n if (toast.duration > 0) {\n const timer = setTimeout(() => {\n handleRemove();\n }, toast.duration);\n\n return () => clearTimeout(timer);\n }\n\n return undefined; // Explicitly return undefined when no timer is set\n }, [toast.duration]);\n\n const handleRemove = useCallback(() => {\n setIsHiding(true);\n setTimeout(() => {\n onRemove(toast.id);\n }, 300); // Animation duration\n }, [toast.id, onRemove]);\n\n return (\n
\n
\n {toast.message}\n \n
\n
\n );\n};\n\n// Toast manager hook\nexport function useToastManager() {\n const [toasts, setToasts] = useState([]);\n\n const addToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {\n const id = `toast-${Date.now()}-${Math.random()}`;\n const newToast: Toast = {\n id,\n message,\n type,\n duration,\n timestamp: Date.now()\n };\n\n setToasts(prev => {\n const updated = [...prev, newToast];\n // Limit to 3 toasts\n return updated.slice(-3);\n });\n\n return id;\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts(prev => prev.filter(toast => toast.id !== id));\n }, []);\n\n const clearAll = useCallback(() => {\n setToasts([]);\n }, []);\n\n return {\n toasts,\n addToast,\n removeToast,\n clearAll\n };\n}\n\n// Convenience functions\nexport const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {\n // This will be used with the toast manager\n console.log(`[Toast] ${type}: ${message}`);\n};\n\nexport const showSuccessToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'success', duration);\n};\n\nexport const showErrorToast = (message: string, duration: number = 5000) => {\n return showToast(message, 'error', duration);\n};\n\nexport const showWarningToast = (message: string, duration: number = 4000) => {\n return showToast(message, 'warning', duration);\n};\n\nexport const showInfoToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'info', duration);\n}; ","import React from 'react';\n\ninterface ThemeSwitcherProps {\n theme: 'light' | 'dark' | 'system';\n isLight: boolean;\n onToggle: () => void;\n isInSidebar?: boolean; // Контекст использования: sidebar или options\n}\n\nconst ThemeSwitcher: React.FC = ({ theme, isLight, onToggle, isInSidebar = false }) => {\n const getIcon = () => {\n switch (theme) {\n case 'light':\n return '🌙'; // Moon - to switch to dark\n case 'dark':\n return '💻'; // System icon - to switch to system\n case 'system':\n return '☀️'; // Sun - to switch to light\n default:\n return '🌙';\n }\n };\n\n const getTitle = () => {\n switch (theme) {\n case 'light':\n return 'Переключить на темную тему';\n case 'dark':\n return 'Переключить на системную тему';\n case 'system':\n return 'Переключить на светлую тему';\n default:\n return 'Переключить тему';\n }\n };\n\n const buttonStyle: React.CSSProperties = {\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: '50%',\n width: '40px',\n height: '40px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '20px',\n // marginTop только для Options (не в sidebar)\n ...(isInSidebar ? {} : { marginTop: '20px' })\n };\n\n return (\n \n );\n};\n\nexport default ThemeSwitcher;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t {\n const classMap = createClassMap(config);\n const {\n conflictingClassGroups,\n conflictingClassGroupModifiers\n } = config;\n const getClassGroupId = className => {\n const classParts = className.split(CLASS_PART_SEPARATOR);\n // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.\n if (classParts[0] === '' && classParts.length !== 1) {\n classParts.shift();\n }\n return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);\n };\n const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {\n const conflicts = conflictingClassGroups[classGroupId] || [];\n if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {\n return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];\n }\n return conflicts;\n };\n return {\n getClassGroupId,\n getConflictingClassGroupIds\n };\n};\nconst getGroupRecursive = (classParts, classPartObject) => {\n if (classParts.length === 0) {\n return classPartObject.classGroupId;\n }\n const currentClassPart = classParts[0];\n const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);\n const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;\n if (classGroupFromNextClassPart) {\n return classGroupFromNextClassPart;\n }\n if (classPartObject.validators.length === 0) {\n return undefined;\n }\n const classRest = classParts.join(CLASS_PART_SEPARATOR);\n return classPartObject.validators.find(({\n validator\n }) => validator(classRest))?.classGroupId;\n};\nconst arbitraryPropertyRegex = /^\\[(.+)\\]$/;\nconst getGroupIdForArbitraryProperty = className => {\n if (arbitraryPropertyRegex.test(className)) {\n const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];\n const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(':'));\n if (property) {\n // I use two dots here because one dot is used as prefix for class groups in plugins\n return 'arbitrary..' + property;\n }\n }\n};\n/**\n * Exported for testing only\n */\nconst createClassMap = config => {\n const {\n theme,\n classGroups\n } = config;\n const classMap = {\n nextPart: new Map(),\n validators: []\n };\n for (const classGroupId in classGroups) {\n processClassesRecursively(classGroups[classGroupId], classMap, classGroupId, theme);\n }\n return classMap;\n};\nconst processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {\n classGroup.forEach(classDefinition => {\n if (typeof classDefinition === 'string') {\n const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);\n classPartObjectToEdit.classGroupId = classGroupId;\n return;\n }\n if (typeof classDefinition === 'function') {\n if (isThemeGetter(classDefinition)) {\n processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);\n return;\n }\n classPartObject.validators.push({\n validator: classDefinition,\n classGroupId\n });\n return;\n }\n Object.entries(classDefinition).forEach(([key, classGroup]) => {\n processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);\n });\n });\n};\nconst getPart = (classPartObject, path) => {\n let currentClassPartObject = classPartObject;\n path.split(CLASS_PART_SEPARATOR).forEach(pathPart => {\n if (!currentClassPartObject.nextPart.has(pathPart)) {\n currentClassPartObject.nextPart.set(pathPart, {\n nextPart: new Map(),\n validators: []\n });\n }\n currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);\n });\n return currentClassPartObject;\n};\nconst isThemeGetter = func => func.isThemeGetter;\n\n// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance\nconst createLruCache = maxCacheSize => {\n if (maxCacheSize < 1) {\n return {\n get: () => undefined,\n set: () => {}\n };\n }\n let cacheSize = 0;\n let cache = new Map();\n let previousCache = new Map();\n const update = (key, value) => {\n cache.set(key, value);\n cacheSize++;\n if (cacheSize > maxCacheSize) {\n cacheSize = 0;\n previousCache = cache;\n cache = new Map();\n }\n };\n return {\n get(key) {\n let value = cache.get(key);\n if (value !== undefined) {\n return value;\n }\n if ((value = previousCache.get(key)) !== undefined) {\n update(key, value);\n return value;\n }\n },\n set(key, value) {\n if (cache.has(key)) {\n cache.set(key, value);\n } else {\n update(key, value);\n }\n }\n };\n};\nconst IMPORTANT_MODIFIER = '!';\nconst MODIFIER_SEPARATOR = ':';\nconst MODIFIER_SEPARATOR_LENGTH = MODIFIER_SEPARATOR.length;\nconst createParseClassName = config => {\n const {\n prefix,\n experimentalParseClassName\n } = config;\n /**\n * Parse class name into parts.\n *\n * Inspired by `splitAtTopLevelOnly` used in Tailwind CSS\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js\n */\n let parseClassName = className => {\n const modifiers = [];\n let bracketDepth = 0;\n let parenDepth = 0;\n let modifierStart = 0;\n let postfixModifierPosition;\n for (let index = 0; index < className.length; index++) {\n let currentCharacter = className[index];\n if (bracketDepth === 0 && parenDepth === 0) {\n if (currentCharacter === MODIFIER_SEPARATOR) {\n modifiers.push(className.slice(modifierStart, index));\n modifierStart = index + MODIFIER_SEPARATOR_LENGTH;\n continue;\n }\n if (currentCharacter === '/') {\n postfixModifierPosition = index;\n continue;\n }\n }\n if (currentCharacter === '[') {\n bracketDepth++;\n } else if (currentCharacter === ']') {\n bracketDepth--;\n } else if (currentCharacter === '(') {\n parenDepth++;\n } else if (currentCharacter === ')') {\n parenDepth--;\n }\n }\n const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);\n const baseClassName = stripImportantModifier(baseClassNameWithImportantModifier);\n const hasImportantModifier = baseClassName !== baseClassNameWithImportantModifier;\n const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;\n return {\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n };\n };\n if (prefix) {\n const fullPrefix = prefix + MODIFIER_SEPARATOR;\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.substring(fullPrefix.length)) : {\n isExternal: true,\n modifiers: [],\n hasImportantModifier: false,\n baseClassName: className,\n maybePostfixModifierPosition: undefined\n };\n }\n if (experimentalParseClassName) {\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => experimentalParseClassName({\n className,\n parseClassName: parseClassNameOriginal\n });\n }\n return parseClassName;\n};\nconst stripImportantModifier = baseClassName => {\n if (baseClassName.endsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(0, baseClassName.length - 1);\n }\n /**\n * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.\n * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864\n */\n if (baseClassName.startsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(1);\n }\n return baseClassName;\n};\n\n/**\n * Sorts modifiers according to following schema:\n * - Predefined modifiers are sorted alphabetically\n * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it\n */\nconst createSortModifiers = config => {\n const orderSensitiveModifiers = Object.fromEntries(config.orderSensitiveModifiers.map(modifier => [modifier, true]));\n const sortModifiers = modifiers => {\n if (modifiers.length <= 1) {\n return modifiers;\n }\n const sortedModifiers = [];\n let unsortedModifiers = [];\n modifiers.forEach(modifier => {\n const isPositionSensitive = modifier[0] === '[' || orderSensitiveModifiers[modifier];\n if (isPositionSensitive) {\n sortedModifiers.push(...unsortedModifiers.sort(), modifier);\n unsortedModifiers = [];\n } else {\n unsortedModifiers.push(modifier);\n }\n });\n sortedModifiers.push(...unsortedModifiers.sort());\n return sortedModifiers;\n };\n return sortModifiers;\n};\nconst createConfigUtils = config => ({\n cache: createLruCache(config.cacheSize),\n parseClassName: createParseClassName(config),\n sortModifiers: createSortModifiers(config),\n ...createClassGroupUtils(config)\n});\nconst SPLIT_CLASSES_REGEX = /\\s+/;\nconst mergeClassList = (classList, configUtils) => {\n const {\n parseClassName,\n getClassGroupId,\n getConflictingClassGroupIds,\n sortModifiers\n } = configUtils;\n /**\n * Set of classGroupIds in following format:\n * `{importantModifier}{variantModifiers}{classGroupId}`\n * @example 'float'\n * @example 'hover:focus:bg-color'\n * @example 'md:!pr'\n */\n const classGroupsInConflict = [];\n const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);\n let result = '';\n for (let index = classNames.length - 1; index >= 0; index -= 1) {\n const originalClassName = classNames[index];\n const {\n isExternal,\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n } = parseClassName(originalClassName);\n if (isExternal) {\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n let hasPostfixModifier = !!maybePostfixModifierPosition;\n let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);\n if (!classGroupId) {\n if (!hasPostfixModifier) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n classGroupId = getClassGroupId(baseClassName);\n if (!classGroupId) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n hasPostfixModifier = false;\n }\n const variantModifier = sortModifiers(modifiers).join(':');\n const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;\n const classId = modifierId + classGroupId;\n if (classGroupsInConflict.includes(classId)) {\n // Tailwind class omitted due to conflict\n continue;\n }\n classGroupsInConflict.push(classId);\n const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);\n for (let i = 0; i < conflictGroups.length; ++i) {\n const group = conflictGroups[i];\n classGroupsInConflict.push(modifierId + group);\n }\n // Tailwind class not in conflict\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n }\n return result;\n};\n\n/**\n * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.\n *\n * Specifically:\n * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js\n * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts\n *\n * Original code has MIT license: Copyright (c) Luke Edwards (lukeed.com)\n */\nfunction twJoin() {\n let index = 0;\n let argument;\n let resolvedValue;\n let string = '';\n while (index < arguments.length) {\n if (argument = arguments[index++]) {\n if (resolvedValue = toValue(argument)) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n}\nconst toValue = mix => {\n if (typeof mix === 'string') {\n return mix;\n }\n let resolvedValue;\n let string = '';\n for (let k = 0; k < mix.length; k++) {\n if (mix[k]) {\n if (resolvedValue = toValue(mix[k])) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n};\nfunction createTailwindMerge(createConfigFirst, ...createConfigRest) {\n let configUtils;\n let cacheGet;\n let cacheSet;\n let functionToCall = initTailwindMerge;\n function initTailwindMerge(classList) {\n const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());\n configUtils = createConfigUtils(config);\n cacheGet = configUtils.cache.get;\n cacheSet = configUtils.cache.set;\n functionToCall = tailwindMerge;\n return tailwindMerge(classList);\n }\n function tailwindMerge(classList) {\n const cachedResult = cacheGet(classList);\n if (cachedResult) {\n return cachedResult;\n }\n const result = mergeClassList(classList, configUtils);\n cacheSet(classList, result);\n return result;\n }\n return function callTailwindMerge() {\n return functionToCall(twJoin.apply(null, arguments));\n };\n}\nconst fromTheme = key => {\n const themeGetter = theme => theme[key] || [];\n themeGetter.isThemeGetter = true;\n return themeGetter;\n};\nconst arbitraryValueRegex = /^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i;\nconst arbitraryVariableRegex = /^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i;\nconst fractionRegex = /^\\d+\\/\\d+$/;\nconst tshirtUnitRegex = /^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/;\nconst lengthUnitRegex = /\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/;\nconst colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/;\n// Shadow always begins with x and y offset separated by underscore optionally prepended by inset\nconst shadowRegex = /^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/;\nconst imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/;\nconst isFraction = value => fractionRegex.test(value);\nconst isNumber = value => !!value && !Number.isNaN(Number(value));\nconst isInteger = value => !!value && Number.isInteger(Number(value));\nconst isPercent = value => value.endsWith('%') && isNumber(value.slice(0, -1));\nconst isTshirtSize = value => tshirtUnitRegex.test(value);\nconst isAny = () => true;\nconst isLengthOnly = value =>\n// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.\n// For example, `hsl(0 0% 0%)` would be classified as a length without this check.\n// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.\nlengthUnitRegex.test(value) && !colorFunctionRegex.test(value);\nconst isNever = () => false;\nconst isShadow = value => shadowRegex.test(value);\nconst isImage = value => imageRegex.test(value);\nconst isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);\nconst isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);\nconst isArbitraryValue = value => arbitraryValueRegex.test(value);\nconst isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);\nconst isArbitraryNumber = value => getIsArbitraryValue(value, isLabelNumber, isNumber);\nconst isArbitraryPosition = value => getIsArbitraryValue(value, isLabelPosition, isNever);\nconst isArbitraryImage = value => getIsArbitraryValue(value, isLabelImage, isImage);\nconst isArbitraryShadow = value => getIsArbitraryValue(value, isLabelShadow, isShadow);\nconst isArbitraryVariable = value => arbitraryVariableRegex.test(value);\nconst isArbitraryVariableLength = value => getIsArbitraryVariable(value, isLabelLength);\nconst isArbitraryVariableFamilyName = value => getIsArbitraryVariable(value, isLabelFamilyName);\nconst isArbitraryVariablePosition = value => getIsArbitraryVariable(value, isLabelPosition);\nconst isArbitraryVariableSize = value => getIsArbitraryVariable(value, isLabelSize);\nconst isArbitraryVariableImage = value => getIsArbitraryVariable(value, isLabelImage);\nconst isArbitraryVariableShadow = value => getIsArbitraryVariable(value, isLabelShadow, true);\n// Helpers\nconst getIsArbitraryValue = (value, testLabel, testValue) => {\n const result = arbitraryValueRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return testValue(result[2]);\n }\n return false;\n};\nconst getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {\n const result = arbitraryVariableRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return shouldMatchNoLabel;\n }\n return false;\n};\n// Labels\nconst isLabelPosition = label => label === 'position' || label === 'percentage';\nconst isLabelImage = label => label === 'image' || label === 'url';\nconst isLabelSize = label => label === 'length' || label === 'size' || label === 'bg-size';\nconst isLabelLength = label => label === 'length';\nconst isLabelNumber = label => label === 'number';\nconst isLabelFamilyName = label => label === 'family-name';\nconst isLabelShadow = label => label === 'shadow';\nconst validators = /*#__PURE__*/Object.defineProperty({\n __proto__: null,\n isAny,\n isAnyNonArbitrary,\n isArbitraryImage,\n isArbitraryLength,\n isArbitraryNumber,\n isArbitraryPosition,\n isArbitraryShadow,\n isArbitrarySize,\n isArbitraryValue,\n isArbitraryVariable,\n isArbitraryVariableFamilyName,\n isArbitraryVariableImage,\n isArbitraryVariableLength,\n isArbitraryVariablePosition,\n isArbitraryVariableShadow,\n isArbitraryVariableSize,\n isFraction,\n isInteger,\n isNumber,\n isPercent,\n isTshirtSize\n}, Symbol.toStringTag, {\n value: 'Module'\n});\nconst getDefaultConfig = () => {\n /**\n * Theme getters for theme variable namespaces\n * @see https://tailwindcss.com/docs/theme#theme-variable-namespaces\n */\n /***/\n const themeColor = fromTheme('color');\n const themeFont = fromTheme('font');\n const themeText = fromTheme('text');\n const themeFontWeight = fromTheme('font-weight');\n const themeTracking = fromTheme('tracking');\n const themeLeading = fromTheme('leading');\n const themeBreakpoint = fromTheme('breakpoint');\n const themeContainer = fromTheme('container');\n const themeSpacing = fromTheme('spacing');\n const themeRadius = fromTheme('radius');\n const themeShadow = fromTheme('shadow');\n const themeInsetShadow = fromTheme('inset-shadow');\n const themeTextShadow = fromTheme('text-shadow');\n const themeDropShadow = fromTheme('drop-shadow');\n const themeBlur = fromTheme('blur');\n const themePerspective = fromTheme('perspective');\n const themeAspect = fromTheme('aspect');\n const themeEase = fromTheme('ease');\n const themeAnimate = fromTheme('animate');\n /**\n * Helpers to avoid repeating the same scales\n *\n * We use functions that create a new array every time they're called instead of static arrays.\n * This ensures that users who modify any scale by mutating the array (e.g. with `array.push(element)`) don't accidentally mutate arrays in other parts of the config.\n */\n /***/\n const scaleBreak = () => ['auto', 'avoid', 'all', 'avoid-page', 'page', 'left', 'right', 'column'];\n const scalePosition = () => ['center', 'top', 'bottom', 'left', 'right', 'top-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-top', 'top-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-top', 'bottom-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-bottom', 'bottom-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-bottom'];\n const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];\n const scaleOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'];\n const scaleOverscroll = () => ['auto', 'contain', 'none'];\n const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];\n const scaleInset = () => [isFraction, 'full', 'auto', ...scaleUnambiguousSpacing()];\n const scaleGridTemplateColsRows = () => [isInteger, 'none', 'subgrid', isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartAndEnd = () => ['auto', {\n span: ['full', isInteger, isArbitraryVariable, isArbitraryValue]\n }, isInteger, isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartOrEnd = () => [isInteger, 'auto', isArbitraryVariable, isArbitraryValue];\n const scaleGridAutoColsRows = () => ['auto', 'min', 'max', 'fr', isArbitraryVariable, isArbitraryValue];\n const scaleAlignPrimaryAxis = () => ['start', 'end', 'center', 'between', 'around', 'evenly', 'stretch', 'baseline', 'center-safe', 'end-safe'];\n const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];\n const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];\n const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];\n const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];\n const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {\n position: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleBgRepeat = () => ['no-repeat', {\n repeat: ['', 'x', 'y', 'space', 'round']\n }];\n const scaleBgSize = () => ['auto', 'cover', 'contain', isArbitraryVariableSize, isArbitrarySize, {\n size: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];\n const scaleRadius = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', 'full', themeRadius, isArbitraryVariable, isArbitraryValue];\n const scaleBorderWidth = () => ['', isNumber, isArbitraryVariableLength, isArbitraryLength];\n const scaleLineStyle = () => ['solid', 'dashed', 'dotted', 'double'];\n const scaleBlendMode = () => ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];\n const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];\n const scaleBlur = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeBlur, isArbitraryVariable, isArbitraryValue];\n const scaleRotate = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleScale = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleTranslate = () => [isFraction, 'full', ...scaleUnambiguousSpacing()];\n return {\n cacheSize: 500,\n theme: {\n animate: ['spin', 'ping', 'pulse', 'bounce'],\n aspect: ['video'],\n blur: [isTshirtSize],\n breakpoint: [isTshirtSize],\n color: [isAny],\n container: [isTshirtSize],\n 'drop-shadow': [isTshirtSize],\n ease: ['in', 'out', 'in-out'],\n font: [isAnyNonArbitrary],\n 'font-weight': ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black'],\n 'inset-shadow': [isTshirtSize],\n leading: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'],\n perspective: ['dramatic', 'near', 'normal', 'midrange', 'distant', 'none'],\n radius: [isTshirtSize],\n shadow: [isTshirtSize],\n spacing: ['px', isNumber],\n text: [isTshirtSize],\n 'text-shadow': [isTshirtSize],\n tracking: ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest']\n },\n classGroups: {\n // --------------\n // --- Layout ---\n // --------------\n /**\n * Aspect Ratio\n * @see https://tailwindcss.com/docs/aspect-ratio\n */\n aspect: [{\n aspect: ['auto', 'square', isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]\n }],\n /**\n * Container\n * @see https://tailwindcss.com/docs/container\n * @deprecated since Tailwind CSS v4.0.0\n */\n container: ['container'],\n /**\n * Columns\n * @see https://tailwindcss.com/docs/columns\n */\n columns: [{\n columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]\n }],\n /**\n * Break After\n * @see https://tailwindcss.com/docs/break-after\n */\n 'break-after': [{\n 'break-after': scaleBreak()\n }],\n /**\n * Break Before\n * @see https://tailwindcss.com/docs/break-before\n */\n 'break-before': [{\n 'break-before': scaleBreak()\n }],\n /**\n * Break Inside\n * @see https://tailwindcss.com/docs/break-inside\n */\n 'break-inside': [{\n 'break-inside': ['auto', 'avoid', 'avoid-page', 'avoid-column']\n }],\n /**\n * Box Decoration Break\n * @see https://tailwindcss.com/docs/box-decoration-break\n */\n 'box-decoration': [{\n 'box-decoration': ['slice', 'clone']\n }],\n /**\n * Box Sizing\n * @see https://tailwindcss.com/docs/box-sizing\n */\n box: [{\n box: ['border', 'content']\n }],\n /**\n * Display\n * @see https://tailwindcss.com/docs/display\n */\n display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],\n /**\n * Screen Reader Only\n * @see https://tailwindcss.com/docs/display#screen-reader-only\n */\n sr: ['sr-only', 'not-sr-only'],\n /**\n * Floats\n * @see https://tailwindcss.com/docs/float\n */\n float: [{\n float: ['right', 'left', 'none', 'start', 'end']\n }],\n /**\n * Clear\n * @see https://tailwindcss.com/docs/clear\n */\n clear: [{\n clear: ['left', 'right', 'both', 'none', 'start', 'end']\n }],\n /**\n * Isolation\n * @see https://tailwindcss.com/docs/isolation\n */\n isolation: ['isolate', 'isolation-auto'],\n /**\n * Object Fit\n * @see https://tailwindcss.com/docs/object-fit\n */\n 'object-fit': [{\n object: ['contain', 'cover', 'fill', 'none', 'scale-down']\n }],\n /**\n * Object Position\n * @see https://tailwindcss.com/docs/object-position\n */\n 'object-position': [{\n object: scalePositionWithArbitrary()\n }],\n /**\n * Overflow\n * @see https://tailwindcss.com/docs/overflow\n */\n overflow: [{\n overflow: scaleOverflow()\n }],\n /**\n * Overflow X\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-x': [{\n 'overflow-x': scaleOverflow()\n }],\n /**\n * Overflow Y\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-y': [{\n 'overflow-y': scaleOverflow()\n }],\n /**\n * Overscroll Behavior\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n overscroll: [{\n overscroll: scaleOverscroll()\n }],\n /**\n * Overscroll Behavior X\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-x': [{\n 'overscroll-x': scaleOverscroll()\n }],\n /**\n * Overscroll Behavior Y\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-y': [{\n 'overscroll-y': scaleOverscroll()\n }],\n /**\n * Position\n * @see https://tailwindcss.com/docs/position\n */\n position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],\n /**\n * Top / Right / Bottom / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n inset: [{\n inset: scaleInset()\n }],\n /**\n * Right / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-x': [{\n 'inset-x': scaleInset()\n }],\n /**\n * Top / Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-y': [{\n 'inset-y': scaleInset()\n }],\n /**\n * Start\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n start: [{\n start: scaleInset()\n }],\n /**\n * End\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n end: [{\n end: scaleInset()\n }],\n /**\n * Top\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n top: [{\n top: scaleInset()\n }],\n /**\n * Right\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n right: [{\n right: scaleInset()\n }],\n /**\n * Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n bottom: [{\n bottom: scaleInset()\n }],\n /**\n * Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n left: [{\n left: scaleInset()\n }],\n /**\n * Visibility\n * @see https://tailwindcss.com/docs/visibility\n */\n visibility: ['visible', 'invisible', 'collapse'],\n /**\n * Z-Index\n * @see https://tailwindcss.com/docs/z-index\n */\n z: [{\n z: [isInteger, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------------\n // --- Flexbox and Grid ---\n // ------------------------\n /**\n * Flex Basis\n * @see https://tailwindcss.com/docs/flex-basis\n */\n basis: [{\n basis: [isFraction, 'full', 'auto', themeContainer, ...scaleUnambiguousSpacing()]\n }],\n /**\n * Flex Direction\n * @see https://tailwindcss.com/docs/flex-direction\n */\n 'flex-direction': [{\n flex: ['row', 'row-reverse', 'col', 'col-reverse']\n }],\n /**\n * Flex Wrap\n * @see https://tailwindcss.com/docs/flex-wrap\n */\n 'flex-wrap': [{\n flex: ['nowrap', 'wrap', 'wrap-reverse']\n }],\n /**\n * Flex\n * @see https://tailwindcss.com/docs/flex\n */\n flex: [{\n flex: [isNumber, isFraction, 'auto', 'initial', 'none', isArbitraryValue]\n }],\n /**\n * Flex Grow\n * @see https://tailwindcss.com/docs/flex-grow\n */\n grow: [{\n grow: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Flex Shrink\n * @see https://tailwindcss.com/docs/flex-shrink\n */\n shrink: [{\n shrink: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Order\n * @see https://tailwindcss.com/docs/order\n */\n order: [{\n order: [isInteger, 'first', 'last', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Grid Template Columns\n * @see https://tailwindcss.com/docs/grid-template-columns\n */\n 'grid-cols': [{\n 'grid-cols': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Column Start / End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start-end': [{\n col: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Column Start\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start': [{\n 'col-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Column End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-end': [{\n 'col-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Template Rows\n * @see https://tailwindcss.com/docs/grid-template-rows\n */\n 'grid-rows': [{\n 'grid-rows': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Row Start / End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start-end': [{\n row: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Row Start\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start': [{\n 'row-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Row End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-end': [{\n 'row-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Auto Flow\n * @see https://tailwindcss.com/docs/grid-auto-flow\n */\n 'grid-flow': [{\n 'grid-flow': ['row', 'col', 'dense', 'row-dense', 'col-dense']\n }],\n /**\n * Grid Auto Columns\n * @see https://tailwindcss.com/docs/grid-auto-columns\n */\n 'auto-cols': [{\n 'auto-cols': scaleGridAutoColsRows()\n }],\n /**\n * Grid Auto Rows\n * @see https://tailwindcss.com/docs/grid-auto-rows\n */\n 'auto-rows': [{\n 'auto-rows': scaleGridAutoColsRows()\n }],\n /**\n * Gap\n * @see https://tailwindcss.com/docs/gap\n */\n gap: [{\n gap: scaleUnambiguousSpacing()\n }],\n /**\n * Gap X\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-x': [{\n 'gap-x': scaleUnambiguousSpacing()\n }],\n /**\n * Gap Y\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-y': [{\n 'gap-y': scaleUnambiguousSpacing()\n }],\n /**\n * Justify Content\n * @see https://tailwindcss.com/docs/justify-content\n */\n 'justify-content': [{\n justify: [...scaleAlignPrimaryAxis(), 'normal']\n }],\n /**\n * Justify Items\n * @see https://tailwindcss.com/docs/justify-items\n */\n 'justify-items': [{\n 'justify-items': [...scaleAlignSecondaryAxis(), 'normal']\n }],\n /**\n * Justify Self\n * @see https://tailwindcss.com/docs/justify-self\n */\n 'justify-self': [{\n 'justify-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n /**\n * Align Content\n * @see https://tailwindcss.com/docs/align-content\n */\n 'align-content': [{\n content: ['normal', ...scaleAlignPrimaryAxis()]\n }],\n /**\n * Align Items\n * @see https://tailwindcss.com/docs/align-items\n */\n 'align-items': [{\n items: [...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Align Self\n * @see https://tailwindcss.com/docs/align-self\n */\n 'align-self': [{\n self: ['auto', ...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Place Content\n * @see https://tailwindcss.com/docs/place-content\n */\n 'place-content': [{\n 'place-content': scaleAlignPrimaryAxis()\n }],\n /**\n * Place Items\n * @see https://tailwindcss.com/docs/place-items\n */\n 'place-items': [{\n 'place-items': [...scaleAlignSecondaryAxis(), 'baseline']\n }],\n /**\n * Place Self\n * @see https://tailwindcss.com/docs/place-self\n */\n 'place-self': [{\n 'place-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n // Spacing\n /**\n * Padding\n * @see https://tailwindcss.com/docs/padding\n */\n p: [{\n p: scaleUnambiguousSpacing()\n }],\n /**\n * Padding X\n * @see https://tailwindcss.com/docs/padding\n */\n px: [{\n px: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Y\n * @see https://tailwindcss.com/docs/padding\n */\n py: [{\n py: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Start\n * @see https://tailwindcss.com/docs/padding\n */\n ps: [{\n ps: scaleUnambiguousSpacing()\n }],\n /**\n * Padding End\n * @see https://tailwindcss.com/docs/padding\n */\n pe: [{\n pe: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Top\n * @see https://tailwindcss.com/docs/padding\n */\n pt: [{\n pt: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Right\n * @see https://tailwindcss.com/docs/padding\n */\n pr: [{\n pr: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Bottom\n * @see https://tailwindcss.com/docs/padding\n */\n pb: [{\n pb: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Left\n * @see https://tailwindcss.com/docs/padding\n */\n pl: [{\n pl: scaleUnambiguousSpacing()\n }],\n /**\n * Margin\n * @see https://tailwindcss.com/docs/margin\n */\n m: [{\n m: scaleMargin()\n }],\n /**\n * Margin X\n * @see https://tailwindcss.com/docs/margin\n */\n mx: [{\n mx: scaleMargin()\n }],\n /**\n * Margin Y\n * @see https://tailwindcss.com/docs/margin\n */\n my: [{\n my: scaleMargin()\n }],\n /**\n * Margin Start\n * @see https://tailwindcss.com/docs/margin\n */\n ms: [{\n ms: scaleMargin()\n }],\n /**\n * Margin End\n * @see https://tailwindcss.com/docs/margin\n */\n me: [{\n me: scaleMargin()\n }],\n /**\n * Margin Top\n * @see https://tailwindcss.com/docs/margin\n */\n mt: [{\n mt: scaleMargin()\n }],\n /**\n * Margin Right\n * @see https://tailwindcss.com/docs/margin\n */\n mr: [{\n mr: scaleMargin()\n }],\n /**\n * Margin Bottom\n * @see https://tailwindcss.com/docs/margin\n */\n mb: [{\n mb: scaleMargin()\n }],\n /**\n * Margin Left\n * @see https://tailwindcss.com/docs/margin\n */\n ml: [{\n ml: scaleMargin()\n }],\n /**\n * Space Between X\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x': [{\n 'space-x': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between X Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x-reverse': ['space-x-reverse'],\n /**\n * Space Between Y\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y': [{\n 'space-y': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between Y Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y-reverse': ['space-y-reverse'],\n // --------------\n // --- Sizing ---\n // --------------\n /**\n * Size\n * @see https://tailwindcss.com/docs/width#setting-both-width-and-height\n */\n size: [{\n size: scaleSizing()\n }],\n /**\n * Width\n * @see https://tailwindcss.com/docs/width\n */\n w: [{\n w: [themeContainer, 'screen', ...scaleSizing()]\n }],\n /**\n * Min-Width\n * @see https://tailwindcss.com/docs/min-width\n */\n 'min-w': [{\n 'min-w': [themeContainer, 'screen', /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'none', ...scaleSizing()]\n }],\n /**\n * Max-Width\n * @see https://tailwindcss.com/docs/max-width\n */\n 'max-w': [{\n 'max-w': [themeContainer, 'screen', 'none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'prose', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n {\n screen: [themeBreakpoint]\n }, ...scaleSizing()]\n }],\n /**\n * Height\n * @see https://tailwindcss.com/docs/height\n */\n h: [{\n h: ['screen', 'lh', ...scaleSizing()]\n }],\n /**\n * Min-Height\n * @see https://tailwindcss.com/docs/min-height\n */\n 'min-h': [{\n 'min-h': ['screen', 'lh', 'none', ...scaleSizing()]\n }],\n /**\n * Max-Height\n * @see https://tailwindcss.com/docs/max-height\n */\n 'max-h': [{\n 'max-h': ['screen', 'lh', ...scaleSizing()]\n }],\n // ------------------\n // --- Typography ---\n // ------------------\n /**\n * Font Size\n * @see https://tailwindcss.com/docs/font-size\n */\n 'font-size': [{\n text: ['base', themeText, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Font Smoothing\n * @see https://tailwindcss.com/docs/font-smoothing\n */\n 'font-smoothing': ['antialiased', 'subpixel-antialiased'],\n /**\n * Font Style\n * @see https://tailwindcss.com/docs/font-style\n */\n 'font-style': ['italic', 'not-italic'],\n /**\n * Font Weight\n * @see https://tailwindcss.com/docs/font-weight\n */\n 'font-weight': [{\n font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Font Stretch\n * @see https://tailwindcss.com/docs/font-stretch\n */\n 'font-stretch': [{\n 'font-stretch': ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', isPercent, isArbitraryValue]\n }],\n /**\n * Font Family\n * @see https://tailwindcss.com/docs/font-family\n */\n 'font-family': [{\n font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont]\n }],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-normal': ['normal-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-ordinal': ['ordinal'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-slashed-zero': ['slashed-zero'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-figure': ['lining-nums', 'oldstyle-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-spacing': ['proportional-nums', 'tabular-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-fraction': ['diagonal-fractions', 'stacked-fractions'],\n /**\n * Letter Spacing\n * @see https://tailwindcss.com/docs/letter-spacing\n */\n tracking: [{\n tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Line Clamp\n * @see https://tailwindcss.com/docs/line-clamp\n */\n 'line-clamp': [{\n 'line-clamp': [isNumber, 'none', isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Line Height\n * @see https://tailwindcss.com/docs/line-height\n */\n leading: [{\n leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n themeLeading, ...scaleUnambiguousSpacing()]\n }],\n /**\n * List Style Image\n * @see https://tailwindcss.com/docs/list-style-image\n */\n 'list-image': [{\n 'list-image': ['none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * List Style Position\n * @see https://tailwindcss.com/docs/list-style-position\n */\n 'list-style-position': [{\n list: ['inside', 'outside']\n }],\n /**\n * List Style Type\n * @see https://tailwindcss.com/docs/list-style-type\n */\n 'list-style-type': [{\n list: ['disc', 'decimal', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Alignment\n * @see https://tailwindcss.com/docs/text-align\n */\n 'text-alignment': [{\n text: ['left', 'center', 'right', 'justify', 'start', 'end']\n }],\n /**\n * Placeholder Color\n * @deprecated since Tailwind CSS v3.0.0\n * @see https://v3.tailwindcss.com/docs/placeholder-color\n */\n 'placeholder-color': [{\n placeholder: scaleColor()\n }],\n /**\n * Text Color\n * @see https://tailwindcss.com/docs/text-color\n */\n 'text-color': [{\n text: scaleColor()\n }],\n /**\n * Text Decoration\n * @see https://tailwindcss.com/docs/text-decoration\n */\n 'text-decoration': ['underline', 'overline', 'line-through', 'no-underline'],\n /**\n * Text Decoration Style\n * @see https://tailwindcss.com/docs/text-decoration-style\n */\n 'text-decoration-style': [{\n decoration: [...scaleLineStyle(), 'wavy']\n }],\n /**\n * Text Decoration Thickness\n * @see https://tailwindcss.com/docs/text-decoration-thickness\n */\n 'text-decoration-thickness': [{\n decoration: [isNumber, 'from-font', 'auto', isArbitraryVariable, isArbitraryLength]\n }],\n /**\n * Text Decoration Color\n * @see https://tailwindcss.com/docs/text-decoration-color\n */\n 'text-decoration-color': [{\n decoration: scaleColor()\n }],\n /**\n * Text Underline Offset\n * @see https://tailwindcss.com/docs/text-underline-offset\n */\n 'underline-offset': [{\n 'underline-offset': [isNumber, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Transform\n * @see https://tailwindcss.com/docs/text-transform\n */\n 'text-transform': ['uppercase', 'lowercase', 'capitalize', 'normal-case'],\n /**\n * Text Overflow\n * @see https://tailwindcss.com/docs/text-overflow\n */\n 'text-overflow': ['truncate', 'text-ellipsis', 'text-clip'],\n /**\n * Text Wrap\n * @see https://tailwindcss.com/docs/text-wrap\n */\n 'text-wrap': [{\n text: ['wrap', 'nowrap', 'balance', 'pretty']\n }],\n /**\n * Text Indent\n * @see https://tailwindcss.com/docs/text-indent\n */\n indent: [{\n indent: scaleUnambiguousSpacing()\n }],\n /**\n * Vertical Alignment\n * @see https://tailwindcss.com/docs/vertical-align\n */\n 'vertical-align': [{\n align: ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Whitespace\n * @see https://tailwindcss.com/docs/whitespace\n */\n whitespace: [{\n whitespace: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces']\n }],\n /**\n * Word Break\n * @see https://tailwindcss.com/docs/word-break\n */\n break: [{\n break: ['normal', 'words', 'all', 'keep']\n }],\n /**\n * Overflow Wrap\n * @see https://tailwindcss.com/docs/overflow-wrap\n */\n wrap: [{\n wrap: ['break-word', 'anywhere', 'normal']\n }],\n /**\n * Hyphens\n * @see https://tailwindcss.com/docs/hyphens\n */\n hyphens: [{\n hyphens: ['none', 'manual', 'auto']\n }],\n /**\n * Content\n * @see https://tailwindcss.com/docs/content\n */\n content: [{\n content: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // -------------------\n // --- Backgrounds ---\n // -------------------\n /**\n * Background Attachment\n * @see https://tailwindcss.com/docs/background-attachment\n */\n 'bg-attachment': [{\n bg: ['fixed', 'local', 'scroll']\n }],\n /**\n * Background Clip\n * @see https://tailwindcss.com/docs/background-clip\n */\n 'bg-clip': [{\n 'bg-clip': ['border', 'padding', 'content', 'text']\n }],\n /**\n * Background Origin\n * @see https://tailwindcss.com/docs/background-origin\n */\n 'bg-origin': [{\n 'bg-origin': ['border', 'padding', 'content']\n }],\n /**\n * Background Position\n * @see https://tailwindcss.com/docs/background-position\n */\n 'bg-position': [{\n bg: scaleBgPosition()\n }],\n /**\n * Background Repeat\n * @see https://tailwindcss.com/docs/background-repeat\n */\n 'bg-repeat': [{\n bg: scaleBgRepeat()\n }],\n /**\n * Background Size\n * @see https://tailwindcss.com/docs/background-size\n */\n 'bg-size': [{\n bg: scaleBgSize()\n }],\n /**\n * Background Image\n * @see https://tailwindcss.com/docs/background-image\n */\n 'bg-image': [{\n bg: ['none', {\n linear: [{\n to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']\n }, isInteger, isArbitraryVariable, isArbitraryValue],\n radial: ['', isArbitraryVariable, isArbitraryValue],\n conic: [isInteger, isArbitraryVariable, isArbitraryValue]\n }, isArbitraryVariableImage, isArbitraryImage]\n }],\n /**\n * Background Color\n * @see https://tailwindcss.com/docs/background-color\n */\n 'bg-color': [{\n bg: scaleColor()\n }],\n /**\n * Gradient Color Stops From Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from-pos': [{\n from: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops Via Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via-pos': [{\n via: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops To Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to-pos': [{\n to: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops From\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from': [{\n from: scaleColor()\n }],\n /**\n * Gradient Color Stops Via\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via': [{\n via: scaleColor()\n }],\n /**\n * Gradient Color Stops To\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to': [{\n to: scaleColor()\n }],\n // ---------------\n // --- Borders ---\n // ---------------\n /**\n * Border Radius\n * @see https://tailwindcss.com/docs/border-radius\n */\n rounded: [{\n rounded: scaleRadius()\n }],\n /**\n * Border Radius Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-s': [{\n 'rounded-s': scaleRadius()\n }],\n /**\n * Border Radius End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-e': [{\n 'rounded-e': scaleRadius()\n }],\n /**\n * Border Radius Top\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-t': [{\n 'rounded-t': scaleRadius()\n }],\n /**\n * Border Radius Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-r': [{\n 'rounded-r': scaleRadius()\n }],\n /**\n * Border Radius Bottom\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-b': [{\n 'rounded-b': scaleRadius()\n }],\n /**\n * Border Radius Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-l': [{\n 'rounded-l': scaleRadius()\n }],\n /**\n * Border Radius Start Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ss': [{\n 'rounded-ss': scaleRadius()\n }],\n /**\n * Border Radius Start End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-se': [{\n 'rounded-se': scaleRadius()\n }],\n /**\n * Border Radius End End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ee': [{\n 'rounded-ee': scaleRadius()\n }],\n /**\n * Border Radius End Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-es': [{\n 'rounded-es': scaleRadius()\n }],\n /**\n * Border Radius Top Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tl': [{\n 'rounded-tl': scaleRadius()\n }],\n /**\n * Border Radius Top Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tr': [{\n 'rounded-tr': scaleRadius()\n }],\n /**\n * Border Radius Bottom Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-br': [{\n 'rounded-br': scaleRadius()\n }],\n /**\n * Border Radius Bottom Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-bl': [{\n 'rounded-bl': scaleRadius()\n }],\n /**\n * Border Width\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w': [{\n border: scaleBorderWidth()\n }],\n /**\n * Border Width X\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-x': [{\n 'border-x': scaleBorderWidth()\n }],\n /**\n * Border Width Y\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-y': [{\n 'border-y': scaleBorderWidth()\n }],\n /**\n * Border Width Start\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-s': [{\n 'border-s': scaleBorderWidth()\n }],\n /**\n * Border Width End\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-e': [{\n 'border-e': scaleBorderWidth()\n }],\n /**\n * Border Width Top\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-t': [{\n 'border-t': scaleBorderWidth()\n }],\n /**\n * Border Width Right\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-r': [{\n 'border-r': scaleBorderWidth()\n }],\n /**\n * Border Width Bottom\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-b': [{\n 'border-b': scaleBorderWidth()\n }],\n /**\n * Border Width Left\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-l': [{\n 'border-l': scaleBorderWidth()\n }],\n /**\n * Divide Width X\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x': [{\n 'divide-x': scaleBorderWidth()\n }],\n /**\n * Divide Width X Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x-reverse': ['divide-x-reverse'],\n /**\n * Divide Width Y\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y': [{\n 'divide-y': scaleBorderWidth()\n }],\n /**\n * Divide Width Y Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y-reverse': ['divide-y-reverse'],\n /**\n * Border Style\n * @see https://tailwindcss.com/docs/border-style\n */\n 'border-style': [{\n border: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Divide Style\n * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style\n */\n 'divide-style': [{\n divide: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Border Color\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color': [{\n border: scaleColor()\n }],\n /**\n * Border Color X\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-x': [{\n 'border-x': scaleColor()\n }],\n /**\n * Border Color Y\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-y': [{\n 'border-y': scaleColor()\n }],\n /**\n * Border Color S\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-s': [{\n 'border-s': scaleColor()\n }],\n /**\n * Border Color E\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-e': [{\n 'border-e': scaleColor()\n }],\n /**\n * Border Color Top\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-t': [{\n 'border-t': scaleColor()\n }],\n /**\n * Border Color Right\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-r': [{\n 'border-r': scaleColor()\n }],\n /**\n * Border Color Bottom\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-b': [{\n 'border-b': scaleColor()\n }],\n /**\n * Border Color Left\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-l': [{\n 'border-l': scaleColor()\n }],\n /**\n * Divide Color\n * @see https://tailwindcss.com/docs/divide-color\n */\n 'divide-color': [{\n divide: scaleColor()\n }],\n /**\n * Outline Style\n * @see https://tailwindcss.com/docs/outline-style\n */\n 'outline-style': [{\n outline: [...scaleLineStyle(), 'none', 'hidden']\n }],\n /**\n * Outline Offset\n * @see https://tailwindcss.com/docs/outline-offset\n */\n 'outline-offset': [{\n 'outline-offset': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Outline Width\n * @see https://tailwindcss.com/docs/outline-width\n */\n 'outline-w': [{\n outline: ['', isNumber, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Outline Color\n * @see https://tailwindcss.com/docs/outline-color\n */\n 'outline-color': [{\n outline: scaleColor()\n }],\n // ---------------\n // --- Effects ---\n // ---------------\n /**\n * Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow\n */\n shadow: [{\n shadow: [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color\n */\n 'shadow-color': [{\n shadow: scaleColor()\n }],\n /**\n * Inset Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow\n */\n 'inset-shadow': [{\n 'inset-shadow': ['none', themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Inset Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color\n */\n 'inset-shadow-color': [{\n 'inset-shadow': scaleColor()\n }],\n /**\n * Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring\n */\n 'ring-w': [{\n ring: scaleBorderWidth()\n }],\n /**\n * Ring Width Inset\n * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-w-inset': ['ring-inset'],\n /**\n * Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color\n */\n 'ring-color': [{\n ring: scaleColor()\n }],\n /**\n * Ring Offset Width\n * @see https://v3.tailwindcss.com/docs/ring-offset-width\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-w': [{\n 'ring-offset': [isNumber, isArbitraryLength]\n }],\n /**\n * Ring Offset Color\n * @see https://v3.tailwindcss.com/docs/ring-offset-color\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-color': [{\n 'ring-offset': scaleColor()\n }],\n /**\n * Inset Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring\n */\n 'inset-ring-w': [{\n 'inset-ring': scaleBorderWidth()\n }],\n /**\n * Inset Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color\n */\n 'inset-ring-color': [{\n 'inset-ring': scaleColor()\n }],\n /**\n * Text Shadow\n * @see https://tailwindcss.com/docs/text-shadow\n */\n 'text-shadow': [{\n 'text-shadow': ['none', themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Text Shadow Color\n * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color\n */\n 'text-shadow-color': [{\n 'text-shadow': scaleColor()\n }],\n /**\n * Opacity\n * @see https://tailwindcss.com/docs/opacity\n */\n opacity: [{\n opacity: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Mix Blend Mode\n * @see https://tailwindcss.com/docs/mix-blend-mode\n */\n 'mix-blend': [{\n 'mix-blend': [...scaleBlendMode(), 'plus-darker', 'plus-lighter']\n }],\n /**\n * Background Blend Mode\n * @see https://tailwindcss.com/docs/background-blend-mode\n */\n 'bg-blend': [{\n 'bg-blend': scaleBlendMode()\n }],\n /**\n * Mask Clip\n * @see https://tailwindcss.com/docs/mask-clip\n */\n 'mask-clip': [{\n 'mask-clip': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }, 'mask-no-clip'],\n /**\n * Mask Composite\n * @see https://tailwindcss.com/docs/mask-composite\n */\n 'mask-composite': [{\n mask: ['add', 'subtract', 'intersect', 'exclude']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image-linear-pos': [{\n 'mask-linear': [isNumber]\n }],\n 'mask-image-linear-from-pos': [{\n 'mask-linear-from': scaleMaskImagePosition()\n }],\n 'mask-image-linear-to-pos': [{\n 'mask-linear-to': scaleMaskImagePosition()\n }],\n 'mask-image-linear-from-color': [{\n 'mask-linear-from': scaleColor()\n }],\n 'mask-image-linear-to-color': [{\n 'mask-linear-to': scaleColor()\n }],\n 'mask-image-t-from-pos': [{\n 'mask-t-from': scaleMaskImagePosition()\n }],\n 'mask-image-t-to-pos': [{\n 'mask-t-to': scaleMaskImagePosition()\n }],\n 'mask-image-t-from-color': [{\n 'mask-t-from': scaleColor()\n }],\n 'mask-image-t-to-color': [{\n 'mask-t-to': scaleColor()\n }],\n 'mask-image-r-from-pos': [{\n 'mask-r-from': scaleMaskImagePosition()\n }],\n 'mask-image-r-to-pos': [{\n 'mask-r-to': scaleMaskImagePosition()\n }],\n 'mask-image-r-from-color': [{\n 'mask-r-from': scaleColor()\n }],\n 'mask-image-r-to-color': [{\n 'mask-r-to': scaleColor()\n }],\n 'mask-image-b-from-pos': [{\n 'mask-b-from': scaleMaskImagePosition()\n }],\n 'mask-image-b-to-pos': [{\n 'mask-b-to': scaleMaskImagePosition()\n }],\n 'mask-image-b-from-color': [{\n 'mask-b-from': scaleColor()\n }],\n 'mask-image-b-to-color': [{\n 'mask-b-to': scaleColor()\n }],\n 'mask-image-l-from-pos': [{\n 'mask-l-from': scaleMaskImagePosition()\n }],\n 'mask-image-l-to-pos': [{\n 'mask-l-to': scaleMaskImagePosition()\n }],\n 'mask-image-l-from-color': [{\n 'mask-l-from': scaleColor()\n }],\n 'mask-image-l-to-color': [{\n 'mask-l-to': scaleColor()\n }],\n 'mask-image-x-from-pos': [{\n 'mask-x-from': scaleMaskImagePosition()\n }],\n 'mask-image-x-to-pos': [{\n 'mask-x-to': scaleMaskImagePosition()\n }],\n 'mask-image-x-from-color': [{\n 'mask-x-from': scaleColor()\n }],\n 'mask-image-x-to-color': [{\n 'mask-x-to': scaleColor()\n }],\n 'mask-image-y-from-pos': [{\n 'mask-y-from': scaleMaskImagePosition()\n }],\n 'mask-image-y-to-pos': [{\n 'mask-y-to': scaleMaskImagePosition()\n }],\n 'mask-image-y-from-color': [{\n 'mask-y-from': scaleColor()\n }],\n 'mask-image-y-to-color': [{\n 'mask-y-to': scaleColor()\n }],\n 'mask-image-radial': [{\n 'mask-radial': [isArbitraryVariable, isArbitraryValue]\n }],\n 'mask-image-radial-from-pos': [{\n 'mask-radial-from': scaleMaskImagePosition()\n }],\n 'mask-image-radial-to-pos': [{\n 'mask-radial-to': scaleMaskImagePosition()\n }],\n 'mask-image-radial-from-color': [{\n 'mask-radial-from': scaleColor()\n }],\n 'mask-image-radial-to-color': [{\n 'mask-radial-to': scaleColor()\n }],\n 'mask-image-radial-shape': [{\n 'mask-radial': ['circle', 'ellipse']\n }],\n 'mask-image-radial-size': [{\n 'mask-radial': [{\n closest: ['side', 'corner'],\n farthest: ['side', 'corner']\n }]\n }],\n 'mask-image-radial-pos': [{\n 'mask-radial-at': scalePosition()\n }],\n 'mask-image-conic-pos': [{\n 'mask-conic': [isNumber]\n }],\n 'mask-image-conic-from-pos': [{\n 'mask-conic-from': scaleMaskImagePosition()\n }],\n 'mask-image-conic-to-pos': [{\n 'mask-conic-to': scaleMaskImagePosition()\n }],\n 'mask-image-conic-from-color': [{\n 'mask-conic-from': scaleColor()\n }],\n 'mask-image-conic-to-color': [{\n 'mask-conic-to': scaleColor()\n }],\n /**\n * Mask Mode\n * @see https://tailwindcss.com/docs/mask-mode\n */\n 'mask-mode': [{\n mask: ['alpha', 'luminance', 'match']\n }],\n /**\n * Mask Origin\n * @see https://tailwindcss.com/docs/mask-origin\n */\n 'mask-origin': [{\n 'mask-origin': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }],\n /**\n * Mask Position\n * @see https://tailwindcss.com/docs/mask-position\n */\n 'mask-position': [{\n mask: scaleBgPosition()\n }],\n /**\n * Mask Repeat\n * @see https://tailwindcss.com/docs/mask-repeat\n */\n 'mask-repeat': [{\n mask: scaleBgRepeat()\n }],\n /**\n * Mask Size\n * @see https://tailwindcss.com/docs/mask-size\n */\n 'mask-size': [{\n mask: scaleBgSize()\n }],\n /**\n * Mask Type\n * @see https://tailwindcss.com/docs/mask-type\n */\n 'mask-type': [{\n 'mask-type': ['alpha', 'luminance']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image': [{\n mask: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // ---------------\n // --- Filters ---\n // ---------------\n /**\n * Filter\n * @see https://tailwindcss.com/docs/filter\n */\n filter: [{\n filter: [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Blur\n * @see https://tailwindcss.com/docs/blur\n */\n blur: [{\n blur: scaleBlur()\n }],\n /**\n * Brightness\n * @see https://tailwindcss.com/docs/brightness\n */\n brightness: [{\n brightness: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Contrast\n * @see https://tailwindcss.com/docs/contrast\n */\n contrast: [{\n contrast: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Drop Shadow\n * @see https://tailwindcss.com/docs/drop-shadow\n */\n 'drop-shadow': [{\n 'drop-shadow': [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeDropShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Drop Shadow Color\n * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color\n */\n 'drop-shadow-color': [{\n 'drop-shadow': scaleColor()\n }],\n /**\n * Grayscale\n * @see https://tailwindcss.com/docs/grayscale\n */\n grayscale: [{\n grayscale: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Hue Rotate\n * @see https://tailwindcss.com/docs/hue-rotate\n */\n 'hue-rotate': [{\n 'hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Invert\n * @see https://tailwindcss.com/docs/invert\n */\n invert: [{\n invert: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Saturate\n * @see https://tailwindcss.com/docs/saturate\n */\n saturate: [{\n saturate: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Sepia\n * @see https://tailwindcss.com/docs/sepia\n */\n sepia: [{\n sepia: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Filter\n * @see https://tailwindcss.com/docs/backdrop-filter\n */\n 'backdrop-filter': [{\n 'backdrop-filter': [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Blur\n * @see https://tailwindcss.com/docs/backdrop-blur\n */\n 'backdrop-blur': [{\n 'backdrop-blur': scaleBlur()\n }],\n /**\n * Backdrop Brightness\n * @see https://tailwindcss.com/docs/backdrop-brightness\n */\n 'backdrop-brightness': [{\n 'backdrop-brightness': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Contrast\n * @see https://tailwindcss.com/docs/backdrop-contrast\n */\n 'backdrop-contrast': [{\n 'backdrop-contrast': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Grayscale\n * @see https://tailwindcss.com/docs/backdrop-grayscale\n */\n 'backdrop-grayscale': [{\n 'backdrop-grayscale': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Hue Rotate\n * @see https://tailwindcss.com/docs/backdrop-hue-rotate\n */\n 'backdrop-hue-rotate': [{\n 'backdrop-hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Invert\n * @see https://tailwindcss.com/docs/backdrop-invert\n */\n 'backdrop-invert': [{\n 'backdrop-invert': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Opacity\n * @see https://tailwindcss.com/docs/backdrop-opacity\n */\n 'backdrop-opacity': [{\n 'backdrop-opacity': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Saturate\n * @see https://tailwindcss.com/docs/backdrop-saturate\n */\n 'backdrop-saturate': [{\n 'backdrop-saturate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Sepia\n * @see https://tailwindcss.com/docs/backdrop-sepia\n */\n 'backdrop-sepia': [{\n 'backdrop-sepia': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n // --------------\n // --- Tables ---\n // --------------\n /**\n * Border Collapse\n * @see https://tailwindcss.com/docs/border-collapse\n */\n 'border-collapse': [{\n border: ['collapse', 'separate']\n }],\n /**\n * Border Spacing\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing': [{\n 'border-spacing': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing X\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-x': [{\n 'border-spacing-x': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing Y\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-y': [{\n 'border-spacing-y': scaleUnambiguousSpacing()\n }],\n /**\n * Table Layout\n * @see https://tailwindcss.com/docs/table-layout\n */\n 'table-layout': [{\n table: ['auto', 'fixed']\n }],\n /**\n * Caption Side\n * @see https://tailwindcss.com/docs/caption-side\n */\n caption: [{\n caption: ['top', 'bottom']\n }],\n // ---------------------------------\n // --- Transitions and Animation ---\n // ---------------------------------\n /**\n * Transition Property\n * @see https://tailwindcss.com/docs/transition-property\n */\n transition: [{\n transition: ['', 'all', 'colors', 'opacity', 'shadow', 'transform', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Behavior\n * @see https://tailwindcss.com/docs/transition-behavior\n */\n 'transition-behavior': [{\n transition: ['normal', 'discrete']\n }],\n /**\n * Transition Duration\n * @see https://tailwindcss.com/docs/transition-duration\n */\n duration: [{\n duration: [isNumber, 'initial', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Timing Function\n * @see https://tailwindcss.com/docs/transition-timing-function\n */\n ease: [{\n ease: ['linear', 'initial', themeEase, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Delay\n * @see https://tailwindcss.com/docs/transition-delay\n */\n delay: [{\n delay: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Animation\n * @see https://tailwindcss.com/docs/animation\n */\n animate: [{\n animate: ['none', themeAnimate, isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------\n // --- Transforms ---\n // ------------------\n /**\n * Backface Visibility\n * @see https://tailwindcss.com/docs/backface-visibility\n */\n backface: [{\n backface: ['hidden', 'visible']\n }],\n /**\n * Perspective\n * @see https://tailwindcss.com/docs/perspective\n */\n perspective: [{\n perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Perspective Origin\n * @see https://tailwindcss.com/docs/perspective-origin\n */\n 'perspective-origin': [{\n 'perspective-origin': scalePositionWithArbitrary()\n }],\n /**\n * Rotate\n * @see https://tailwindcss.com/docs/rotate\n */\n rotate: [{\n rotate: scaleRotate()\n }],\n /**\n * Rotate X\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-x': [{\n 'rotate-x': scaleRotate()\n }],\n /**\n * Rotate Y\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-y': [{\n 'rotate-y': scaleRotate()\n }],\n /**\n * Rotate Z\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-z': [{\n 'rotate-z': scaleRotate()\n }],\n /**\n * Scale\n * @see https://tailwindcss.com/docs/scale\n */\n scale: [{\n scale: scaleScale()\n }],\n /**\n * Scale X\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-x': [{\n 'scale-x': scaleScale()\n }],\n /**\n * Scale Y\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-y': [{\n 'scale-y': scaleScale()\n }],\n /**\n * Scale Z\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-z': [{\n 'scale-z': scaleScale()\n }],\n /**\n * Scale 3D\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-3d': ['scale-3d'],\n /**\n * Skew\n * @see https://tailwindcss.com/docs/skew\n */\n skew: [{\n skew: scaleSkew()\n }],\n /**\n * Skew X\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-x': [{\n 'skew-x': scaleSkew()\n }],\n /**\n * Skew Y\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-y': [{\n 'skew-y': scaleSkew()\n }],\n /**\n * Transform\n * @see https://tailwindcss.com/docs/transform\n */\n transform: [{\n transform: [isArbitraryVariable, isArbitraryValue, '', 'none', 'gpu', 'cpu']\n }],\n /**\n * Transform Origin\n * @see https://tailwindcss.com/docs/transform-origin\n */\n 'transform-origin': [{\n origin: scalePositionWithArbitrary()\n }],\n /**\n * Transform Style\n * @see https://tailwindcss.com/docs/transform-style\n */\n 'transform-style': [{\n transform: ['3d', 'flat']\n }],\n /**\n * Translate\n * @see https://tailwindcss.com/docs/translate\n */\n translate: [{\n translate: scaleTranslate()\n }],\n /**\n * Translate X\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-x': [{\n 'translate-x': scaleTranslate()\n }],\n /**\n * Translate Y\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-y': [{\n 'translate-y': scaleTranslate()\n }],\n /**\n * Translate Z\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-z': [{\n 'translate-z': scaleTranslate()\n }],\n /**\n * Translate None\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-none': ['translate-none'],\n // ---------------------\n // --- Interactivity ---\n // ---------------------\n /**\n * Accent Color\n * @see https://tailwindcss.com/docs/accent-color\n */\n accent: [{\n accent: scaleColor()\n }],\n /**\n * Appearance\n * @see https://tailwindcss.com/docs/appearance\n */\n appearance: [{\n appearance: ['none', 'auto']\n }],\n /**\n * Caret Color\n * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities\n */\n 'caret-color': [{\n caret: scaleColor()\n }],\n /**\n * Color Scheme\n * @see https://tailwindcss.com/docs/color-scheme\n */\n 'color-scheme': [{\n scheme: ['normal', 'dark', 'light', 'light-dark', 'only-dark', 'only-light']\n }],\n /**\n * Cursor\n * @see https://tailwindcss.com/docs/cursor\n */\n cursor: [{\n cursor: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'help', 'not-allowed', 'none', 'context-menu', 'progress', 'cell', 'crosshair', 'vertical-text', 'alias', 'copy', 'no-drop', 'grab', 'grabbing', 'all-scroll', 'col-resize', 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Field Sizing\n * @see https://tailwindcss.com/docs/field-sizing\n */\n 'field-sizing': [{\n 'field-sizing': ['fixed', 'content']\n }],\n /**\n * Pointer Events\n * @see https://tailwindcss.com/docs/pointer-events\n */\n 'pointer-events': [{\n 'pointer-events': ['auto', 'none']\n }],\n /**\n * Resize\n * @see https://tailwindcss.com/docs/resize\n */\n resize: [{\n resize: ['none', '', 'y', 'x']\n }],\n /**\n * Scroll Behavior\n * @see https://tailwindcss.com/docs/scroll-behavior\n */\n 'scroll-behavior': [{\n scroll: ['auto', 'smooth']\n }],\n /**\n * Scroll Margin\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-m': [{\n 'scroll-m': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin X\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mx': [{\n 'scroll-mx': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Y\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-my': [{\n 'scroll-my': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Start\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ms': [{\n 'scroll-ms': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin End\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-me': [{\n 'scroll-me': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Top\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mt': [{\n 'scroll-mt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Right\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mr': [{\n 'scroll-mr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Bottom\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mb': [{\n 'scroll-mb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Left\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ml': [{\n 'scroll-ml': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-p': [{\n 'scroll-p': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding X\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-px': [{\n 'scroll-px': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Y\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-py': [{\n 'scroll-py': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Start\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-ps': [{\n 'scroll-ps': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding End\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pe': [{\n 'scroll-pe': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Top\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pt': [{\n 'scroll-pt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Right\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pr': [{\n 'scroll-pr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Bottom\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pb': [{\n 'scroll-pb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Left\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pl': [{\n 'scroll-pl': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Snap Align\n * @see https://tailwindcss.com/docs/scroll-snap-align\n */\n 'snap-align': [{\n snap: ['start', 'end', 'center', 'align-none']\n }],\n /**\n * Scroll Snap Stop\n * @see https://tailwindcss.com/docs/scroll-snap-stop\n */\n 'snap-stop': [{\n snap: ['normal', 'always']\n }],\n /**\n * Scroll Snap Type\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-type': [{\n snap: ['none', 'x', 'y', 'both']\n }],\n /**\n * Scroll Snap Type Strictness\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-strictness': [{\n snap: ['mandatory', 'proximity']\n }],\n /**\n * Touch Action\n * @see https://tailwindcss.com/docs/touch-action\n */\n touch: [{\n touch: ['auto', 'none', 'manipulation']\n }],\n /**\n * Touch Action X\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-x': [{\n 'touch-pan': ['x', 'left', 'right']\n }],\n /**\n * Touch Action Y\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-y': [{\n 'touch-pan': ['y', 'up', 'down']\n }],\n /**\n * Touch Action Pinch Zoom\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-pz': ['touch-pinch-zoom'],\n /**\n * User Select\n * @see https://tailwindcss.com/docs/user-select\n */\n select: [{\n select: ['none', 'text', 'all', 'auto']\n }],\n /**\n * Will Change\n * @see https://tailwindcss.com/docs/will-change\n */\n 'will-change': [{\n 'will-change': ['auto', 'scroll', 'contents', 'transform', isArbitraryVariable, isArbitraryValue]\n }],\n // -----------\n // --- SVG ---\n // -----------\n /**\n * Fill\n * @see https://tailwindcss.com/docs/fill\n */\n fill: [{\n fill: ['none', ...scaleColor()]\n }],\n /**\n * Stroke Width\n * @see https://tailwindcss.com/docs/stroke-width\n */\n 'stroke-w': [{\n stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]\n }],\n /**\n * Stroke\n * @see https://tailwindcss.com/docs/stroke\n */\n stroke: [{\n stroke: ['none', ...scaleColor()]\n }],\n // ---------------------\n // --- Accessibility ---\n // ---------------------\n /**\n * Forced Color Adjust\n * @see https://tailwindcss.com/docs/forced-color-adjust\n */\n 'forced-color-adjust': [{\n 'forced-color-adjust': ['auto', 'none']\n }]\n },\n conflictingClassGroups: {\n overflow: ['overflow-x', 'overflow-y'],\n overscroll: ['overscroll-x', 'overscroll-y'],\n inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],\n 'inset-x': ['right', 'left'],\n 'inset-y': ['top', 'bottom'],\n flex: ['basis', 'grow', 'shrink'],\n gap: ['gap-x', 'gap-y'],\n p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],\n px: ['pr', 'pl'],\n py: ['pt', 'pb'],\n m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],\n mx: ['mr', 'ml'],\n my: ['mt', 'mb'],\n size: ['w', 'h'],\n 'font-size': ['leading'],\n 'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],\n 'fvn-ordinal': ['fvn-normal'],\n 'fvn-slashed-zero': ['fvn-normal'],\n 'fvn-figure': ['fvn-normal'],\n 'fvn-spacing': ['fvn-normal'],\n 'fvn-fraction': ['fvn-normal'],\n 'line-clamp': ['display', 'overflow'],\n rounded: ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],\n 'rounded-s': ['rounded-ss', 'rounded-es'],\n 'rounded-e': ['rounded-se', 'rounded-ee'],\n 'rounded-t': ['rounded-tl', 'rounded-tr'],\n 'rounded-r': ['rounded-tr', 'rounded-br'],\n 'rounded-b': ['rounded-br', 'rounded-bl'],\n 'rounded-l': ['rounded-tl', 'rounded-bl'],\n 'border-spacing': ['border-spacing-x', 'border-spacing-y'],\n 'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],\n 'border-w-x': ['border-w-r', 'border-w-l'],\n 'border-w-y': ['border-w-t', 'border-w-b'],\n 'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],\n 'border-color-x': ['border-color-r', 'border-color-l'],\n 'border-color-y': ['border-color-t', 'border-color-b'],\n translate: ['translate-x', 'translate-y', 'translate-none'],\n 'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],\n 'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],\n 'scroll-mx': ['scroll-mr', 'scroll-ml'],\n 'scroll-my': ['scroll-mt', 'scroll-mb'],\n 'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],\n 'scroll-px': ['scroll-pr', 'scroll-pl'],\n 'scroll-py': ['scroll-pt', 'scroll-pb'],\n touch: ['touch-x', 'touch-y', 'touch-pz'],\n 'touch-x': ['touch'],\n 'touch-y': ['touch'],\n 'touch-pz': ['touch']\n },\n conflictingClassGroupModifiers: {\n 'font-size': ['leading']\n },\n orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']\n };\n};\n\n/**\n * @param baseConfig Config where other config will be merged into. This object will be mutated.\n * @param configExtension Partial config to merge into the `baseConfig`.\n */\nconst mergeConfigs = (baseConfig, {\n cacheSize,\n prefix,\n experimentalParseClassName,\n extend = {},\n override = {}\n}) => {\n overrideProperty(baseConfig, 'cacheSize', cacheSize);\n overrideProperty(baseConfig, 'prefix', prefix);\n overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName);\n overrideConfigProperties(baseConfig.theme, override.theme);\n overrideConfigProperties(baseConfig.classGroups, override.classGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroupModifiers, override.conflictingClassGroupModifiers);\n overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers);\n mergeConfigProperties(baseConfig.theme, extend.theme);\n mergeConfigProperties(baseConfig.classGroups, extend.classGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroupModifiers, extend.conflictingClassGroupModifiers);\n mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers');\n return baseConfig;\n};\nconst overrideProperty = (baseObject, overrideKey, overrideValue) => {\n if (overrideValue !== undefined) {\n baseObject[overrideKey] = overrideValue;\n }\n};\nconst overrideConfigProperties = (baseObject, overrideObject) => {\n if (overrideObject) {\n for (const key in overrideObject) {\n overrideProperty(baseObject, key, overrideObject[key]);\n }\n }\n};\nconst mergeConfigProperties = (baseObject, mergeObject) => {\n if (mergeObject) {\n for (const key in mergeObject) {\n mergeArrayProperties(baseObject, mergeObject, key);\n }\n }\n};\nconst mergeArrayProperties = (baseObject, mergeObject, key) => {\n const mergeValue = mergeObject[key];\n if (mergeValue !== undefined) {\n baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue;\n }\n};\nconst extendTailwindMerge = (configExtension, ...createConfig) => typeof configExtension === 'function' ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig) : createTailwindMerge(() => mergeConfigs(getDefaultConfig(), configExtension), ...createConfig);\nconst twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);\nexport { createTailwindMerge, extendTailwindMerge, fromTheme, getDefaultConfig, mergeConfigs, twJoin, twMerge, validators };\n//# sourceMappingURL=bundle-mjs.mjs.map\n","import { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nexport const cn = (...inputs) => twMerge(clsx(inputs));\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","/**\n * @license React\n * scheduler.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nfunction push(heap, node) {\n var index = heap.length;\n heap.push(node);\n a: for (; 0 < index; ) {\n var parentIndex = (index - 1) >>> 1,\n parent = heap[parentIndex];\n if (0 < compare(parent, node))\n (heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);\n else break a;\n }\n}\nfunction peek(heap) {\n return 0 === heap.length ? null : heap[0];\n}\nfunction pop(heap) {\n if (0 === heap.length) return null;\n var first = heap[0],\n last = heap.pop();\n if (last !== first) {\n heap[0] = last;\n a: for (\n var index = 0, length = heap.length, halfLength = length >>> 1;\n index < halfLength;\n\n ) {\n var leftIndex = 2 * (index + 1) - 1,\n left = heap[leftIndex],\n rightIndex = leftIndex + 1,\n right = heap[rightIndex];\n if (0 > compare(left, last))\n rightIndex < length && 0 > compare(right, left)\n ? ((heap[index] = right),\n (heap[rightIndex] = last),\n (index = rightIndex))\n : ((heap[index] = left),\n (heap[leftIndex] = last),\n (index = leftIndex));\n else if (rightIndex < length && 0 > compare(right, last))\n (heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);\n else break a;\n }\n }\n return first;\n}\nfunction compare(a, b) {\n var diff = a.sortIndex - b.sortIndex;\n return 0 !== diff ? diff : a.id - b.id;\n}\nexports.unstable_now = void 0;\nif (\"object\" === typeof performance && \"function\" === typeof performance.now) {\n var localPerformance = performance;\n exports.unstable_now = function () {\n return localPerformance.now();\n };\n} else {\n var localDate = Date,\n initialTime = localDate.now();\n exports.unstable_now = function () {\n return localDate.now() - initialTime;\n };\n}\nvar taskQueue = [],\n timerQueue = [],\n taskIdCounter = 1,\n currentTask = null,\n currentPriorityLevel = 3,\n isPerformingWork = !1,\n isHostCallbackScheduled = !1,\n isHostTimeoutScheduled = !1,\n needsPaint = !1,\n localSetTimeout = \"function\" === typeof setTimeout ? setTimeout : null,\n localClearTimeout = \"function\" === typeof clearTimeout ? clearTimeout : null,\n localSetImmediate = \"undefined\" !== typeof setImmediate ? setImmediate : null;\nfunction advanceTimers(currentTime) {\n for (var timer = peek(timerQueue); null !== timer; ) {\n if (null === timer.callback) pop(timerQueue);\n else if (timer.startTime <= currentTime)\n pop(timerQueue),\n (timer.sortIndex = timer.expirationTime),\n push(taskQueue, timer);\n else break;\n timer = peek(timerQueue);\n }\n}\nfunction handleTimeout(currentTime) {\n isHostTimeoutScheduled = !1;\n advanceTimers(currentTime);\n if (!isHostCallbackScheduled)\n if (null !== peek(taskQueue))\n (isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);\n }\n}\nvar isMessageLoopRunning = !1,\n taskTimeoutID = -1,\n frameInterval = 5,\n startTime = -1;\nfunction shouldYieldToHost() {\n return needsPaint\n ? !0\n : exports.unstable_now() - startTime < frameInterval\n ? !1\n : !0;\n}\nfunction performWorkUntilDeadline() {\n needsPaint = !1;\n if (isMessageLoopRunning) {\n var currentTime = exports.unstable_now();\n startTime = currentTime;\n var hasMoreWork = !0;\n try {\n a: {\n isHostCallbackScheduled = !1;\n isHostTimeoutScheduled &&\n ((isHostTimeoutScheduled = !1),\n localClearTimeout(taskTimeoutID),\n (taskTimeoutID = -1));\n isPerformingWork = !0;\n var previousPriorityLevel = currentPriorityLevel;\n try {\n b: {\n advanceTimers(currentTime);\n for (\n currentTask = peek(taskQueue);\n null !== currentTask &&\n !(\n currentTask.expirationTime > currentTime && shouldYieldToHost()\n );\n\n ) {\n var callback = currentTask.callback;\n if (\"function\" === typeof callback) {\n currentTask.callback = null;\n currentPriorityLevel = currentTask.priorityLevel;\n var continuationCallback = callback(\n currentTask.expirationTime <= currentTime\n );\n currentTime = exports.unstable_now();\n if (\"function\" === typeof continuationCallback) {\n currentTask.callback = continuationCallback;\n advanceTimers(currentTime);\n hasMoreWork = !0;\n break b;\n }\n currentTask === peek(taskQueue) && pop(taskQueue);\n advanceTimers(currentTime);\n } else pop(taskQueue);\n currentTask = peek(taskQueue);\n }\n if (null !== currentTask) hasMoreWork = !0;\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(\n handleTimeout,\n firstTimer.startTime - currentTime\n );\n hasMoreWork = !1;\n }\n }\n break a;\n } finally {\n (currentTask = null),\n (currentPriorityLevel = previousPriorityLevel),\n (isPerformingWork = !1);\n }\n hasMoreWork = void 0;\n }\n } finally {\n hasMoreWork\n ? schedulePerformWorkUntilDeadline()\n : (isMessageLoopRunning = !1);\n }\n }\n}\nvar schedulePerformWorkUntilDeadline;\nif (\"function\" === typeof localSetImmediate)\n schedulePerformWorkUntilDeadline = function () {\n localSetImmediate(performWorkUntilDeadline);\n };\nelse if (\"undefined\" !== typeof MessageChannel) {\n var channel = new MessageChannel(),\n port = channel.port2;\n channel.port1.onmessage = performWorkUntilDeadline;\n schedulePerformWorkUntilDeadline = function () {\n port.postMessage(null);\n };\n} else\n schedulePerformWorkUntilDeadline = function () {\n localSetTimeout(performWorkUntilDeadline, 0);\n };\nfunction requestHostTimeout(callback, ms) {\n taskTimeoutID = localSetTimeout(function () {\n callback(exports.unstable_now());\n }, ms);\n}\nexports.unstable_IdlePriority = 5;\nexports.unstable_ImmediatePriority = 1;\nexports.unstable_LowPriority = 4;\nexports.unstable_NormalPriority = 3;\nexports.unstable_Profiling = null;\nexports.unstable_UserBlockingPriority = 2;\nexports.unstable_cancelCallback = function (task) {\n task.callback = null;\n};\nexports.unstable_forceFrameRate = function (fps) {\n 0 > fps || 125 < fps\n ? console.error(\n \"forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported\"\n )\n : (frameInterval = 0 < fps ? Math.floor(1e3 / fps) : 5);\n};\nexports.unstable_getCurrentPriorityLevel = function () {\n return currentPriorityLevel;\n};\nexports.unstable_next = function (eventHandler) {\n switch (currentPriorityLevel) {\n case 1:\n case 2:\n case 3:\n var priorityLevel = 3;\n break;\n default:\n priorityLevel = currentPriorityLevel;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_requestPaint = function () {\n needsPaint = !0;\n};\nexports.unstable_runWithPriority = function (priorityLevel, eventHandler) {\n switch (priorityLevel) {\n case 1:\n case 2:\n case 3:\n case 4:\n case 5:\n break;\n default:\n priorityLevel = 3;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_scheduleCallback = function (\n priorityLevel,\n callback,\n options\n) {\n var currentTime = exports.unstable_now();\n \"object\" === typeof options && null !== options\n ? ((options = options.delay),\n (options =\n \"number\" === typeof options && 0 < options\n ? currentTime + options\n : currentTime))\n : (options = currentTime);\n switch (priorityLevel) {\n case 1:\n var timeout = -1;\n break;\n case 2:\n timeout = 250;\n break;\n case 5:\n timeout = 1073741823;\n break;\n case 4:\n timeout = 1e4;\n break;\n default:\n timeout = 5e3;\n }\n timeout = options + timeout;\n priorityLevel = {\n id: taskIdCounter++,\n callback: callback,\n priorityLevel: priorityLevel,\n startTime: options,\n expirationTime: timeout,\n sortIndex: -1\n };\n options > currentTime\n ? ((priorityLevel.sortIndex = options),\n push(timerQueue, priorityLevel),\n null === peek(taskQueue) &&\n priorityLevel === peek(timerQueue) &&\n (isHostTimeoutScheduled\n ? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))\n : (isHostTimeoutScheduled = !0),\n requestHostTimeout(handleTimeout, options - currentTime)))\n : ((priorityLevel.sortIndex = timeout),\n push(taskQueue, priorityLevel),\n isHostCallbackScheduled ||\n isPerformingWork ||\n ((isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));\n return priorityLevel;\n};\nexports.unstable_shouldYield = shouldYieldToHost;\nexports.unstable_wrapCallback = function (callback) {\n var parentPriorityLevel = currentPriorityLevel;\n return function () {\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = parentPriorityLevel;\n try {\n return callback.apply(this, arguments);\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n };\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction noop() {}\nvar Internals = {\n d: {\n f: noop,\n r: function () {\n throw Error(formatProdErrorMessage(522));\n },\n D: noop,\n C: noop,\n L: noop,\n m: noop,\n X: noop,\n S: noop,\n M: noop\n },\n p: 0,\n findDOMNode: null\n },\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\");\nfunction createPortal$1(children, containerInfo, implementation) {\n var key =\n 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;\n return {\n $$typeof: REACT_PORTAL_TYPE,\n key: null == key ? null : \"\" + key,\n children: children,\n containerInfo: containerInfo,\n implementation: implementation\n };\n}\nvar ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\nfunction getCrossOriginStringAs(as, input) {\n if (\"font\" === as) return \"\";\n if (\"string\" === typeof input)\n return \"use-credentials\" === input ? input : \"\";\n}\nexports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n Internals;\nexports.createPortal = function (children, container) {\n var key =\n 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;\n if (\n !container ||\n (1 !== container.nodeType &&\n 9 !== container.nodeType &&\n 11 !== container.nodeType)\n )\n throw Error(formatProdErrorMessage(299));\n return createPortal$1(children, container, null, key);\n};\nexports.flushSync = function (fn) {\n var previousTransition = ReactSharedInternals.T,\n previousUpdatePriority = Internals.p;\n try {\n if (((ReactSharedInternals.T = null), (Internals.p = 2), fn)) return fn();\n } finally {\n (ReactSharedInternals.T = previousTransition),\n (Internals.p = previousUpdatePriority),\n Internals.d.f();\n }\n};\nexports.preconnect = function (href, options) {\n \"string\" === typeof href &&\n (options\n ? ((options = options.crossOrigin),\n (options =\n \"string\" === typeof options\n ? \"use-credentials\" === options\n ? options\n : \"\"\n : void 0))\n : (options = null),\n Internals.d.C(href, options));\n};\nexports.prefetchDNS = function (href) {\n \"string\" === typeof href && Internals.d.D(href);\n};\nexports.preinit = function (href, options) {\n if (\"string\" === typeof href && options && \"string\" === typeof options.as) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin),\n integrity =\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n fetchPriority =\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0;\n \"style\" === as\n ? Internals.d.S(\n href,\n \"string\" === typeof options.precedence ? options.precedence : void 0,\n {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority\n }\n )\n : \"script\" === as &&\n Internals.d.X(href, {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n};\nexports.preinitModule = function (href, options) {\n if (\"string\" === typeof href)\n if (\"object\" === typeof options && null !== options) {\n if (null == options.as || \"script\" === options.as) {\n var crossOrigin = getCrossOriginStringAs(\n options.as,\n options.crossOrigin\n );\n Internals.d.M(href, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n } else null == options && Internals.d.M(href);\n};\nexports.preload = function (href, options) {\n if (\n \"string\" === typeof href &&\n \"object\" === typeof options &&\n null !== options &&\n \"string\" === typeof options.as\n ) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);\n Internals.d.L(href, as, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0,\n type: \"string\" === typeof options.type ? options.type : void 0,\n fetchPriority:\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0,\n referrerPolicy:\n \"string\" === typeof options.referrerPolicy\n ? options.referrerPolicy\n : void 0,\n imageSrcSet:\n \"string\" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,\n imageSizes:\n \"string\" === typeof options.imageSizes ? options.imageSizes : void 0,\n media: \"string\" === typeof options.media ? options.media : void 0\n });\n }\n};\nexports.preloadModule = function (href, options) {\n if (\"string\" === typeof href)\n if (options) {\n var crossOrigin = getCrossOriginStringAs(options.as, options.crossOrigin);\n Internals.d.m(href, {\n as:\n \"string\" === typeof options.as && \"script\" !== options.as\n ? options.as\n : void 0,\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0\n });\n } else Internals.d.m(href);\n};\nexports.requestFormReset = function (form) {\n Internals.d.r(form);\n};\nexports.unstable_batchedUpdates = function (fn, a) {\n return fn(a);\n};\nexports.useFormState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useFormState(action, initialState, permalink);\n};\nexports.useFormStatus = function () {\n return ReactSharedInternals.H.useHostTransitionStatus();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nfunction checkDCE() {\n /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\n if (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' ||\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function'\n ) {\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // This branch is unreachable because this function is only called\n // in production, but the condition is true only in development.\n // Therefore if the branch is still here, dead code elimination wasn't\n // properly applied.\n // Don't change the message. React DevTools relies on it. Also make sure\n // this message doesn't occur elsewhere in this function, or it will cause\n // a false positive.\n throw new Error('^_^');\n }\n try {\n // Verify that the code above has been dead code eliminated (DCE'd).\n __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);\n } catch (err) {\n // DevTools shouldn't crash React, no matter what.\n // We should still report in case we break this code.\n console.error(err);\n }\n}\n\nif (process.env.NODE_ENV === 'production') {\n // DCE check should happen before ReactDOM bundle executes so that\n // DevTools can report bad minification during injection.\n checkDCE();\n module.exports = require('./cjs/react-dom.production.js');\n} else {\n module.exports = require('./cjs/react-dom.development.js');\n}\n","/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n\"use strict\";\nvar Scheduler = require(\"scheduler\"),\n React = require(\"react\"),\n ReactDOM = require(\"react-dom\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction isValidContainer(node) {\n return !(\n !node ||\n (1 !== node.nodeType && 9 !== node.nodeType && 11 !== node.nodeType)\n );\n}\nfunction getNearestMountedFiber(fiber) {\n var node = fiber,\n nearestMounted = fiber;\n if (fiber.alternate) for (; node.return; ) node = node.return;\n else {\n fiber = node;\n do\n (node = fiber),\n 0 !== (node.flags & 4098) && (nearestMounted = node.return),\n (fiber = node.return);\n while (fiber);\n }\n return 3 === node.tag ? nearestMounted : null;\n}\nfunction getSuspenseInstanceFromFiber(fiber) {\n if (13 === fiber.tag) {\n var suspenseState = fiber.memoizedState;\n null === suspenseState &&\n ((fiber = fiber.alternate),\n null !== fiber && (suspenseState = fiber.memoizedState));\n if (null !== suspenseState) return suspenseState.dehydrated;\n }\n return null;\n}\nfunction getActivityInstanceFromFiber(fiber) {\n if (31 === fiber.tag) {\n var activityState = fiber.memoizedState;\n null === activityState &&\n ((fiber = fiber.alternate),\n null !== fiber && (activityState = fiber.memoizedState));\n if (null !== activityState) return activityState.dehydrated;\n }\n return null;\n}\nfunction assertIsMounted(fiber) {\n if (getNearestMountedFiber(fiber) !== fiber)\n throw Error(formatProdErrorMessage(188));\n}\nfunction findCurrentFiberUsingSlowPath(fiber) {\n var alternate = fiber.alternate;\n if (!alternate) {\n alternate = getNearestMountedFiber(fiber);\n if (null === alternate) throw Error(formatProdErrorMessage(188));\n return alternate !== fiber ? null : fiber;\n }\n for (var a = fiber, b = alternate; ; ) {\n var parentA = a.return;\n if (null === parentA) break;\n var parentB = parentA.alternate;\n if (null === parentB) {\n b = parentA.return;\n if (null !== b) {\n a = b;\n continue;\n }\n break;\n }\n if (parentA.child === parentB.child) {\n for (parentB = parentA.child; parentB; ) {\n if (parentB === a) return assertIsMounted(parentA), fiber;\n if (parentB === b) return assertIsMounted(parentA), alternate;\n parentB = parentB.sibling;\n }\n throw Error(formatProdErrorMessage(188));\n }\n if (a.return !== b.return) (a = parentA), (b = parentB);\n else {\n for (var didFindChild = !1, child$0 = parentA.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentA;\n b = parentB;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentA;\n a = parentB;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) {\n for (child$0 = parentB.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentB;\n b = parentA;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentB;\n a = parentA;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) throw Error(formatProdErrorMessage(189));\n }\n }\n if (a.alternate !== b) throw Error(formatProdErrorMessage(190));\n }\n if (3 !== a.tag) throw Error(formatProdErrorMessage(188));\n return a.stateNode.current === a ? fiber : alternate;\n}\nfunction findCurrentHostFiberImpl(node) {\n var tag = node.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return node;\n for (node = node.child; null !== node; ) {\n tag = findCurrentHostFiberImpl(node);\n if (null !== tag) return tag;\n node = node.sibling;\n }\n return null;\n}\nvar assign = Object.assign,\n REACT_LEGACY_ELEMENT_TYPE = Symbol.for(\"react.element\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\nSymbol.for(\"react.scope\");\nvar REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\");\nSymbol.for(\"react.legacy_hidden\");\nSymbol.for(\"react.tracing_marker\");\nvar REACT_MEMO_CACHE_SENTINEL = Symbol.for(\"react.memo_cache_sentinel\");\nSymbol.for(\"react.view_transition\");\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\");\nfunction getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (type.$$typeof) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n}\nvar isArrayImpl = Array.isArray,\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n ReactDOMSharedInternals =\n ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n sharedNotPendingObject = {\n pending: !1,\n data: null,\n method: null,\n action: null\n },\n valueStack = [],\n index = -1;\nfunction createCursor(defaultValue) {\n return { current: defaultValue };\n}\nfunction pop(cursor) {\n 0 > index ||\n ((cursor.current = valueStack[index]), (valueStack[index] = null), index--);\n}\nfunction push(cursor, value) {\n index++;\n valueStack[index] = cursor.current;\n cursor.current = value;\n}\nvar contextStackCursor = createCursor(null),\n contextFiberStackCursor = createCursor(null),\n rootInstanceStackCursor = createCursor(null),\n hostTransitionProviderCursor = createCursor(null);\nfunction pushHostContainer(fiber, nextRootInstance) {\n push(rootInstanceStackCursor, nextRootInstance);\n push(contextFiberStackCursor, fiber);\n push(contextStackCursor, null);\n switch (nextRootInstance.nodeType) {\n case 9:\n case 11:\n fiber = (fiber = nextRootInstance.documentElement)\n ? (fiber = fiber.namespaceURI)\n ? getOwnHostContext(fiber)\n : 0\n : 0;\n break;\n default:\n if (\n ((fiber = nextRootInstance.tagName),\n (nextRootInstance = nextRootInstance.namespaceURI))\n )\n (nextRootInstance = getOwnHostContext(nextRootInstance)),\n (fiber = getChildHostContextProd(nextRootInstance, fiber));\n else\n switch (fiber) {\n case \"svg\":\n fiber = 1;\n break;\n case \"math\":\n fiber = 2;\n break;\n default:\n fiber = 0;\n }\n }\n pop(contextStackCursor);\n push(contextStackCursor, fiber);\n}\nfunction popHostContainer() {\n pop(contextStackCursor);\n pop(contextFiberStackCursor);\n pop(rootInstanceStackCursor);\n}\nfunction pushHostContext(fiber) {\n null !== fiber.memoizedState && push(hostTransitionProviderCursor, fiber);\n var context = contextStackCursor.current;\n var JSCompiler_inline_result = getChildHostContextProd(context, fiber.type);\n context !== JSCompiler_inline_result &&\n (push(contextFiberStackCursor, fiber),\n push(contextStackCursor, JSCompiler_inline_result));\n}\nfunction popHostContext(fiber) {\n contextFiberStackCursor.current === fiber &&\n (pop(contextStackCursor), pop(contextFiberStackCursor));\n hostTransitionProviderCursor.current === fiber &&\n (pop(hostTransitionProviderCursor),\n (HostTransitionContext._currentValue = sharedNotPendingObject));\n}\nvar prefix, suffix;\nfunction describeBuiltInComponentFrame(name) {\n if (void 0 === prefix)\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = (match && match[1]) || \"\";\n suffix =\n -1 < x.stack.indexOf(\"\\n at\")\n ? \" ()\"\n : -1 < x.stack.indexOf(\"@\")\n ? \"@unknown:0:0\"\n : \"\";\n }\n return \"\\n\" + prefix + name + suffix;\n}\nvar reentry = !1;\nfunction describeNativeComponentFrame(fn, construct) {\n if (!fn || reentry) return \"\";\n reentry = !0;\n var previousPrepareStackTrace = Error.prepareStackTrace;\n Error.prepareStackTrace = void 0;\n try {\n var RunInRootFrame = {\n DetermineComponentFrameRoot: function () {\n try {\n if (construct) {\n var Fake = function () {\n throw Error();\n };\n Object.defineProperty(Fake.prototype, \"props\", {\n set: function () {\n throw Error();\n }\n });\n if (\"object\" === typeof Reflect && Reflect.construct) {\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n var control = x;\n }\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x$1) {\n control = x$1;\n }\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x$2) {\n control = x$2;\n }\n (Fake = fn()) &&\n \"function\" === typeof Fake.catch &&\n Fake.catch(function () {});\n }\n } catch (sample) {\n if (sample && control && \"string\" === typeof sample.stack)\n return [sample.stack, control.stack];\n }\n return [null, null];\n }\n };\n RunInRootFrame.DetermineComponentFrameRoot.displayName =\n \"DetermineComponentFrameRoot\";\n var namePropDescriptor = Object.getOwnPropertyDescriptor(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\"\n );\n namePropDescriptor &&\n namePropDescriptor.configurable &&\n Object.defineProperty(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\",\n { value: \"DetermineComponentFrameRoot\" }\n );\n var _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(),\n sampleStack = _RunInRootFrame$Deter[0],\n controlStack = _RunInRootFrame$Deter[1];\n if (sampleStack && controlStack) {\n var sampleLines = sampleStack.split(\"\\n\"),\n controlLines = controlStack.split(\"\\n\");\n for (\n namePropDescriptor = RunInRootFrame = 0;\n RunInRootFrame < sampleLines.length &&\n !sampleLines[RunInRootFrame].includes(\"DetermineComponentFrameRoot\");\n\n )\n RunInRootFrame++;\n for (\n ;\n namePropDescriptor < controlLines.length &&\n !controlLines[namePropDescriptor].includes(\n \"DetermineComponentFrameRoot\"\n );\n\n )\n namePropDescriptor++;\n if (\n RunInRootFrame === sampleLines.length ||\n namePropDescriptor === controlLines.length\n )\n for (\n RunInRootFrame = sampleLines.length - 1,\n namePropDescriptor = controlLines.length - 1;\n 1 <= RunInRootFrame &&\n 0 <= namePropDescriptor &&\n sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor];\n\n )\n namePropDescriptor--;\n for (\n ;\n 1 <= RunInRootFrame && 0 <= namePropDescriptor;\n RunInRootFrame--, namePropDescriptor--\n )\n if (sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {\n if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {\n do\n if (\n (RunInRootFrame--,\n namePropDescriptor--,\n 0 > namePropDescriptor ||\n sampleLines[RunInRootFrame] !==\n controlLines[namePropDescriptor])\n ) {\n var frame =\n \"\\n\" +\n sampleLines[RunInRootFrame].replace(\" at new \", \" at \");\n fn.displayName &&\n frame.includes(\"\") &&\n (frame = frame.replace(\"\", fn.displayName));\n return frame;\n }\n while (1 <= RunInRootFrame && 0 <= namePropDescriptor);\n }\n break;\n }\n }\n } finally {\n (reentry = !1), (Error.prepareStackTrace = previousPrepareStackTrace);\n }\n return (previousPrepareStackTrace = fn ? fn.displayName || fn.name : \"\")\n ? describeBuiltInComponentFrame(previousPrepareStackTrace)\n : \"\";\n}\nfunction describeFiber(fiber, childFiber) {\n switch (fiber.tag) {\n case 26:\n case 27:\n case 5:\n return describeBuiltInComponentFrame(fiber.type);\n case 16:\n return describeBuiltInComponentFrame(\"Lazy\");\n case 13:\n return fiber.child !== childFiber && null !== childFiber\n ? describeBuiltInComponentFrame(\"Suspense Fallback\")\n : describeBuiltInComponentFrame(\"Suspense\");\n case 19:\n return describeBuiltInComponentFrame(\"SuspenseList\");\n case 0:\n case 15:\n return describeNativeComponentFrame(fiber.type, !1);\n case 11:\n return describeNativeComponentFrame(fiber.type.render, !1);\n case 1:\n return describeNativeComponentFrame(fiber.type, !0);\n case 31:\n return describeBuiltInComponentFrame(\"Activity\");\n default:\n return \"\";\n }\n}\nfunction getStackByFiberInDevAndProd(workInProgress) {\n try {\n var info = \"\",\n previous = null;\n do\n (info += describeFiber(workInProgress, previous)),\n (previous = workInProgress),\n (workInProgress = workInProgress.return);\n while (workInProgress);\n return info;\n } catch (x) {\n return \"\\nError generating stack: \" + x.message + \"\\n\" + x.stack;\n }\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty,\n scheduleCallback$3 = Scheduler.unstable_scheduleCallback,\n cancelCallback$1 = Scheduler.unstable_cancelCallback,\n shouldYield = Scheduler.unstable_shouldYield,\n requestPaint = Scheduler.unstable_requestPaint,\n now = Scheduler.unstable_now,\n getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel,\n ImmediatePriority = Scheduler.unstable_ImmediatePriority,\n UserBlockingPriority = Scheduler.unstable_UserBlockingPriority,\n NormalPriority$1 = Scheduler.unstable_NormalPriority,\n LowPriority = Scheduler.unstable_LowPriority,\n IdlePriority = Scheduler.unstable_IdlePriority,\n log$1 = Scheduler.log,\n unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue,\n rendererID = null,\n injectedHook = null;\nfunction setIsStrictModeForDevtools(newIsStrictMode) {\n \"function\" === typeof log$1 && unstable_setDisableYieldValue(newIsStrictMode);\n if (injectedHook && \"function\" === typeof injectedHook.setStrictMode)\n try {\n injectedHook.setStrictMode(rendererID, newIsStrictMode);\n } catch (err) {}\n}\nvar clz32 = Math.clz32 ? Math.clz32 : clz32Fallback,\n log = Math.log,\n LN2 = Math.LN2;\nfunction clz32Fallback(x) {\n x >>>= 0;\n return 0 === x ? 32 : (31 - ((log(x) / LN2) | 0)) | 0;\n}\nvar nextTransitionUpdateLane = 256,\n nextTransitionDeferredLane = 262144,\n nextRetryLane = 4194304;\nfunction getHighestPriorityLanes(lanes) {\n var pendingSyncLanes = lanes & 42;\n if (0 !== pendingSyncLanes) return pendingSyncLanes;\n switch (lanes & -lanes) {\n case 1:\n return 1;\n case 2:\n return 2;\n case 4:\n return 4;\n case 8:\n return 8;\n case 16:\n return 16;\n case 32:\n return 32;\n case 64:\n return 64;\n case 128:\n return 128;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n return lanes & 261888;\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return lanes & 3932160;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return lanes & 62914560;\n case 67108864:\n return 67108864;\n case 134217728:\n return 134217728;\n case 268435456:\n return 268435456;\n case 536870912:\n return 536870912;\n case 1073741824:\n return 0;\n default:\n return lanes;\n }\n}\nfunction getNextLanes(root, wipLanes, rootHasPendingCommit) {\n var pendingLanes = root.pendingLanes;\n if (0 === pendingLanes) return 0;\n var nextLanes = 0,\n suspendedLanes = root.suspendedLanes,\n pingedLanes = root.pingedLanes;\n root = root.warmLanes;\n var nonIdlePendingLanes = pendingLanes & 134217727;\n 0 !== nonIdlePendingLanes\n ? ((pendingLanes = nonIdlePendingLanes & ~suspendedLanes),\n 0 !== pendingLanes\n ? (nextLanes = getHighestPriorityLanes(pendingLanes))\n : ((pingedLanes &= nonIdlePendingLanes),\n 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = nonIdlePendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit)))))\n : ((nonIdlePendingLanes = pendingLanes & ~suspendedLanes),\n 0 !== nonIdlePendingLanes\n ? (nextLanes = getHighestPriorityLanes(nonIdlePendingLanes))\n : 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = pendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit))));\n return 0 === nextLanes\n ? 0\n : 0 !== wipLanes &&\n wipLanes !== nextLanes &&\n 0 === (wipLanes & suspendedLanes) &&\n ((suspendedLanes = nextLanes & -nextLanes),\n (rootHasPendingCommit = wipLanes & -wipLanes),\n suspendedLanes >= rootHasPendingCommit ||\n (32 === suspendedLanes && 0 !== (rootHasPendingCommit & 4194048)))\n ? wipLanes\n : nextLanes;\n}\nfunction checkIfRootIsPrerendering(root, renderLanes) {\n return (\n 0 ===\n (root.pendingLanes &\n ~(root.suspendedLanes & ~root.pingedLanes) &\n renderLanes)\n );\n}\nfunction computeExpirationTime(lane, currentTime) {\n switch (lane) {\n case 1:\n case 2:\n case 4:\n case 8:\n case 64:\n return currentTime + 250;\n case 16:\n case 32:\n case 128:\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return currentTime + 5e3;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return -1;\n case 67108864:\n case 134217728:\n case 268435456:\n case 536870912:\n case 1073741824:\n return -1;\n default:\n return -1;\n }\n}\nfunction claimNextRetryLane() {\n var lane = nextRetryLane;\n nextRetryLane <<= 1;\n 0 === (nextRetryLane & 62914560) && (nextRetryLane = 4194304);\n return lane;\n}\nfunction createLaneMap(initial) {\n for (var laneMap = [], i = 0; 31 > i; i++) laneMap.push(initial);\n return laneMap;\n}\nfunction markRootUpdated$1(root, updateLane) {\n root.pendingLanes |= updateLane;\n 268435456 !== updateLane &&\n ((root.suspendedLanes = 0), (root.pingedLanes = 0), (root.warmLanes = 0));\n}\nfunction markRootFinished(\n root,\n finishedLanes,\n remainingLanes,\n spawnedLane,\n updatedLanes,\n suspendedRetryLanes\n) {\n var previouslyPendingLanes = root.pendingLanes;\n root.pendingLanes = remainingLanes;\n root.suspendedLanes = 0;\n root.pingedLanes = 0;\n root.warmLanes = 0;\n root.expiredLanes &= remainingLanes;\n root.entangledLanes &= remainingLanes;\n root.errorRecoveryDisabledLanes &= remainingLanes;\n root.shellSuspendCounter = 0;\n var entanglements = root.entanglements,\n expirationTimes = root.expirationTimes,\n hiddenUpdates = root.hiddenUpdates;\n for (\n remainingLanes = previouslyPendingLanes & ~remainingLanes;\n 0 < remainingLanes;\n\n ) {\n var index$7 = 31 - clz32(remainingLanes),\n lane = 1 << index$7;\n entanglements[index$7] = 0;\n expirationTimes[index$7] = -1;\n var hiddenUpdatesForLane = hiddenUpdates[index$7];\n if (null !== hiddenUpdatesForLane)\n for (\n hiddenUpdates[index$7] = null, index$7 = 0;\n index$7 < hiddenUpdatesForLane.length;\n index$7++\n ) {\n var update = hiddenUpdatesForLane[index$7];\n null !== update && (update.lane &= -536870913);\n }\n remainingLanes &= ~lane;\n }\n 0 !== spawnedLane && markSpawnedDeferredLane(root, spawnedLane, 0);\n 0 !== suspendedRetryLanes &&\n 0 === updatedLanes &&\n 0 !== root.tag &&\n (root.suspendedLanes |=\n suspendedRetryLanes & ~(previouslyPendingLanes & ~finishedLanes));\n}\nfunction markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {\n root.pendingLanes |= spawnedLane;\n root.suspendedLanes &= ~spawnedLane;\n var spawnedLaneIndex = 31 - clz32(spawnedLane);\n root.entangledLanes |= spawnedLane;\n root.entanglements[spawnedLaneIndex] =\n root.entanglements[spawnedLaneIndex] |\n 1073741824 |\n (entangledLanes & 261930);\n}\nfunction markRootEntangled(root, entangledLanes) {\n var rootEntangledLanes = (root.entangledLanes |= entangledLanes);\n for (root = root.entanglements; rootEntangledLanes; ) {\n var index$8 = 31 - clz32(rootEntangledLanes),\n lane = 1 << index$8;\n (lane & entangledLanes) | (root[index$8] & entangledLanes) &&\n (root[index$8] |= entangledLanes);\n rootEntangledLanes &= ~lane;\n }\n}\nfunction getBumpedLaneForHydration(root, renderLanes) {\n var renderLane = renderLanes & -renderLanes;\n renderLane =\n 0 !== (renderLane & 42) ? 1 : getBumpedLaneForHydrationByLane(renderLane);\n return 0 !== (renderLane & (root.suspendedLanes | renderLanes))\n ? 0\n : renderLane;\n}\nfunction getBumpedLaneForHydrationByLane(lane) {\n switch (lane) {\n case 2:\n lane = 1;\n break;\n case 8:\n lane = 4;\n break;\n case 32:\n lane = 16;\n break;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n lane = 128;\n break;\n case 268435456:\n lane = 134217728;\n break;\n default:\n lane = 0;\n }\n return lane;\n}\nfunction lanesToEventPriority(lanes) {\n lanes &= -lanes;\n return 2 < lanes\n ? 8 < lanes\n ? 0 !== (lanes & 134217727)\n ? 32\n : 268435456\n : 8\n : 2;\n}\nfunction resolveUpdatePriority() {\n var updatePriority = ReactDOMSharedInternals.p;\n if (0 !== updatePriority) return updatePriority;\n updatePriority = window.event;\n return void 0 === updatePriority ? 32 : getEventPriority(updatePriority.type);\n}\nfunction runWithPriority(priority, fn) {\n var previousPriority = ReactDOMSharedInternals.p;\n try {\n return (ReactDOMSharedInternals.p = priority), fn();\n } finally {\n ReactDOMSharedInternals.p = previousPriority;\n }\n}\nvar randomKey = Math.random().toString(36).slice(2),\n internalInstanceKey = \"__reactFiber$\" + randomKey,\n internalPropsKey = \"__reactProps$\" + randomKey,\n internalContainerInstanceKey = \"__reactContainer$\" + randomKey,\n internalEventHandlersKey = \"__reactEvents$\" + randomKey,\n internalEventHandlerListenersKey = \"__reactListeners$\" + randomKey,\n internalEventHandlesSetKey = \"__reactHandles$\" + randomKey,\n internalRootNodeResourcesKey = \"__reactResources$\" + randomKey,\n internalHoistableMarker = \"__reactMarker$\" + randomKey;\nfunction detachDeletedInstance(node) {\n delete node[internalInstanceKey];\n delete node[internalPropsKey];\n delete node[internalEventHandlersKey];\n delete node[internalEventHandlerListenersKey];\n delete node[internalEventHandlesSetKey];\n}\nfunction getClosestInstanceFromNode(targetNode) {\n var targetInst = targetNode[internalInstanceKey];\n if (targetInst) return targetInst;\n for (var parentNode = targetNode.parentNode; parentNode; ) {\n if (\n (targetInst =\n parentNode[internalContainerInstanceKey] ||\n parentNode[internalInstanceKey])\n ) {\n parentNode = targetInst.alternate;\n if (\n null !== targetInst.child ||\n (null !== parentNode && null !== parentNode.child)\n )\n for (\n targetNode = getParentHydrationBoundary(targetNode);\n null !== targetNode;\n\n ) {\n if ((parentNode = targetNode[internalInstanceKey])) return parentNode;\n targetNode = getParentHydrationBoundary(targetNode);\n }\n return targetInst;\n }\n targetNode = parentNode;\n parentNode = targetNode.parentNode;\n }\n return null;\n}\nfunction getInstanceFromNode(node) {\n if (\n (node = node[internalInstanceKey] || node[internalContainerInstanceKey])\n ) {\n var tag = node.tag;\n if (\n 5 === tag ||\n 6 === tag ||\n 13 === tag ||\n 31 === tag ||\n 26 === tag ||\n 27 === tag ||\n 3 === tag\n )\n return node;\n }\n return null;\n}\nfunction getNodeFromInstance(inst) {\n var tag = inst.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return inst.stateNode;\n throw Error(formatProdErrorMessage(33));\n}\nfunction getResourcesFromRoot(root) {\n var resources = root[internalRootNodeResourcesKey];\n resources ||\n (resources = root[internalRootNodeResourcesKey] =\n { hoistableStyles: new Map(), hoistableScripts: new Map() });\n return resources;\n}\nfunction markNodeAsHoistable(node) {\n node[internalHoistableMarker] = !0;\n}\nvar allNativeEvents = new Set(),\n registrationNameDependencies = {};\nfunction registerTwoPhaseEvent(registrationName, dependencies) {\n registerDirectEvent(registrationName, dependencies);\n registerDirectEvent(registrationName + \"Capture\", dependencies);\n}\nfunction registerDirectEvent(registrationName, dependencies) {\n registrationNameDependencies[registrationName] = dependencies;\n for (\n registrationName = 0;\n registrationName < dependencies.length;\n registrationName++\n )\n allNativeEvents.add(dependencies[registrationName]);\n}\nvar VALID_ATTRIBUTE_NAME_REGEX = RegExp(\n \"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"\n ),\n illegalAttributeNameCache = {},\n validatedAttributeNameCache = {};\nfunction isAttributeNameSafe(attributeName) {\n if (hasOwnProperty.call(validatedAttributeNameCache, attributeName))\n return !0;\n if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) return !1;\n if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName))\n return (validatedAttributeNameCache[attributeName] = !0);\n illegalAttributeNameCache[attributeName] = !0;\n return !1;\n}\nfunction setValueForAttribute(node, name, value) {\n if (isAttributeNameSafe(name))\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n node.removeAttribute(name);\n return;\n case \"boolean\":\n var prefix$10 = name.toLowerCase().slice(0, 5);\n if (\"data-\" !== prefix$10 && \"aria-\" !== prefix$10) {\n node.removeAttribute(name);\n return;\n }\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForKnownAttribute(node, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForNamespacedAttribute(node, namespace, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttributeNS(namespace, name, \"\" + value);\n }\n}\nfunction getToStringValue(value) {\n switch (typeof value) {\n case \"bigint\":\n case \"boolean\":\n case \"number\":\n case \"string\":\n case \"undefined\":\n return value;\n case \"object\":\n return value;\n default:\n return \"\";\n }\n}\nfunction isCheckable(elem) {\n var type = elem.type;\n return (\n (elem = elem.nodeName) &&\n \"input\" === elem.toLowerCase() &&\n (\"checkbox\" === type || \"radio\" === type)\n );\n}\nfunction trackValueOnNode(node, valueField, currentValue) {\n var descriptor = Object.getOwnPropertyDescriptor(\n node.constructor.prototype,\n valueField\n );\n if (\n !node.hasOwnProperty(valueField) &&\n \"undefined\" !== typeof descriptor &&\n \"function\" === typeof descriptor.get &&\n \"function\" === typeof descriptor.set\n ) {\n var get = descriptor.get,\n set = descriptor.set;\n Object.defineProperty(node, valueField, {\n configurable: !0,\n get: function () {\n return get.call(this);\n },\n set: function (value) {\n currentValue = \"\" + value;\n set.call(this, value);\n }\n });\n Object.defineProperty(node, valueField, {\n enumerable: descriptor.enumerable\n });\n return {\n getValue: function () {\n return currentValue;\n },\n setValue: function (value) {\n currentValue = \"\" + value;\n },\n stopTracking: function () {\n node._valueTracker = null;\n delete node[valueField];\n }\n };\n }\n}\nfunction track(node) {\n if (!node._valueTracker) {\n var valueField = isCheckable(node) ? \"checked\" : \"value\";\n node._valueTracker = trackValueOnNode(\n node,\n valueField,\n \"\" + node[valueField]\n );\n }\n}\nfunction updateValueIfChanged(node) {\n if (!node) return !1;\n var tracker = node._valueTracker;\n if (!tracker) return !0;\n var lastValue = tracker.getValue();\n var value = \"\";\n node &&\n (value = isCheckable(node)\n ? node.checked\n ? \"true\"\n : \"false\"\n : node.value);\n node = value;\n return node !== lastValue ? (tracker.setValue(node), !0) : !1;\n}\nfunction getActiveElement(doc) {\n doc = doc || (\"undefined\" !== typeof document ? document : void 0);\n if (\"undefined\" === typeof doc) return null;\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\nvar escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\\n\"\\\\]/g;\nfunction escapeSelectorAttributeValueInsideDoubleQuotes(value) {\n return value.replace(\n escapeSelectorAttributeValueInsideDoubleQuotesRegex,\n function (ch) {\n return \"\\\\\" + ch.charCodeAt(0).toString(16) + \" \";\n }\n );\n}\nfunction updateInput(\n element,\n value,\n defaultValue,\n lastDefaultValue,\n checked,\n defaultChecked,\n type,\n name\n) {\n element.name = \"\";\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type\n ? (element.type = type)\n : element.removeAttribute(\"type\");\n if (null != value)\n if (\"number\" === type) {\n if ((0 === value && \"\" === element.value) || element.value != value)\n element.value = \"\" + getToStringValue(value);\n } else\n element.value !== \"\" + getToStringValue(value) &&\n (element.value = \"\" + getToStringValue(value));\n else\n (\"submit\" !== type && \"reset\" !== type) || element.removeAttribute(\"value\");\n null != value\n ? setDefaultValue(element, type, getToStringValue(value))\n : null != defaultValue\n ? setDefaultValue(element, type, getToStringValue(defaultValue))\n : null != lastDefaultValue && element.removeAttribute(\"value\");\n null == checked &&\n null != defaultChecked &&\n (element.defaultChecked = !!defaultChecked);\n null != checked &&\n (element.checked =\n checked && \"function\" !== typeof checked && \"symbol\" !== typeof checked);\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name\n ? (element.name = \"\" + getToStringValue(name))\n : element.removeAttribute(\"name\");\n}\nfunction initInput(\n element,\n value,\n defaultValue,\n checked,\n defaultChecked,\n type,\n name,\n isHydrating\n) {\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type &&\n (element.type = type);\n if (null != value || null != defaultValue) {\n if (\n !(\n (\"submit\" !== type && \"reset\" !== type) ||\n (void 0 !== value && null !== value)\n )\n ) {\n track(element);\n return;\n }\n defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n value = null != value ? \"\" + getToStringValue(value) : defaultValue;\n isHydrating || value === element.value || (element.value = value);\n element.defaultValue = value;\n }\n checked = null != checked ? checked : defaultChecked;\n checked =\n \"function\" !== typeof checked && \"symbol\" !== typeof checked && !!checked;\n element.checked = isHydrating ? element.checked : !!checked;\n element.defaultChecked = !!checked;\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name &&\n (element.name = name);\n track(element);\n}\nfunction setDefaultValue(node, type, value) {\n (\"number\" === type && getActiveElement(node.ownerDocument) === node) ||\n node.defaultValue === \"\" + value ||\n (node.defaultValue = \"\" + value);\n}\nfunction updateOptions(node, multiple, propValue, setDefaultSelected) {\n node = node.options;\n if (multiple) {\n multiple = {};\n for (var i = 0; i < propValue.length; i++)\n multiple[\"$\" + propValue[i]] = !0;\n for (propValue = 0; propValue < node.length; propValue++)\n (i = multiple.hasOwnProperty(\"$\" + node[propValue].value)),\n node[propValue].selected !== i && (node[propValue].selected = i),\n i && setDefaultSelected && (node[propValue].defaultSelected = !0);\n } else {\n propValue = \"\" + getToStringValue(propValue);\n multiple = null;\n for (i = 0; i < node.length; i++) {\n if (node[i].value === propValue) {\n node[i].selected = !0;\n setDefaultSelected && (node[i].defaultSelected = !0);\n return;\n }\n null !== multiple || node[i].disabled || (multiple = node[i]);\n }\n null !== multiple && (multiple.selected = !0);\n }\n}\nfunction updateTextarea(element, value, defaultValue) {\n if (\n null != value &&\n ((value = \"\" + getToStringValue(value)),\n value !== element.value && (element.value = value),\n null == defaultValue)\n ) {\n element.defaultValue !== value && (element.defaultValue = value);\n return;\n }\n element.defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n}\nfunction initTextarea(element, value, defaultValue, children) {\n if (null == value) {\n if (null != children) {\n if (null != defaultValue) throw Error(formatProdErrorMessage(92));\n if (isArrayImpl(children)) {\n if (1 < children.length) throw Error(formatProdErrorMessage(93));\n children = children[0];\n }\n defaultValue = children;\n }\n null == defaultValue && (defaultValue = \"\");\n value = defaultValue;\n }\n defaultValue = getToStringValue(value);\n element.defaultValue = defaultValue;\n children = element.textContent;\n children === defaultValue &&\n \"\" !== children &&\n null !== children &&\n (element.value = children);\n track(element);\n}\nfunction setTextContent(node, text) {\n if (text) {\n var firstChild = node.firstChild;\n if (\n firstChild &&\n firstChild === node.lastChild &&\n 3 === firstChild.nodeType\n ) {\n firstChild.nodeValue = text;\n return;\n }\n }\n node.textContent = text;\n}\nvar unitlessNumbers = new Set(\n \"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\n \" \"\n )\n);\nfunction setValueForStyle(style, styleName, value) {\n var isCustomProperty = 0 === styleName.indexOf(\"--\");\n null == value || \"boolean\" === typeof value || \"\" === value\n ? isCustomProperty\n ? style.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (style.cssFloat = \"\")\n : (style[styleName] = \"\")\n : isCustomProperty\n ? style.setProperty(styleName, value)\n : \"number\" !== typeof value ||\n 0 === value ||\n unitlessNumbers.has(styleName)\n ? \"float\" === styleName\n ? (style.cssFloat = value)\n : (style[styleName] = (\"\" + value).trim())\n : (style[styleName] = value + \"px\");\n}\nfunction setValueForStyles(node, styles, prevStyles) {\n if (null != styles && \"object\" !== typeof styles)\n throw Error(formatProdErrorMessage(62));\n node = node.style;\n if (null != prevStyles) {\n for (var styleName in prevStyles)\n !prevStyles.hasOwnProperty(styleName) ||\n (null != styles && styles.hasOwnProperty(styleName)) ||\n (0 === styleName.indexOf(\"--\")\n ? node.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (node.cssFloat = \"\")\n : (node[styleName] = \"\"));\n for (var styleName$16 in styles)\n (styleName = styles[styleName$16]),\n styles.hasOwnProperty(styleName$16) &&\n prevStyles[styleName$16] !== styleName &&\n setValueForStyle(node, styleName$16, styleName);\n } else\n for (var styleName$17 in styles)\n styles.hasOwnProperty(styleName$17) &&\n setValueForStyle(node, styleName$17, styles[styleName$17]);\n}\nfunction isCustomElement(tagName) {\n if (-1 === tagName.indexOf(\"-\")) return !1;\n switch (tagName) {\n case \"annotation-xml\":\n case \"color-profile\":\n case \"font-face\":\n case \"font-face-src\":\n case \"font-face-uri\":\n case \"font-face-format\":\n case \"font-face-name\":\n case \"missing-glyph\":\n return !1;\n default:\n return !0;\n }\n}\nvar aliases = new Map([\n [\"acceptCharset\", \"accept-charset\"],\n [\"htmlFor\", \"for\"],\n [\"httpEquiv\", \"http-equiv\"],\n [\"crossOrigin\", \"crossorigin\"],\n [\"accentHeight\", \"accent-height\"],\n [\"alignmentBaseline\", \"alignment-baseline\"],\n [\"arabicForm\", \"arabic-form\"],\n [\"baselineShift\", \"baseline-shift\"],\n [\"capHeight\", \"cap-height\"],\n [\"clipPath\", \"clip-path\"],\n [\"clipRule\", \"clip-rule\"],\n [\"colorInterpolation\", \"color-interpolation\"],\n [\"colorInterpolationFilters\", \"color-interpolation-filters\"],\n [\"colorProfile\", \"color-profile\"],\n [\"colorRendering\", \"color-rendering\"],\n [\"dominantBaseline\", \"dominant-baseline\"],\n [\"enableBackground\", \"enable-background\"],\n [\"fillOpacity\", \"fill-opacity\"],\n [\"fillRule\", \"fill-rule\"],\n [\"floodColor\", \"flood-color\"],\n [\"floodOpacity\", \"flood-opacity\"],\n [\"fontFamily\", \"font-family\"],\n [\"fontSize\", \"font-size\"],\n [\"fontSizeAdjust\", \"font-size-adjust\"],\n [\"fontStretch\", \"font-stretch\"],\n [\"fontStyle\", \"font-style\"],\n [\"fontVariant\", \"font-variant\"],\n [\"fontWeight\", \"font-weight\"],\n [\"glyphName\", \"glyph-name\"],\n [\"glyphOrientationHorizontal\", \"glyph-orientation-horizontal\"],\n [\"glyphOrientationVertical\", \"glyph-orientation-vertical\"],\n [\"horizAdvX\", \"horiz-adv-x\"],\n [\"horizOriginX\", \"horiz-origin-x\"],\n [\"imageRendering\", \"image-rendering\"],\n [\"letterSpacing\", \"letter-spacing\"],\n [\"lightingColor\", \"lighting-color\"],\n [\"markerEnd\", \"marker-end\"],\n [\"markerMid\", \"marker-mid\"],\n [\"markerStart\", \"marker-start\"],\n [\"overlinePosition\", \"overline-position\"],\n [\"overlineThickness\", \"overline-thickness\"],\n [\"paintOrder\", \"paint-order\"],\n [\"panose-1\", \"panose-1\"],\n [\"pointerEvents\", \"pointer-events\"],\n [\"renderingIntent\", \"rendering-intent\"],\n [\"shapeRendering\", \"shape-rendering\"],\n [\"stopColor\", \"stop-color\"],\n [\"stopOpacity\", \"stop-opacity\"],\n [\"strikethroughPosition\", \"strikethrough-position\"],\n [\"strikethroughThickness\", \"strikethrough-thickness\"],\n [\"strokeDasharray\", \"stroke-dasharray\"],\n [\"strokeDashoffset\", \"stroke-dashoffset\"],\n [\"strokeLinecap\", \"stroke-linecap\"],\n [\"strokeLinejoin\", \"stroke-linejoin\"],\n [\"strokeMiterlimit\", \"stroke-miterlimit\"],\n [\"strokeOpacity\", \"stroke-opacity\"],\n [\"strokeWidth\", \"stroke-width\"],\n [\"textAnchor\", \"text-anchor\"],\n [\"textDecoration\", \"text-decoration\"],\n [\"textRendering\", \"text-rendering\"],\n [\"transformOrigin\", \"transform-origin\"],\n [\"underlinePosition\", \"underline-position\"],\n [\"underlineThickness\", \"underline-thickness\"],\n [\"unicodeBidi\", \"unicode-bidi\"],\n [\"unicodeRange\", \"unicode-range\"],\n [\"unitsPerEm\", \"units-per-em\"],\n [\"vAlphabetic\", \"v-alphabetic\"],\n [\"vHanging\", \"v-hanging\"],\n [\"vIdeographic\", \"v-ideographic\"],\n [\"vMathematical\", \"v-mathematical\"],\n [\"vectorEffect\", \"vector-effect\"],\n [\"vertAdvY\", \"vert-adv-y\"],\n [\"vertOriginX\", \"vert-origin-x\"],\n [\"vertOriginY\", \"vert-origin-y\"],\n [\"wordSpacing\", \"word-spacing\"],\n [\"writingMode\", \"writing-mode\"],\n [\"xmlnsXlink\", \"xmlns:xlink\"],\n [\"xHeight\", \"x-height\"]\n ]),\n isJavaScriptProtocol =\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;\nfunction sanitizeURL(url) {\n return isJavaScriptProtocol.test(\"\" + url)\n ? \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\"\n : url;\n}\nfunction noop$1() {}\nvar currentReplayingEvent = null;\nfunction getEventTarget(nativeEvent) {\n nativeEvent = nativeEvent.target || nativeEvent.srcElement || window;\n nativeEvent.correspondingUseElement &&\n (nativeEvent = nativeEvent.correspondingUseElement);\n return 3 === nativeEvent.nodeType ? nativeEvent.parentNode : nativeEvent;\n}\nvar restoreTarget = null,\n restoreQueue = null;\nfunction restoreStateOfTarget(target) {\n var internalInstance = getInstanceFromNode(target);\n if (internalInstance && (target = internalInstance.stateNode)) {\n var props = target[internalPropsKey] || null;\n a: switch (((target = internalInstance.stateNode), internalInstance.type)) {\n case \"input\":\n updateInput(\n target,\n props.value,\n props.defaultValue,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name\n );\n internalInstance = props.name;\n if (\"radio\" === props.type && null != internalInstance) {\n for (props = target; props.parentNode; ) props = props.parentNode;\n props = props.querySelectorAll(\n 'input[name=\"' +\n escapeSelectorAttributeValueInsideDoubleQuotes(\n \"\" + internalInstance\n ) +\n '\"][type=\"radio\"]'\n );\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n ) {\n var otherNode = props[internalInstance];\n if (otherNode !== target && otherNode.form === target.form) {\n var otherProps = otherNode[internalPropsKey] || null;\n if (!otherProps) throw Error(formatProdErrorMessage(90));\n updateInput(\n otherNode,\n otherProps.value,\n otherProps.defaultValue,\n otherProps.defaultValue,\n otherProps.checked,\n otherProps.defaultChecked,\n otherProps.type,\n otherProps.name\n );\n }\n }\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n )\n (otherNode = props[internalInstance]),\n otherNode.form === target.form && updateValueIfChanged(otherNode);\n }\n break a;\n case \"textarea\":\n updateTextarea(target, props.value, props.defaultValue);\n break a;\n case \"select\":\n (internalInstance = props.value),\n null != internalInstance &&\n updateOptions(target, !!props.multiple, internalInstance, !1);\n }\n }\n}\nvar isInsideEventHandler = !1;\nfunction batchedUpdates$1(fn, a, b) {\n if (isInsideEventHandler) return fn(a, b);\n isInsideEventHandler = !0;\n try {\n var JSCompiler_inline_result = fn(a);\n return JSCompiler_inline_result;\n } finally {\n if (\n ((isInsideEventHandler = !1),\n null !== restoreTarget || null !== restoreQueue)\n )\n if (\n (flushSyncWork$1(),\n restoreTarget &&\n ((a = restoreTarget),\n (fn = restoreQueue),\n (restoreQueue = restoreTarget = null),\n restoreStateOfTarget(a),\n fn))\n )\n for (a = 0; a < fn.length; a++) restoreStateOfTarget(fn[a]);\n }\n}\nfunction getListener(inst, registrationName) {\n var stateNode = inst.stateNode;\n if (null === stateNode) return null;\n var props = stateNode[internalPropsKey] || null;\n if (null === props) return null;\n stateNode = props[registrationName];\n a: switch (registrationName) {\n case \"onClick\":\n case \"onClickCapture\":\n case \"onDoubleClick\":\n case \"onDoubleClickCapture\":\n case \"onMouseDown\":\n case \"onMouseDownCapture\":\n case \"onMouseMove\":\n case \"onMouseMoveCapture\":\n case \"onMouseUp\":\n case \"onMouseUpCapture\":\n case \"onMouseEnter\":\n (props = !props.disabled) ||\n ((inst = inst.type),\n (props = !(\n \"button\" === inst ||\n \"input\" === inst ||\n \"select\" === inst ||\n \"textarea\" === inst\n )));\n inst = !props;\n break a;\n default:\n inst = !1;\n }\n if (inst) return null;\n if (stateNode && \"function\" !== typeof stateNode)\n throw Error(\n formatProdErrorMessage(231, registrationName, typeof stateNode)\n );\n return stateNode;\n}\nvar canUseDOM = !(\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ),\n passiveBrowserEventsSupported = !1;\nif (canUseDOM)\n try {\n var options = {};\n Object.defineProperty(options, \"passive\", {\n get: function () {\n passiveBrowserEventsSupported = !0;\n }\n });\n window.addEventListener(\"test\", options, options);\n window.removeEventListener(\"test\", options, options);\n } catch (e) {\n passiveBrowserEventsSupported = !1;\n }\nvar root = null,\n startText = null,\n fallbackText = null;\nfunction getData() {\n if (fallbackText) return fallbackText;\n var start,\n startValue = startText,\n startLength = startValue.length,\n end,\n endValue = \"value\" in root ? root.value : root.textContent,\n endLength = endValue.length;\n for (\n start = 0;\n start < startLength && startValue[start] === endValue[start];\n start++\n );\n var minEnd = startLength - start;\n for (\n end = 1;\n end <= minEnd &&\n startValue[startLength - end] === endValue[endLength - end];\n end++\n );\n return (fallbackText = endValue.slice(start, 1 < end ? 1 - end : void 0));\n}\nfunction getEventCharCode(nativeEvent) {\n var keyCode = nativeEvent.keyCode;\n \"charCode\" in nativeEvent\n ? ((nativeEvent = nativeEvent.charCode),\n 0 === nativeEvent && 13 === keyCode && (nativeEvent = 13))\n : (nativeEvent = keyCode);\n 10 === nativeEvent && (nativeEvent = 13);\n return 32 <= nativeEvent || 13 === nativeEvent ? nativeEvent : 0;\n}\nfunction functionThatReturnsTrue() {\n return !0;\n}\nfunction functionThatReturnsFalse() {\n return !1;\n}\nfunction createSyntheticEvent(Interface) {\n function SyntheticBaseEvent(\n reactName,\n reactEventType,\n targetInst,\n nativeEvent,\n nativeEventTarget\n ) {\n this._reactName = reactName;\n this._targetInst = targetInst;\n this.type = reactEventType;\n this.nativeEvent = nativeEvent;\n this.target = nativeEventTarget;\n this.currentTarget = null;\n for (var propName in Interface)\n Interface.hasOwnProperty(propName) &&\n ((reactName = Interface[propName]),\n (this[propName] = reactName\n ? reactName(nativeEvent)\n : nativeEvent[propName]));\n this.isDefaultPrevented = (\n null != nativeEvent.defaultPrevented\n ? nativeEvent.defaultPrevented\n : !1 === nativeEvent.returnValue\n )\n ? functionThatReturnsTrue\n : functionThatReturnsFalse;\n this.isPropagationStopped = functionThatReturnsFalse;\n return this;\n }\n assign(SyntheticBaseEvent.prototype, {\n preventDefault: function () {\n this.defaultPrevented = !0;\n var event = this.nativeEvent;\n event &&\n (event.preventDefault\n ? event.preventDefault()\n : \"unknown\" !== typeof event.returnValue && (event.returnValue = !1),\n (this.isDefaultPrevented = functionThatReturnsTrue));\n },\n stopPropagation: function () {\n var event = this.nativeEvent;\n event &&\n (event.stopPropagation\n ? event.stopPropagation()\n : \"unknown\" !== typeof event.cancelBubble &&\n (event.cancelBubble = !0),\n (this.isPropagationStopped = functionThatReturnsTrue));\n },\n persist: function () {},\n isPersistent: functionThatReturnsTrue\n });\n return SyntheticBaseEvent;\n}\nvar EventInterface = {\n eventPhase: 0,\n bubbles: 0,\n cancelable: 0,\n timeStamp: function (event) {\n return event.timeStamp || Date.now();\n },\n defaultPrevented: 0,\n isTrusted: 0\n },\n SyntheticEvent = createSyntheticEvent(EventInterface),\n UIEventInterface = assign({}, EventInterface, { view: 0, detail: 0 }),\n SyntheticUIEvent = createSyntheticEvent(UIEventInterface),\n lastMovementX,\n lastMovementY,\n lastMouseEvent,\n MouseEventInterface = assign({}, UIEventInterface, {\n screenX: 0,\n screenY: 0,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n getModifierState: getEventModifierState,\n button: 0,\n buttons: 0,\n relatedTarget: function (event) {\n return void 0 === event.relatedTarget\n ? event.fromElement === event.srcElement\n ? event.toElement\n : event.fromElement\n : event.relatedTarget;\n },\n movementX: function (event) {\n if (\"movementX\" in event) return event.movementX;\n event !== lastMouseEvent &&\n (lastMouseEvent && \"mousemove\" === event.type\n ? ((lastMovementX = event.screenX - lastMouseEvent.screenX),\n (lastMovementY = event.screenY - lastMouseEvent.screenY))\n : (lastMovementY = lastMovementX = 0),\n (lastMouseEvent = event));\n return lastMovementX;\n },\n movementY: function (event) {\n return \"movementY\" in event ? event.movementY : lastMovementY;\n }\n }),\n SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface),\n DragEventInterface = assign({}, MouseEventInterface, { dataTransfer: 0 }),\n SyntheticDragEvent = createSyntheticEvent(DragEventInterface),\n FocusEventInterface = assign({}, UIEventInterface, { relatedTarget: 0 }),\n SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface),\n AnimationEventInterface = assign({}, EventInterface, {\n animationName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface),\n ClipboardEventInterface = assign({}, EventInterface, {\n clipboardData: function (event) {\n return \"clipboardData\" in event\n ? event.clipboardData\n : window.clipboardData;\n }\n }),\n SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface),\n CompositionEventInterface = assign({}, EventInterface, { data: 0 }),\n SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface),\n normalizeKey = {\n Esc: \"Escape\",\n Spacebar: \" \",\n Left: \"ArrowLeft\",\n Up: \"ArrowUp\",\n Right: \"ArrowRight\",\n Down: \"ArrowDown\",\n Del: \"Delete\",\n Win: \"OS\",\n Menu: \"ContextMenu\",\n Apps: \"ContextMenu\",\n Scroll: \"ScrollLock\",\n MozPrintableKey: \"Unidentified\"\n },\n translateToKey = {\n 8: \"Backspace\",\n 9: \"Tab\",\n 12: \"Clear\",\n 13: \"Enter\",\n 16: \"Shift\",\n 17: \"Control\",\n 18: \"Alt\",\n 19: \"Pause\",\n 20: \"CapsLock\",\n 27: \"Escape\",\n 32: \" \",\n 33: \"PageUp\",\n 34: \"PageDown\",\n 35: \"End\",\n 36: \"Home\",\n 37: \"ArrowLeft\",\n 38: \"ArrowUp\",\n 39: \"ArrowRight\",\n 40: \"ArrowDown\",\n 45: \"Insert\",\n 46: \"Delete\",\n 112: \"F1\",\n 113: \"F2\",\n 114: \"F3\",\n 115: \"F4\",\n 116: \"F5\",\n 117: \"F6\",\n 118: \"F7\",\n 119: \"F8\",\n 120: \"F9\",\n 121: \"F10\",\n 122: \"F11\",\n 123: \"F12\",\n 144: \"NumLock\",\n 145: \"ScrollLock\",\n 224: \"Meta\"\n },\n modifierKeyToProp = {\n Alt: \"altKey\",\n Control: \"ctrlKey\",\n Meta: \"metaKey\",\n Shift: \"shiftKey\"\n };\nfunction modifierStateGetter(keyArg) {\n var nativeEvent = this.nativeEvent;\n return nativeEvent.getModifierState\n ? nativeEvent.getModifierState(keyArg)\n : (keyArg = modifierKeyToProp[keyArg])\n ? !!nativeEvent[keyArg]\n : !1;\n}\nfunction getEventModifierState() {\n return modifierStateGetter;\n}\nvar KeyboardEventInterface = assign({}, UIEventInterface, {\n key: function (nativeEvent) {\n if (nativeEvent.key) {\n var key = normalizeKey[nativeEvent.key] || nativeEvent.key;\n if (\"Unidentified\" !== key) return key;\n }\n return \"keypress\" === nativeEvent.type\n ? ((nativeEvent = getEventCharCode(nativeEvent)),\n 13 === nativeEvent ? \"Enter\" : String.fromCharCode(nativeEvent))\n : \"keydown\" === nativeEvent.type || \"keyup\" === nativeEvent.type\n ? translateToKey[nativeEvent.keyCode] || \"Unidentified\"\n : \"\";\n },\n code: 0,\n location: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n repeat: 0,\n locale: 0,\n getModifierState: getEventModifierState,\n charCode: function (event) {\n return \"keypress\" === event.type ? getEventCharCode(event) : 0;\n },\n keyCode: function (event) {\n return \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n },\n which: function (event) {\n return \"keypress\" === event.type\n ? getEventCharCode(event)\n : \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n }\n }),\n SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface),\n PointerEventInterface = assign({}, MouseEventInterface, {\n pointerId: 0,\n width: 0,\n height: 0,\n pressure: 0,\n tangentialPressure: 0,\n tiltX: 0,\n tiltY: 0,\n twist: 0,\n pointerType: 0,\n isPrimary: 0\n }),\n SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface),\n TouchEventInterface = assign({}, UIEventInterface, {\n touches: 0,\n targetTouches: 0,\n changedTouches: 0,\n altKey: 0,\n metaKey: 0,\n ctrlKey: 0,\n shiftKey: 0,\n getModifierState: getEventModifierState\n }),\n SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface),\n TransitionEventInterface = assign({}, EventInterface, {\n propertyName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface),\n WheelEventInterface = assign({}, MouseEventInterface, {\n deltaX: function (event) {\n return \"deltaX\" in event\n ? event.deltaX\n : \"wheelDeltaX\" in event\n ? -event.wheelDeltaX\n : 0;\n },\n deltaY: function (event) {\n return \"deltaY\" in event\n ? event.deltaY\n : \"wheelDeltaY\" in event\n ? -event.wheelDeltaY\n : \"wheelDelta\" in event\n ? -event.wheelDelta\n : 0;\n },\n deltaZ: 0,\n deltaMode: 0\n }),\n SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface),\n ToggleEventInterface = assign({}, EventInterface, {\n newState: 0,\n oldState: 0\n }),\n SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface),\n END_KEYCODES = [9, 13, 27, 32],\n canUseCompositionEvent = canUseDOM && \"CompositionEvent\" in window,\n documentMode = null;\ncanUseDOM &&\n \"documentMode\" in document &&\n (documentMode = document.documentMode);\nvar canUseTextInputEvent = canUseDOM && \"TextEvent\" in window && !documentMode,\n useFallbackCompositionData =\n canUseDOM &&\n (!canUseCompositionEvent ||\n (documentMode && 8 < documentMode && 11 >= documentMode)),\n SPACEBAR_CHAR = String.fromCharCode(32),\n hasSpaceKeypress = !1;\nfunction isFallbackCompositionEnd(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"keyup\":\n return -1 !== END_KEYCODES.indexOf(nativeEvent.keyCode);\n case \"keydown\":\n return 229 !== nativeEvent.keyCode;\n case \"keypress\":\n case \"mousedown\":\n case \"focusout\":\n return !0;\n default:\n return !1;\n }\n}\nfunction getDataFromCustomEvent(nativeEvent) {\n nativeEvent = nativeEvent.detail;\n return \"object\" === typeof nativeEvent && \"data\" in nativeEvent\n ? nativeEvent.data\n : null;\n}\nvar isComposing = !1;\nfunction getNativeBeforeInputChars(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"compositionend\":\n return getDataFromCustomEvent(nativeEvent);\n case \"keypress\":\n if (32 !== nativeEvent.which) return null;\n hasSpaceKeypress = !0;\n return SPACEBAR_CHAR;\n case \"textInput\":\n return (\n (domEventName = nativeEvent.data),\n domEventName === SPACEBAR_CHAR && hasSpaceKeypress ? null : domEventName\n );\n default:\n return null;\n }\n}\nfunction getFallbackBeforeInputChars(domEventName, nativeEvent) {\n if (isComposing)\n return \"compositionend\" === domEventName ||\n (!canUseCompositionEvent &&\n isFallbackCompositionEnd(domEventName, nativeEvent))\n ? ((domEventName = getData()),\n (fallbackText = startText = root = null),\n (isComposing = !1),\n domEventName)\n : null;\n switch (domEventName) {\n case \"paste\":\n return null;\n case \"keypress\":\n if (\n !(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) ||\n (nativeEvent.ctrlKey && nativeEvent.altKey)\n ) {\n if (nativeEvent.char && 1 < nativeEvent.char.length)\n return nativeEvent.char;\n if (nativeEvent.which) return String.fromCharCode(nativeEvent.which);\n }\n return null;\n case \"compositionend\":\n return useFallbackCompositionData && \"ko\" !== nativeEvent.locale\n ? null\n : nativeEvent.data;\n default:\n return null;\n }\n}\nvar supportedInputTypes = {\n color: !0,\n date: !0,\n datetime: !0,\n \"datetime-local\": !0,\n email: !0,\n month: !0,\n number: !0,\n password: !0,\n range: !0,\n search: !0,\n tel: !0,\n text: !0,\n time: !0,\n url: !0,\n week: !0\n};\nfunction isTextInputElement(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return \"input\" === nodeName\n ? !!supportedInputTypes[elem.type]\n : \"textarea\" === nodeName\n ? !0\n : !1;\n}\nfunction createAndAccumulateChangeEvent(\n dispatchQueue,\n inst,\n nativeEvent,\n target\n) {\n restoreTarget\n ? restoreQueue\n ? restoreQueue.push(target)\n : (restoreQueue = [target])\n : (restoreTarget = target);\n inst = accumulateTwoPhaseListeners(inst, \"onChange\");\n 0 < inst.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onChange\",\n \"change\",\n null,\n nativeEvent,\n target\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: inst }));\n}\nvar activeElement$1 = null,\n activeElementInst$1 = null;\nfunction runEventInBatch(dispatchQueue) {\n processDispatchQueue(dispatchQueue, 0);\n}\nfunction getInstIfValueChanged(targetInst) {\n var targetNode = getNodeFromInstance(targetInst);\n if (updateValueIfChanged(targetNode)) return targetInst;\n}\nfunction getTargetInstForChangeEvent(domEventName, targetInst) {\n if (\"change\" === domEventName) return targetInst;\n}\nvar isInputEventSupported = !1;\nif (canUseDOM) {\n var JSCompiler_inline_result$jscomp$286;\n if (canUseDOM) {\n var isSupported$jscomp$inline_427 = \"oninput\" in document;\n if (!isSupported$jscomp$inline_427) {\n var element$jscomp$inline_428 = document.createElement(\"div\");\n element$jscomp$inline_428.setAttribute(\"oninput\", \"return;\");\n isSupported$jscomp$inline_427 =\n \"function\" === typeof element$jscomp$inline_428.oninput;\n }\n JSCompiler_inline_result$jscomp$286 = isSupported$jscomp$inline_427;\n } else JSCompiler_inline_result$jscomp$286 = !1;\n isInputEventSupported =\n JSCompiler_inline_result$jscomp$286 &&\n (!document.documentMode || 9 < document.documentMode);\n}\nfunction stopWatchingForValueChange() {\n activeElement$1 &&\n (activeElement$1.detachEvent(\"onpropertychange\", handlePropertyChange),\n (activeElementInst$1 = activeElement$1 = null));\n}\nfunction handlePropertyChange(nativeEvent) {\n if (\n \"value\" === nativeEvent.propertyName &&\n getInstIfValueChanged(activeElementInst$1)\n ) {\n var dispatchQueue = [];\n createAndAccumulateChangeEvent(\n dispatchQueue,\n activeElementInst$1,\n nativeEvent,\n getEventTarget(nativeEvent)\n );\n batchedUpdates$1(runEventInBatch, dispatchQueue);\n }\n}\nfunction handleEventsForInputEventPolyfill(domEventName, target, targetInst) {\n \"focusin\" === domEventName\n ? (stopWatchingForValueChange(),\n (activeElement$1 = target),\n (activeElementInst$1 = targetInst),\n activeElement$1.attachEvent(\"onpropertychange\", handlePropertyChange))\n : \"focusout\" === domEventName && stopWatchingForValueChange();\n}\nfunction getTargetInstForInputEventPolyfill(domEventName) {\n if (\n \"selectionchange\" === domEventName ||\n \"keyup\" === domEventName ||\n \"keydown\" === domEventName\n )\n return getInstIfValueChanged(activeElementInst$1);\n}\nfunction getTargetInstForClickEvent(domEventName, targetInst) {\n if (\"click\" === domEventName) return getInstIfValueChanged(targetInst);\n}\nfunction getTargetInstForInputOrChangeEvent(domEventName, targetInst) {\n if (\"input\" === domEventName || \"change\" === domEventName)\n return getInstIfValueChanged(targetInst);\n}\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is;\nfunction shallowEqual(objA, objB) {\n if (objectIs(objA, objB)) return !0;\n if (\n \"object\" !== typeof objA ||\n null === objA ||\n \"object\" !== typeof objB ||\n null === objB\n )\n return !1;\n var keysA = Object.keys(objA),\n keysB = Object.keys(objB);\n if (keysA.length !== keysB.length) return !1;\n for (keysB = 0; keysB < keysA.length; keysB++) {\n var currentKey = keysA[keysB];\n if (\n !hasOwnProperty.call(objB, currentKey) ||\n !objectIs(objA[currentKey], objB[currentKey])\n )\n return !1;\n }\n return !0;\n}\nfunction getLeafNode(node) {\n for (; node && node.firstChild; ) node = node.firstChild;\n return node;\n}\nfunction getNodeForCharacterOffset(root, offset) {\n var node = getLeafNode(root);\n root = 0;\n for (var nodeEnd; node; ) {\n if (3 === node.nodeType) {\n nodeEnd = root + node.textContent.length;\n if (root <= offset && nodeEnd >= offset)\n return { node: node, offset: offset - root };\n root = nodeEnd;\n }\n a: {\n for (; node; ) {\n if (node.nextSibling) {\n node = node.nextSibling;\n break a;\n }\n node = node.parentNode;\n }\n node = void 0;\n }\n node = getLeafNode(node);\n }\n}\nfunction containsNode(outerNode, innerNode) {\n return outerNode && innerNode\n ? outerNode === innerNode\n ? !0\n : outerNode && 3 === outerNode.nodeType\n ? !1\n : innerNode && 3 === innerNode.nodeType\n ? containsNode(outerNode, innerNode.parentNode)\n : \"contains\" in outerNode\n ? outerNode.contains(innerNode)\n : outerNode.compareDocumentPosition\n ? !!(outerNode.compareDocumentPosition(innerNode) & 16)\n : !1\n : !1;\n}\nfunction getActiveElementDeep(containerInfo) {\n containerInfo =\n null != containerInfo &&\n null != containerInfo.ownerDocument &&\n null != containerInfo.ownerDocument.defaultView\n ? containerInfo.ownerDocument.defaultView\n : window;\n for (\n var element = getActiveElement(containerInfo.document);\n element instanceof containerInfo.HTMLIFrameElement;\n\n ) {\n try {\n var JSCompiler_inline_result =\n \"string\" === typeof element.contentWindow.location.href;\n } catch (err) {\n JSCompiler_inline_result = !1;\n }\n if (JSCompiler_inline_result) containerInfo = element.contentWindow;\n else break;\n element = getActiveElement(containerInfo.document);\n }\n return element;\n}\nfunction hasSelectionCapabilities(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return (\n nodeName &&\n ((\"input\" === nodeName &&\n (\"text\" === elem.type ||\n \"search\" === elem.type ||\n \"tel\" === elem.type ||\n \"url\" === elem.type ||\n \"password\" === elem.type)) ||\n \"textarea\" === nodeName ||\n \"true\" === elem.contentEditable)\n );\n}\nvar skipSelectionChangeEvent =\n canUseDOM && \"documentMode\" in document && 11 >= document.documentMode,\n activeElement = null,\n activeElementInst = null,\n lastSelection = null,\n mouseDown = !1;\nfunction constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {\n var doc =\n nativeEventTarget.window === nativeEventTarget\n ? nativeEventTarget.document\n : 9 === nativeEventTarget.nodeType\n ? nativeEventTarget\n : nativeEventTarget.ownerDocument;\n mouseDown ||\n null == activeElement ||\n activeElement !== getActiveElement(doc) ||\n ((doc = activeElement),\n \"selectionStart\" in doc && hasSelectionCapabilities(doc)\n ? (doc = { start: doc.selectionStart, end: doc.selectionEnd })\n : ((doc = (\n (doc.ownerDocument && doc.ownerDocument.defaultView) ||\n window\n ).getSelection()),\n (doc = {\n anchorNode: doc.anchorNode,\n anchorOffset: doc.anchorOffset,\n focusNode: doc.focusNode,\n focusOffset: doc.focusOffset\n })),\n (lastSelection && shallowEqual(lastSelection, doc)) ||\n ((lastSelection = doc),\n (doc = accumulateTwoPhaseListeners(activeElementInst, \"onSelect\")),\n 0 < doc.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onSelect\",\n \"select\",\n null,\n nativeEvent,\n nativeEventTarget\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: doc }),\n (nativeEvent.target = activeElement))));\n}\nfunction makePrefixMap(styleProp, eventName) {\n var prefixes = {};\n prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();\n prefixes[\"Webkit\" + styleProp] = \"webkit\" + eventName;\n prefixes[\"Moz\" + styleProp] = \"moz\" + eventName;\n return prefixes;\n}\nvar vendorPrefixes = {\n animationend: makePrefixMap(\"Animation\", \"AnimationEnd\"),\n animationiteration: makePrefixMap(\"Animation\", \"AnimationIteration\"),\n animationstart: makePrefixMap(\"Animation\", \"AnimationStart\"),\n transitionrun: makePrefixMap(\"Transition\", \"TransitionRun\"),\n transitionstart: makePrefixMap(\"Transition\", \"TransitionStart\"),\n transitioncancel: makePrefixMap(\"Transition\", \"TransitionCancel\"),\n transitionend: makePrefixMap(\"Transition\", \"TransitionEnd\")\n },\n prefixedEventNames = {},\n style = {};\ncanUseDOM &&\n ((style = document.createElement(\"div\").style),\n \"AnimationEvent\" in window ||\n (delete vendorPrefixes.animationend.animation,\n delete vendorPrefixes.animationiteration.animation,\n delete vendorPrefixes.animationstart.animation),\n \"TransitionEvent\" in window ||\n delete vendorPrefixes.transitionend.transition);\nfunction getVendorPrefixedEventName(eventName) {\n if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];\n if (!vendorPrefixes[eventName]) return eventName;\n var prefixMap = vendorPrefixes[eventName],\n styleProp;\n for (styleProp in prefixMap)\n if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)\n return (prefixedEventNames[eventName] = prefixMap[styleProp]);\n return eventName;\n}\nvar ANIMATION_END = getVendorPrefixedEventName(\"animationend\"),\n ANIMATION_ITERATION = getVendorPrefixedEventName(\"animationiteration\"),\n ANIMATION_START = getVendorPrefixedEventName(\"animationstart\"),\n TRANSITION_RUN = getVendorPrefixedEventName(\"transitionrun\"),\n TRANSITION_START = getVendorPrefixedEventName(\"transitionstart\"),\n TRANSITION_CANCEL = getVendorPrefixedEventName(\"transitioncancel\"),\n TRANSITION_END = getVendorPrefixedEventName(\"transitionend\"),\n topLevelEventsToReactNames = new Map(),\n simpleEventPluginEvents =\n \"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\n \" \"\n );\nsimpleEventPluginEvents.push(\"scrollEnd\");\nfunction registerSimpleEvent(domEventName, reactName) {\n topLevelEventsToReactNames.set(domEventName, reactName);\n registerTwoPhaseEvent(reactName, [domEventName]);\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n concurrentQueues = [],\n concurrentQueuesIndex = 0,\n concurrentlyUpdatedLanes = 0;\nfunction finishQueueingConcurrentUpdates() {\n for (\n var endIndex = concurrentQueuesIndex,\n i = (concurrentlyUpdatedLanes = concurrentQueuesIndex = 0);\n i < endIndex;\n\n ) {\n var fiber = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var queue = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var update = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var lane = concurrentQueues[i];\n concurrentQueues[i++] = null;\n if (null !== queue && null !== update) {\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n }\n 0 !== lane && markUpdateLaneFromFiberToRoot(fiber, update, lane);\n }\n}\nfunction enqueueUpdate$1(fiber, queue, update, lane) {\n concurrentQueues[concurrentQueuesIndex++] = fiber;\n concurrentQueues[concurrentQueuesIndex++] = queue;\n concurrentQueues[concurrentQueuesIndex++] = update;\n concurrentQueues[concurrentQueuesIndex++] = lane;\n concurrentlyUpdatedLanes |= lane;\n fiber.lanes |= lane;\n fiber = fiber.alternate;\n null !== fiber && (fiber.lanes |= lane);\n}\nfunction enqueueConcurrentHookUpdate(fiber, queue, update, lane) {\n enqueueUpdate$1(fiber, queue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction enqueueConcurrentRenderForLane(fiber, lane) {\n enqueueUpdate$1(fiber, null, null, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {\n sourceFiber.lanes |= lane;\n var alternate = sourceFiber.alternate;\n null !== alternate && (alternate.lanes |= lane);\n for (var isHidden = !1, parent = sourceFiber.return; null !== parent; )\n (parent.childLanes |= lane),\n (alternate = parent.alternate),\n null !== alternate && (alternate.childLanes |= lane),\n 22 === parent.tag &&\n ((sourceFiber = parent.stateNode),\n null === sourceFiber || sourceFiber._visibility & 1 || (isHidden = !0)),\n (sourceFiber = parent),\n (parent = parent.return);\n return 3 === sourceFiber.tag\n ? ((parent = sourceFiber.stateNode),\n isHidden &&\n null !== update &&\n ((isHidden = 31 - clz32(lane)),\n (sourceFiber = parent.hiddenUpdates),\n (alternate = sourceFiber[isHidden]),\n null === alternate\n ? (sourceFiber[isHidden] = [update])\n : alternate.push(update),\n (update.lane = lane | 536870912)),\n parent)\n : null;\n}\nfunction getRootForUpdatedFiber(sourceFiber) {\n if (50 < nestedUpdateCount)\n throw (\n ((nestedUpdateCount = 0),\n (rootWithNestedUpdates = null),\n Error(formatProdErrorMessage(185)))\n );\n for (var parent = sourceFiber.return; null !== parent; )\n (sourceFiber = parent), (parent = sourceFiber.return);\n return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;\n}\nvar emptyContextObject = {};\nfunction FiberNode(tag, pendingProps, key, mode) {\n this.tag = tag;\n this.key = key;\n this.sibling =\n this.child =\n this.return =\n this.stateNode =\n this.type =\n this.elementType =\n null;\n this.index = 0;\n this.refCleanup = this.ref = null;\n this.pendingProps = pendingProps;\n this.dependencies =\n this.memoizedState =\n this.updateQueue =\n this.memoizedProps =\n null;\n this.mode = mode;\n this.subtreeFlags = this.flags = 0;\n this.deletions = null;\n this.childLanes = this.lanes = 0;\n this.alternate = null;\n}\nfunction createFiberImplClass(tag, pendingProps, key, mode) {\n return new FiberNode(tag, pendingProps, key, mode);\n}\nfunction shouldConstruct(Component) {\n Component = Component.prototype;\n return !(!Component || !Component.isReactComponent);\n}\nfunction createWorkInProgress(current, pendingProps) {\n var workInProgress = current.alternate;\n null === workInProgress\n ? ((workInProgress = createFiberImplClass(\n current.tag,\n pendingProps,\n current.key,\n current.mode\n )),\n (workInProgress.elementType = current.elementType),\n (workInProgress.type = current.type),\n (workInProgress.stateNode = current.stateNode),\n (workInProgress.alternate = current),\n (current.alternate = workInProgress))\n : ((workInProgress.pendingProps = pendingProps),\n (workInProgress.type = current.type),\n (workInProgress.flags = 0),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null));\n workInProgress.flags = current.flags & 65011712;\n workInProgress.childLanes = current.childLanes;\n workInProgress.lanes = current.lanes;\n workInProgress.child = current.child;\n workInProgress.memoizedProps = current.memoizedProps;\n workInProgress.memoizedState = current.memoizedState;\n workInProgress.updateQueue = current.updateQueue;\n pendingProps = current.dependencies;\n workInProgress.dependencies =\n null === pendingProps\n ? null\n : { lanes: pendingProps.lanes, firstContext: pendingProps.firstContext };\n workInProgress.sibling = current.sibling;\n workInProgress.index = current.index;\n workInProgress.ref = current.ref;\n workInProgress.refCleanup = current.refCleanup;\n return workInProgress;\n}\nfunction resetWorkInProgress(workInProgress, renderLanes) {\n workInProgress.flags &= 65011714;\n var current = workInProgress.alternate;\n null === current\n ? ((workInProgress.childLanes = 0),\n (workInProgress.lanes = renderLanes),\n (workInProgress.child = null),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.memoizedProps = null),\n (workInProgress.memoizedState = null),\n (workInProgress.updateQueue = null),\n (workInProgress.dependencies = null),\n (workInProgress.stateNode = null))\n : ((workInProgress.childLanes = current.childLanes),\n (workInProgress.lanes = current.lanes),\n (workInProgress.child = current.child),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null),\n (workInProgress.memoizedProps = current.memoizedProps),\n (workInProgress.memoizedState = current.memoizedState),\n (workInProgress.updateQueue = current.updateQueue),\n (workInProgress.type = current.type),\n (renderLanes = current.dependencies),\n (workInProgress.dependencies =\n null === renderLanes\n ? null\n : {\n lanes: renderLanes.lanes,\n firstContext: renderLanes.firstContext\n }));\n return workInProgress;\n}\nfunction createFiberFromTypeAndProps(\n type,\n key,\n pendingProps,\n owner,\n mode,\n lanes\n) {\n var fiberTag = 0;\n owner = type;\n if (\"function\" === typeof type) shouldConstruct(type) && (fiberTag = 1);\n else if (\"string\" === typeof type)\n fiberTag = isHostHoistableType(\n type,\n pendingProps,\n contextStackCursor.current\n )\n ? 26\n : \"html\" === type || \"head\" === type || \"body\" === type\n ? 27\n : 5;\n else\n a: switch (type) {\n case REACT_ACTIVITY_TYPE:\n return (\n (type = createFiberImplClass(31, pendingProps, key, mode)),\n (type.elementType = REACT_ACTIVITY_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_FRAGMENT_TYPE:\n return createFiberFromFragment(pendingProps.children, mode, lanes, key);\n case REACT_STRICT_MODE_TYPE:\n fiberTag = 8;\n mode |= 24;\n break;\n case REACT_PROFILER_TYPE:\n return (\n (type = createFiberImplClass(12, pendingProps, key, mode | 2)),\n (type.elementType = REACT_PROFILER_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_TYPE:\n return (\n (type = createFiberImplClass(13, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_LIST_TYPE:\n return (\n (type = createFiberImplClass(19, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_LIST_TYPE),\n (type.lanes = lanes),\n type\n );\n default:\n if (\"object\" === typeof type && null !== type)\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n fiberTag = 10;\n break a;\n case REACT_CONSUMER_TYPE:\n fiberTag = 9;\n break a;\n case REACT_FORWARD_REF_TYPE:\n fiberTag = 11;\n break a;\n case REACT_MEMO_TYPE:\n fiberTag = 14;\n break a;\n case REACT_LAZY_TYPE:\n fiberTag = 16;\n owner = null;\n break a;\n }\n fiberTag = 29;\n pendingProps = Error(\n formatProdErrorMessage(130, null === type ? \"null\" : typeof type, \"\")\n );\n owner = null;\n }\n key = createFiberImplClass(fiberTag, pendingProps, key, mode);\n key.elementType = type;\n key.type = owner;\n key.lanes = lanes;\n return key;\n}\nfunction createFiberFromFragment(elements, mode, lanes, key) {\n elements = createFiberImplClass(7, elements, key, mode);\n elements.lanes = lanes;\n return elements;\n}\nfunction createFiberFromText(content, mode, lanes) {\n content = createFiberImplClass(6, content, null, mode);\n content.lanes = lanes;\n return content;\n}\nfunction createFiberFromDehydratedFragment(dehydratedNode) {\n var fiber = createFiberImplClass(18, null, null, 0);\n fiber.stateNode = dehydratedNode;\n return fiber;\n}\nfunction createFiberFromPortal(portal, mode, lanes) {\n mode = createFiberImplClass(\n 4,\n null !== portal.children ? portal.children : [],\n portal.key,\n mode\n );\n mode.lanes = lanes;\n mode.stateNode = {\n containerInfo: portal.containerInfo,\n pendingChildren: null,\n implementation: portal.implementation\n };\n return mode;\n}\nvar CapturedStacks = new WeakMap();\nfunction createCapturedValueAtFiber(value, source) {\n if (\"object\" === typeof value && null !== value) {\n var existing = CapturedStacks.get(value);\n if (void 0 !== existing) return existing;\n source = {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n CapturedStacks.set(value, source);\n return source;\n }\n return {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n}\nvar forkStack = [],\n forkStackIndex = 0,\n treeForkProvider = null,\n treeForkCount = 0,\n idStack = [],\n idStackIndex = 0,\n treeContextProvider = null,\n treeContextId = 1,\n treeContextOverflow = \"\";\nfunction pushTreeFork(workInProgress, totalChildren) {\n forkStack[forkStackIndex++] = treeForkCount;\n forkStack[forkStackIndex++] = treeForkProvider;\n treeForkProvider = workInProgress;\n treeForkCount = totalChildren;\n}\nfunction pushTreeId(workInProgress, totalChildren, index) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextProvider = workInProgress;\n var baseIdWithLeadingBit = treeContextId;\n workInProgress = treeContextOverflow;\n var baseLength = 32 - clz32(baseIdWithLeadingBit) - 1;\n baseIdWithLeadingBit &= ~(1 << baseLength);\n index += 1;\n var length = 32 - clz32(totalChildren) + baseLength;\n if (30 < length) {\n var numberOfOverflowBits = baseLength - (baseLength % 5);\n length = (\n baseIdWithLeadingBit &\n ((1 << numberOfOverflowBits) - 1)\n ).toString(32);\n baseIdWithLeadingBit >>= numberOfOverflowBits;\n baseLength -= numberOfOverflowBits;\n treeContextId =\n (1 << (32 - clz32(totalChildren) + baseLength)) |\n (index << baseLength) |\n baseIdWithLeadingBit;\n treeContextOverflow = length + workInProgress;\n } else\n (treeContextId =\n (1 << length) | (index << baseLength) | baseIdWithLeadingBit),\n (treeContextOverflow = workInProgress);\n}\nfunction pushMaterializedTreeId(workInProgress) {\n null !== workInProgress.return &&\n (pushTreeFork(workInProgress, 1), pushTreeId(workInProgress, 1, 0));\n}\nfunction popTreeContext(workInProgress) {\n for (; workInProgress === treeForkProvider; )\n (treeForkProvider = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null),\n (treeForkCount = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null);\n for (; workInProgress === treeContextProvider; )\n (treeContextProvider = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextOverflow = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextId = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null);\n}\nfunction restoreSuspendedTreeContext(workInProgress, suspendedContext) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextId = suspendedContext.id;\n treeContextOverflow = suspendedContext.overflow;\n treeContextProvider = workInProgress;\n}\nvar hydrationParentFiber = null,\n nextHydratableInstance = null,\n isHydrating = !1,\n hydrationErrors = null,\n rootOrSingletonContext = !1,\n HydrationMismatchException = Error(formatProdErrorMessage(519));\nfunction throwOnHydrationMismatch(fiber) {\n var error = Error(\n formatProdErrorMessage(\n 418,\n 1 < arguments.length && void 0 !== arguments[1] && arguments[1]\n ? \"text\"\n : \"HTML\",\n \"\"\n )\n );\n queueHydrationError(createCapturedValueAtFiber(error, fiber));\n throw HydrationMismatchException;\n}\nfunction prepareToHydrateHostInstance(fiber) {\n var instance = fiber.stateNode,\n type = fiber.type,\n props = fiber.memoizedProps;\n instance[internalInstanceKey] = fiber;\n instance[internalPropsKey] = props;\n switch (type) {\n case \"dialog\":\n listenToNonDelegatedEvent(\"cancel\", instance);\n listenToNonDelegatedEvent(\"close\", instance);\n break;\n case \"iframe\":\n case \"object\":\n case \"embed\":\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"video\":\n case \"audio\":\n for (type = 0; type < mediaEventTypes.length; type++)\n listenToNonDelegatedEvent(mediaEventTypes[type], instance);\n break;\n case \"source\":\n listenToNonDelegatedEvent(\"error\", instance);\n break;\n case \"img\":\n case \"image\":\n case \"link\":\n listenToNonDelegatedEvent(\"error\", instance);\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"details\":\n listenToNonDelegatedEvent(\"toggle\", instance);\n break;\n case \"input\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n initInput(\n instance,\n props.value,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name,\n !0\n );\n break;\n case \"select\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n break;\n case \"textarea\":\n listenToNonDelegatedEvent(\"invalid\", instance),\n initTextarea(instance, props.value, props.defaultValue, props.children);\n }\n type = props.children;\n (\"string\" !== typeof type &&\n \"number\" !== typeof type &&\n \"bigint\" !== typeof type) ||\n instance.textContent === \"\" + type ||\n !0 === props.suppressHydrationWarning ||\n checkForUnmatchedText(instance.textContent, type)\n ? (null != props.popover &&\n (listenToNonDelegatedEvent(\"beforetoggle\", instance),\n listenToNonDelegatedEvent(\"toggle\", instance)),\n null != props.onScroll && listenToNonDelegatedEvent(\"scroll\", instance),\n null != props.onScrollEnd &&\n listenToNonDelegatedEvent(\"scrollend\", instance),\n null != props.onClick && (instance.onclick = noop$1),\n (instance = !0))\n : (instance = !1);\n instance || throwOnHydrationMismatch(fiber, !0);\n}\nfunction popToNextHostParent(fiber) {\n for (hydrationParentFiber = fiber.return; hydrationParentFiber; )\n switch (hydrationParentFiber.tag) {\n case 5:\n case 31:\n case 13:\n rootOrSingletonContext = !1;\n return;\n case 27:\n case 3:\n rootOrSingletonContext = !0;\n return;\n default:\n hydrationParentFiber = hydrationParentFiber.return;\n }\n}\nfunction popHydrationState(fiber) {\n if (fiber !== hydrationParentFiber) return !1;\n if (!isHydrating) return popToNextHostParent(fiber), (isHydrating = !0), !1;\n var tag = fiber.tag,\n JSCompiler_temp;\n if ((JSCompiler_temp = 3 !== tag && 27 !== tag)) {\n if ((JSCompiler_temp = 5 === tag))\n (JSCompiler_temp = fiber.type),\n (JSCompiler_temp =\n !(\"form\" !== JSCompiler_temp && \"button\" !== JSCompiler_temp) ||\n shouldSetTextContent(fiber.type, fiber.memoizedProps));\n JSCompiler_temp = !JSCompiler_temp;\n }\n JSCompiler_temp && nextHydratableInstance && throwOnHydrationMismatch(fiber);\n popToNextHostParent(fiber);\n if (13 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else if (31 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else\n 27 === tag\n ? ((tag = nextHydratableInstance),\n isSingletonScope(fiber.type)\n ? ((fiber = previousHydratableOnEnteringScopedSingleton),\n (previousHydratableOnEnteringScopedSingleton = null),\n (nextHydratableInstance = fiber))\n : (nextHydratableInstance = tag))\n : (nextHydratableInstance = hydrationParentFiber\n ? getNextHydratable(fiber.stateNode.nextSibling)\n : null);\n return !0;\n}\nfunction resetHydrationState() {\n nextHydratableInstance = hydrationParentFiber = null;\n isHydrating = !1;\n}\nfunction upgradeHydrationErrorsToRecoverable() {\n var queuedErrors = hydrationErrors;\n null !== queuedErrors &&\n (null === workInProgressRootRecoverableErrors\n ? (workInProgressRootRecoverableErrors = queuedErrors)\n : workInProgressRootRecoverableErrors.push.apply(\n workInProgressRootRecoverableErrors,\n queuedErrors\n ),\n (hydrationErrors = null));\n return queuedErrors;\n}\nfunction queueHydrationError(error) {\n null === hydrationErrors\n ? (hydrationErrors = [error])\n : hydrationErrors.push(error);\n}\nvar valueCursor = createCursor(null),\n currentlyRenderingFiber$1 = null,\n lastContextDependency = null;\nfunction pushProvider(providerFiber, context, nextValue) {\n push(valueCursor, context._currentValue);\n context._currentValue = nextValue;\n}\nfunction popProvider(context) {\n context._currentValue = valueCursor.current;\n pop(valueCursor);\n}\nfunction scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {\n for (; null !== parent; ) {\n var alternate = parent.alternate;\n (parent.childLanes & renderLanes) !== renderLanes\n ? ((parent.childLanes |= renderLanes),\n null !== alternate && (alternate.childLanes |= renderLanes))\n : null !== alternate &&\n (alternate.childLanes & renderLanes) !== renderLanes &&\n (alternate.childLanes |= renderLanes);\n if (parent === propagationRoot) break;\n parent = parent.return;\n }\n}\nfunction propagateContextChanges(\n workInProgress,\n contexts,\n renderLanes,\n forcePropagateEntireTree\n) {\n var fiber = workInProgress.child;\n null !== fiber && (fiber.return = workInProgress);\n for (; null !== fiber; ) {\n var list = fiber.dependencies;\n if (null !== list) {\n var nextFiber = fiber.child;\n list = list.firstContext;\n a: for (; null !== list; ) {\n var dependency = list;\n list = fiber;\n for (var i = 0; i < contexts.length; i++)\n if (dependency.context === contexts[i]) {\n list.lanes |= renderLanes;\n dependency = list.alternate;\n null !== dependency && (dependency.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(\n list.return,\n renderLanes,\n workInProgress\n );\n forcePropagateEntireTree || (nextFiber = null);\n break a;\n }\n list = dependency.next;\n }\n } else if (18 === fiber.tag) {\n nextFiber = fiber.return;\n if (null === nextFiber) throw Error(formatProdErrorMessage(341));\n nextFiber.lanes |= renderLanes;\n list = nextFiber.alternate;\n null !== list && (list.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(nextFiber, renderLanes, workInProgress);\n nextFiber = null;\n } else nextFiber = fiber.child;\n if (null !== nextFiber) nextFiber.return = fiber;\n else\n for (nextFiber = fiber; null !== nextFiber; ) {\n if (nextFiber === workInProgress) {\n nextFiber = null;\n break;\n }\n fiber = nextFiber.sibling;\n if (null !== fiber) {\n fiber.return = nextFiber.return;\n nextFiber = fiber;\n break;\n }\n nextFiber = nextFiber.return;\n }\n fiber = nextFiber;\n }\n}\nfunction propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n forcePropagateEntireTree\n) {\n current = null;\n for (\n var parent = workInProgress, isInsidePropagationBailout = !1;\n null !== parent;\n\n ) {\n if (!isInsidePropagationBailout)\n if (0 !== (parent.flags & 524288)) isInsidePropagationBailout = !0;\n else if (0 !== (parent.flags & 262144)) break;\n if (10 === parent.tag) {\n var currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent = currentParent.memoizedProps;\n if (null !== currentParent) {\n var context = parent.type;\n objectIs(parent.pendingProps.value, currentParent.value) ||\n (null !== current ? current.push(context) : (current = [context]));\n }\n } else if (parent === hostTransitionProviderCursor.current) {\n currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent.memoizedState.memoizedState !==\n parent.memoizedState.memoizedState &&\n (null !== current\n ? current.push(HostTransitionContext)\n : (current = [HostTransitionContext]));\n }\n parent = parent.return;\n }\n null !== current &&\n propagateContextChanges(\n workInProgress,\n current,\n renderLanes,\n forcePropagateEntireTree\n );\n workInProgress.flags |= 262144;\n}\nfunction checkIfContextChanged(currentDependencies) {\n for (\n currentDependencies = currentDependencies.firstContext;\n null !== currentDependencies;\n\n ) {\n if (\n !objectIs(\n currentDependencies.context._currentValue,\n currentDependencies.memoizedValue\n )\n )\n return !0;\n currentDependencies = currentDependencies.next;\n }\n return !1;\n}\nfunction prepareToReadContext(workInProgress) {\n currentlyRenderingFiber$1 = workInProgress;\n lastContextDependency = null;\n workInProgress = workInProgress.dependencies;\n null !== workInProgress && (workInProgress.firstContext = null);\n}\nfunction readContext(context) {\n return readContextForConsumer(currentlyRenderingFiber$1, context);\n}\nfunction readContextDuringReconciliation(consumer, context) {\n null === currentlyRenderingFiber$1 && prepareToReadContext(consumer);\n return readContextForConsumer(consumer, context);\n}\nfunction readContextForConsumer(consumer, context) {\n var value = context._currentValue;\n context = { context: context, memoizedValue: value, next: null };\n if (null === lastContextDependency) {\n if (null === consumer) throw Error(formatProdErrorMessage(308));\n lastContextDependency = context;\n consumer.dependencies = { lanes: 0, firstContext: context };\n consumer.flags |= 524288;\n } else lastContextDependency = lastContextDependency.next = context;\n return value;\n}\nvar AbortControllerLocal =\n \"undefined\" !== typeof AbortController\n ? AbortController\n : function () {\n var listeners = [],\n signal = (this.signal = {\n aborted: !1,\n addEventListener: function (type, listener) {\n listeners.push(listener);\n }\n });\n this.abort = function () {\n signal.aborted = !0;\n listeners.forEach(function (listener) {\n return listener();\n });\n };\n },\n scheduleCallback$2 = Scheduler.unstable_scheduleCallback,\n NormalPriority = Scheduler.unstable_NormalPriority,\n CacheContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n Consumer: null,\n Provider: null,\n _currentValue: null,\n _currentValue2: null,\n _threadCount: 0\n };\nfunction createCache() {\n return {\n controller: new AbortControllerLocal(),\n data: new Map(),\n refCount: 0\n };\n}\nfunction releaseCache(cache) {\n cache.refCount--;\n 0 === cache.refCount &&\n scheduleCallback$2(NormalPriority, function () {\n cache.controller.abort();\n });\n}\nvar currentEntangledListeners = null,\n currentEntangledPendingCount = 0,\n currentEntangledLane = 0,\n currentEntangledActionThenable = null;\nfunction entangleAsyncAction(transition, thenable) {\n if (null === currentEntangledListeners) {\n var entangledListeners = (currentEntangledListeners = []);\n currentEntangledPendingCount = 0;\n currentEntangledLane = requestTransitionLane();\n currentEntangledActionThenable = {\n status: \"pending\",\n value: void 0,\n then: function (resolve) {\n entangledListeners.push(resolve);\n }\n };\n }\n currentEntangledPendingCount++;\n thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);\n return thenable;\n}\nfunction pingEngtangledActionScope() {\n if (\n 0 === --currentEntangledPendingCount &&\n null !== currentEntangledListeners\n ) {\n null !== currentEntangledActionThenable &&\n (currentEntangledActionThenable.status = \"fulfilled\");\n var listeners = currentEntangledListeners;\n currentEntangledListeners = null;\n currentEntangledLane = 0;\n currentEntangledActionThenable = null;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])();\n }\n}\nfunction chainThenableValue(thenable, result) {\n var listeners = [],\n thenableWithOverride = {\n status: \"pending\",\n value: null,\n reason: null,\n then: function (resolve) {\n listeners.push(resolve);\n }\n };\n thenable.then(\n function () {\n thenableWithOverride.status = \"fulfilled\";\n thenableWithOverride.value = result;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])(result);\n },\n function (error) {\n thenableWithOverride.status = \"rejected\";\n thenableWithOverride.reason = error;\n for (error = 0; error < listeners.length; error++)\n (0, listeners[error])(void 0);\n }\n );\n return thenableWithOverride;\n}\nvar prevOnStartTransitionFinish = ReactSharedInternals.S;\nReactSharedInternals.S = function (transition, returnValue) {\n globalMostRecentTransitionTime = now();\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n entangleAsyncAction(transition, returnValue);\n null !== prevOnStartTransitionFinish &&\n prevOnStartTransitionFinish(transition, returnValue);\n};\nvar resumedCache = createCursor(null);\nfunction peekCacheFromPool() {\n var cacheResumedFromPreviousRender = resumedCache.current;\n return null !== cacheResumedFromPreviousRender\n ? cacheResumedFromPreviousRender\n : workInProgressRoot.pooledCache;\n}\nfunction pushTransition(offscreenWorkInProgress, prevCachePool) {\n null === prevCachePool\n ? push(resumedCache, resumedCache.current)\n : push(resumedCache, prevCachePool.pool);\n}\nfunction getSuspendedCache() {\n var cacheFromPool = peekCacheFromPool();\n return null === cacheFromPool\n ? null\n : { parent: CacheContext._currentValue, pool: cacheFromPool };\n}\nvar SuspenseException = Error(formatProdErrorMessage(460)),\n SuspenseyCommitException = Error(formatProdErrorMessage(474)),\n SuspenseActionException = Error(formatProdErrorMessage(542)),\n noopSuspenseyCommitThenable = { then: function () {} };\nfunction isThenableResolved(thenable) {\n thenable = thenable.status;\n return \"fulfilled\" === thenable || \"rejected\" === thenable;\n}\nfunction trackUsedThenable(thenableState, thenable, index) {\n index = thenableState[index];\n void 0 === index\n ? thenableState.push(thenable)\n : index !== thenable && (thenable.then(noop$1, noop$1), (thenable = index));\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n default:\n if (\"string\" === typeof thenable.status) thenable.then(noop$1, noop$1);\n else {\n thenableState = workInProgressRoot;\n if (null !== thenableState && 100 < thenableState.shellSuspendCounter)\n throw Error(formatProdErrorMessage(482));\n thenableState = thenable;\n thenableState.status = \"pending\";\n thenableState.then(\n function (fulfilledValue) {\n if (\"pending\" === thenable.status) {\n var fulfilledThenable = thenable;\n fulfilledThenable.status = \"fulfilled\";\n fulfilledThenable.value = fulfilledValue;\n }\n },\n function (error) {\n if (\"pending\" === thenable.status) {\n var rejectedThenable = thenable;\n rejectedThenable.status = \"rejected\";\n rejectedThenable.reason = error;\n }\n }\n );\n }\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n }\n suspendedThenable = thenable;\n throw SuspenseException;\n }\n}\nfunction resolveLazy(lazyType) {\n try {\n var init = lazyType._init;\n return init(lazyType._payload);\n } catch (x) {\n if (null !== x && \"object\" === typeof x && \"function\" === typeof x.then)\n throw ((suspendedThenable = x), SuspenseException);\n throw x;\n }\n}\nvar suspendedThenable = null;\nfunction getSuspendedThenable() {\n if (null === suspendedThenable) throw Error(formatProdErrorMessage(459));\n var thenable = suspendedThenable;\n suspendedThenable = null;\n return thenable;\n}\nfunction checkIfUseWrappedInAsyncCatch(rejectedReason) {\n if (\n rejectedReason === SuspenseException ||\n rejectedReason === SuspenseActionException\n )\n throw Error(formatProdErrorMessage(483));\n}\nvar thenableState$1 = null,\n thenableIndexCounter$1 = 0;\nfunction unwrapThenable(thenable) {\n var index = thenableIndexCounter$1;\n thenableIndexCounter$1 += 1;\n null === thenableState$1 && (thenableState$1 = []);\n return trackUsedThenable(thenableState$1, thenable, index);\n}\nfunction coerceRef(workInProgress, element) {\n element = element.props.ref;\n workInProgress.ref = void 0 !== element ? element : null;\n}\nfunction throwOnInvalidObjectTypeImpl(returnFiber, newChild) {\n if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE)\n throw Error(formatProdErrorMessage(525));\n returnFiber = Object.prototype.toString.call(newChild);\n throw Error(\n formatProdErrorMessage(\n 31,\n \"[object Object]\" === returnFiber\n ? \"object with keys {\" + Object.keys(newChild).join(\", \") + \"}\"\n : returnFiber\n )\n );\n}\nfunction createChildReconciler(shouldTrackSideEffects) {\n function deleteChild(returnFiber, childToDelete) {\n if (shouldTrackSideEffects) {\n var deletions = returnFiber.deletions;\n null === deletions\n ? ((returnFiber.deletions = [childToDelete]), (returnFiber.flags |= 16))\n : deletions.push(childToDelete);\n }\n }\n function deleteRemainingChildren(returnFiber, currentFirstChild) {\n if (!shouldTrackSideEffects) return null;\n for (; null !== currentFirstChild; )\n deleteChild(returnFiber, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return null;\n }\n function mapRemainingChildren(currentFirstChild) {\n for (var existingChildren = new Map(); null !== currentFirstChild; )\n null !== currentFirstChild.key\n ? existingChildren.set(currentFirstChild.key, currentFirstChild)\n : existingChildren.set(currentFirstChild.index, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return existingChildren;\n }\n function useFiber(fiber, pendingProps) {\n fiber = createWorkInProgress(fiber, pendingProps);\n fiber.index = 0;\n fiber.sibling = null;\n return fiber;\n }\n function placeChild(newFiber, lastPlacedIndex, newIndex) {\n newFiber.index = newIndex;\n if (!shouldTrackSideEffects)\n return (newFiber.flags |= 1048576), lastPlacedIndex;\n newIndex = newFiber.alternate;\n if (null !== newIndex)\n return (\n (newIndex = newIndex.index),\n newIndex < lastPlacedIndex\n ? ((newFiber.flags |= 67108866), lastPlacedIndex)\n : newIndex\n );\n newFiber.flags |= 67108866;\n return lastPlacedIndex;\n }\n function placeSingleChild(newFiber) {\n shouldTrackSideEffects &&\n null === newFiber.alternate &&\n (newFiber.flags |= 67108866);\n return newFiber;\n }\n function updateTextNode(returnFiber, current, textContent, lanes) {\n if (null === current || 6 !== current.tag)\n return (\n (current = createFiberFromText(textContent, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, textContent);\n current.return = returnFiber;\n return current;\n }\n function updateElement(returnFiber, current, element, lanes) {\n var elementType = element.type;\n if (elementType === REACT_FRAGMENT_TYPE)\n return updateFragment(\n returnFiber,\n current,\n element.props.children,\n lanes,\n element.key\n );\n if (\n null !== current &&\n (current.elementType === elementType ||\n (\"object\" === typeof elementType &&\n null !== elementType &&\n elementType.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(elementType) === current.type))\n )\n return (\n (current = useFiber(current, element.props)),\n coerceRef(current, element),\n (current.return = returnFiber),\n current\n );\n current = createFiberFromTypeAndProps(\n element.type,\n element.key,\n element.props,\n null,\n returnFiber.mode,\n lanes\n );\n coerceRef(current, element);\n current.return = returnFiber;\n return current;\n }\n function updatePortal(returnFiber, current, portal, lanes) {\n if (\n null === current ||\n 4 !== current.tag ||\n current.stateNode.containerInfo !== portal.containerInfo ||\n current.stateNode.implementation !== portal.implementation\n )\n return (\n (current = createFiberFromPortal(portal, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, portal.children || []);\n current.return = returnFiber;\n return current;\n }\n function updateFragment(returnFiber, current, fragment, lanes, key) {\n if (null === current || 7 !== current.tag)\n return (\n (current = createFiberFromFragment(\n fragment,\n returnFiber.mode,\n lanes,\n key\n )),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, fragment);\n current.return = returnFiber;\n return current;\n }\n function createChild(returnFiber, newChild, lanes) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (newChild = createFiberFromText(\n \"\" + newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n lanes\n );\n case REACT_PORTAL_TYPE:\n return (\n (newChild = createFiberFromPortal(\n newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n createChild(returnFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (newChild = createFiberFromFragment(\n newChild,\n returnFiber.mode,\n lanes,\n null\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"function\" === typeof newChild.then)\n return createChild(returnFiber, unwrapThenable(newChild), lanes);\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return createChild(\n returnFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateSlot(returnFiber, oldFiber, newChild, lanes) {\n var key = null !== oldFiber ? oldFiber.key : null;\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return null !== key\n ? null\n : updateTextNode(returnFiber, oldFiber, \"\" + newChild, lanes);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return newChild.key === key\n ? updateElement(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_PORTAL_TYPE:\n return newChild.key === key\n ? updatePortal(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateSlot(returnFiber, oldFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return null !== key\n ? null\n : updateFragment(returnFiber, oldFiber, newChild, lanes, null);\n if (\"function\" === typeof newChild.then)\n return updateSlot(\n returnFiber,\n oldFiber,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateSlot(\n returnFiber,\n oldFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n ) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateTextNode(returnFiber, existingChildren, \"\" + newChild, lanes)\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updateElement(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_PORTAL_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updatePortal(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateFragment(returnFiber, existingChildren, newChild, lanes, null)\n );\n if (\"function\" === typeof newChild.then)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null;\n null !== oldFiber && newIdx < newChildren.length;\n newIdx++\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(\n returnFiber,\n oldFiber,\n newChildren[newIdx],\n lanes\n );\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (newIdx === newChildren.length)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; newIdx < newChildren.length; newIdx++)\n (oldFiber = createChild(returnFiber, newChildren[newIdx], lanes)),\n null !== oldFiber &&\n ((currentFirstChild = placeChild(\n oldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = oldFiber)\n : (previousNewFiber.sibling = oldFiber),\n (previousNewFiber = oldFiber));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n newIdx < newChildren.length;\n newIdx++\n )\n (nextOldFiber = updateFromMap(\n oldFiber,\n returnFiber,\n newIdx,\n newChildren[newIdx],\n lanes\n )),\n null !== nextOldFiber &&\n (shouldTrackSideEffects &&\n null !== nextOldFiber.alternate &&\n oldFiber.delete(\n null === nextOldFiber.key ? newIdx : nextOldFiber.key\n ),\n (currentFirstChild = placeChild(\n nextOldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = nextOldFiber)\n : (previousNewFiber.sibling = nextOldFiber),\n (previousNewFiber = nextOldFiber));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n if (null == newChildren) throw Error(formatProdErrorMessage(151));\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null,\n step = newChildren.next();\n null !== oldFiber && !step.done;\n newIdx++, step = newChildren.next()\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (step.done)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; !step.done; newIdx++, step = newChildren.next())\n (step = createChild(returnFiber, step.value, lanes)),\n null !== step &&\n ((currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n !step.done;\n newIdx++, step = newChildren.next()\n )\n (step = updateFromMap(oldFiber, returnFiber, newIdx, step.value, lanes)),\n null !== step &&\n (shouldTrackSideEffects &&\n null !== step.alternate &&\n oldFiber.delete(null === step.key ? newIdx : step.key),\n (currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n ) {\n \"object\" === typeof newChild &&\n null !== newChild &&\n newChild.type === REACT_FRAGMENT_TYPE &&\n null === newChild.key &&\n (newChild = newChild.props.children);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n a: {\n for (var key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key) {\n key = newChild.type;\n if (key === REACT_FRAGMENT_TYPE) {\n if (7 === currentFirstChild.tag) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(\n currentFirstChild,\n newChild.props.children\n );\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n } else if (\n currentFirstChild.elementType === key ||\n (\"object\" === typeof key &&\n null !== key &&\n key.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(key) === currentFirstChild.type)\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.props);\n coerceRef(lanes, newChild);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n } else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n newChild.type === REACT_FRAGMENT_TYPE\n ? ((lanes = createFiberFromFragment(\n newChild.props.children,\n returnFiber.mode,\n lanes,\n newChild.key\n )),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : ((lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n (returnFiber = lanes));\n }\n return placeSingleChild(returnFiber);\n case REACT_PORTAL_TYPE:\n a: {\n for (key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key)\n if (\n 4 === currentFirstChild.tag &&\n currentFirstChild.stateNode.containerInfo ===\n newChild.containerInfo &&\n currentFirstChild.stateNode.implementation ===\n newChild.implementation\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.children || []);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n } else {\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n }\n else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n lanes = createFiberFromPortal(newChild, returnFiber.mode, lanes);\n lanes.return = returnFiber;\n returnFiber = lanes;\n }\n return placeSingleChild(returnFiber);\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild))\n return reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n if (getIteratorFn(newChild)) {\n key = getIteratorFn(newChild);\n if (\"function\" !== typeof key) throw Error(formatProdErrorMessage(150));\n newChild = key.call(newChild);\n return reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n }\n if (\"function\" === typeof newChild.then)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n ? ((newChild = \"\" + newChild),\n null !== currentFirstChild && 6 === currentFirstChild.tag\n ? (deleteRemainingChildren(returnFiber, currentFirstChild.sibling),\n (lanes = useFiber(currentFirstChild, newChild)),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : (deleteRemainingChildren(returnFiber, currentFirstChild),\n (lanes = createFiberFromText(newChild, returnFiber.mode, lanes)),\n (lanes.return = returnFiber),\n (returnFiber = lanes)),\n placeSingleChild(returnFiber))\n : deleteRemainingChildren(returnFiber, currentFirstChild);\n }\n return function (returnFiber, currentFirstChild, newChild, lanes) {\n try {\n thenableIndexCounter$1 = 0;\n var firstChildFiber = reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n thenableState$1 = null;\n return firstChildFiber;\n } catch (x) {\n if (x === SuspenseException || x === SuspenseActionException) throw x;\n var fiber = createFiberImplClass(29, x, null, returnFiber.mode);\n fiber.lanes = lanes;\n fiber.return = returnFiber;\n return fiber;\n } finally {\n }\n };\n}\nvar reconcileChildFibers = createChildReconciler(!0),\n mountChildFibers = createChildReconciler(!1),\n hasForceUpdate = !1;\nfunction initializeUpdateQueue(fiber) {\n fiber.updateQueue = {\n baseState: fiber.memoizedState,\n firstBaseUpdate: null,\n lastBaseUpdate: null,\n shared: { pending: null, lanes: 0, hiddenCallbacks: null },\n callbacks: null\n };\n}\nfunction cloneUpdateQueue(current, workInProgress) {\n current = current.updateQueue;\n workInProgress.updateQueue === current &&\n (workInProgress.updateQueue = {\n baseState: current.baseState,\n firstBaseUpdate: current.firstBaseUpdate,\n lastBaseUpdate: current.lastBaseUpdate,\n shared: current.shared,\n callbacks: null\n });\n}\nfunction createUpdate(lane) {\n return { lane: lane, tag: 0, payload: null, callback: null, next: null };\n}\nfunction enqueueUpdate(fiber, update, lane) {\n var updateQueue = fiber.updateQueue;\n if (null === updateQueue) return null;\n updateQueue = updateQueue.shared;\n if (0 !== (executionContext & 2)) {\n var pending = updateQueue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n updateQueue.pending = update;\n update = getRootForUpdatedFiber(fiber);\n markUpdateLaneFromFiberToRoot(fiber, null, lane);\n return update;\n }\n enqueueUpdate$1(fiber, updateQueue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction entangleTransitions(root, fiber, lane) {\n fiber = fiber.updateQueue;\n if (null !== fiber && ((fiber = fiber.shared), 0 !== (lane & 4194048))) {\n var queueLanes = fiber.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n fiber.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nfunction enqueueCapturedUpdate(workInProgress, capturedUpdate) {\n var queue = workInProgress.updateQueue,\n current = workInProgress.alternate;\n if (\n null !== current &&\n ((current = current.updateQueue), queue === current)\n ) {\n var newFirst = null,\n newLast = null;\n queue = queue.firstBaseUpdate;\n if (null !== queue) {\n do {\n var clone = {\n lane: queue.lane,\n tag: queue.tag,\n payload: queue.payload,\n callback: null,\n next: null\n };\n null === newLast\n ? (newFirst = newLast = clone)\n : (newLast = newLast.next = clone);\n queue = queue.next;\n } while (null !== queue);\n null === newLast\n ? (newFirst = newLast = capturedUpdate)\n : (newLast = newLast.next = capturedUpdate);\n } else newFirst = newLast = capturedUpdate;\n queue = {\n baseState: current.baseState,\n firstBaseUpdate: newFirst,\n lastBaseUpdate: newLast,\n shared: current.shared,\n callbacks: current.callbacks\n };\n workInProgress.updateQueue = queue;\n return;\n }\n workInProgress = queue.lastBaseUpdate;\n null === workInProgress\n ? (queue.firstBaseUpdate = capturedUpdate)\n : (workInProgress.next = capturedUpdate);\n queue.lastBaseUpdate = capturedUpdate;\n}\nvar didReadFromEntangledAsyncAction = !1;\nfunction suspendIfUpdateReadFromEntangledAsyncAction() {\n if (didReadFromEntangledAsyncAction) {\n var entangledActionThenable = currentEntangledActionThenable;\n if (null !== entangledActionThenable) throw entangledActionThenable;\n }\n}\nfunction processUpdateQueue(\n workInProgress$jscomp$0,\n props,\n instance$jscomp$0,\n renderLanes\n) {\n didReadFromEntangledAsyncAction = !1;\n var queue = workInProgress$jscomp$0.updateQueue;\n hasForceUpdate = !1;\n var firstBaseUpdate = queue.firstBaseUpdate,\n lastBaseUpdate = queue.lastBaseUpdate,\n pendingQueue = queue.shared.pending;\n if (null !== pendingQueue) {\n queue.shared.pending = null;\n var lastPendingUpdate = pendingQueue,\n firstPendingUpdate = lastPendingUpdate.next;\n lastPendingUpdate.next = null;\n null === lastBaseUpdate\n ? (firstBaseUpdate = firstPendingUpdate)\n : (lastBaseUpdate.next = firstPendingUpdate);\n lastBaseUpdate = lastPendingUpdate;\n var current = workInProgress$jscomp$0.alternate;\n null !== current &&\n ((current = current.updateQueue),\n (pendingQueue = current.lastBaseUpdate),\n pendingQueue !== lastBaseUpdate &&\n (null === pendingQueue\n ? (current.firstBaseUpdate = firstPendingUpdate)\n : (pendingQueue.next = firstPendingUpdate),\n (current.lastBaseUpdate = lastPendingUpdate)));\n }\n if (null !== firstBaseUpdate) {\n var newState = queue.baseState;\n lastBaseUpdate = 0;\n current = firstPendingUpdate = lastPendingUpdate = null;\n pendingQueue = firstBaseUpdate;\n do {\n var updateLane = pendingQueue.lane & -536870913,\n isHiddenUpdate = updateLane !== pendingQueue.lane;\n if (\n isHiddenUpdate\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n 0 !== updateLane &&\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction = !0);\n null !== current &&\n (current = current.next =\n {\n lane: 0,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: null,\n next: null\n });\n a: {\n var workInProgress = workInProgress$jscomp$0,\n update = pendingQueue;\n updateLane = props;\n var instance = instance$jscomp$0;\n switch (update.tag) {\n case 1:\n workInProgress = update.payload;\n if (\"function\" === typeof workInProgress) {\n newState = workInProgress.call(instance, newState, updateLane);\n break a;\n }\n newState = workInProgress;\n break a;\n case 3:\n workInProgress.flags = (workInProgress.flags & -65537) | 128;\n case 0:\n workInProgress = update.payload;\n updateLane =\n \"function\" === typeof workInProgress\n ? workInProgress.call(instance, newState, updateLane)\n : workInProgress;\n if (null === updateLane || void 0 === updateLane) break a;\n newState = assign({}, newState, updateLane);\n break a;\n case 2:\n hasForceUpdate = !0;\n }\n }\n updateLane = pendingQueue.callback;\n null !== updateLane &&\n ((workInProgress$jscomp$0.flags |= 64),\n isHiddenUpdate && (workInProgress$jscomp$0.flags |= 8192),\n (isHiddenUpdate = queue.callbacks),\n null === isHiddenUpdate\n ? (queue.callbacks = [updateLane])\n : isHiddenUpdate.push(updateLane));\n } else\n (isHiddenUpdate = {\n lane: updateLane,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: pendingQueue.callback,\n next: null\n }),\n null === current\n ? ((firstPendingUpdate = current = isHiddenUpdate),\n (lastPendingUpdate = newState))\n : (current = current.next = isHiddenUpdate),\n (lastBaseUpdate |= updateLane);\n pendingQueue = pendingQueue.next;\n if (null === pendingQueue)\n if (((pendingQueue = queue.shared.pending), null === pendingQueue))\n break;\n else\n (isHiddenUpdate = pendingQueue),\n (pendingQueue = isHiddenUpdate.next),\n (isHiddenUpdate.next = null),\n (queue.lastBaseUpdate = isHiddenUpdate),\n (queue.shared.pending = null);\n } while (1);\n null === current && (lastPendingUpdate = newState);\n queue.baseState = lastPendingUpdate;\n queue.firstBaseUpdate = firstPendingUpdate;\n queue.lastBaseUpdate = current;\n null === firstBaseUpdate && (queue.shared.lanes = 0);\n workInProgressRootSkippedLanes |= lastBaseUpdate;\n workInProgress$jscomp$0.lanes = lastBaseUpdate;\n workInProgress$jscomp$0.memoizedState = newState;\n }\n}\nfunction callCallback(callback, context) {\n if (\"function\" !== typeof callback)\n throw Error(formatProdErrorMessage(191, callback));\n callback.call(context);\n}\nfunction commitCallbacks(updateQueue, context) {\n var callbacks = updateQueue.callbacks;\n if (null !== callbacks)\n for (\n updateQueue.callbacks = null, updateQueue = 0;\n updateQueue < callbacks.length;\n updateQueue++\n )\n callCallback(callbacks[updateQueue], context);\n}\nvar currentTreeHiddenStackCursor = createCursor(null),\n prevEntangledRenderLanesCursor = createCursor(0);\nfunction pushHiddenContext(fiber, context) {\n fiber = entangledRenderLanes;\n push(prevEntangledRenderLanesCursor, fiber);\n push(currentTreeHiddenStackCursor, context);\n entangledRenderLanes = fiber | context.baseLanes;\n}\nfunction reuseHiddenContextOnStack() {\n push(prevEntangledRenderLanesCursor, entangledRenderLanes);\n push(currentTreeHiddenStackCursor, currentTreeHiddenStackCursor.current);\n}\nfunction popHiddenContext() {\n entangledRenderLanes = prevEntangledRenderLanesCursor.current;\n pop(currentTreeHiddenStackCursor);\n pop(prevEntangledRenderLanesCursor);\n}\nvar suspenseHandlerStackCursor = createCursor(null),\n shellBoundary = null;\nfunction pushPrimaryTreeSuspenseHandler(handler) {\n var current = handler.alternate;\n push(suspenseStackCursor, suspenseStackCursor.current & 1);\n push(suspenseHandlerStackCursor, handler);\n null === shellBoundary &&\n (null === current || null !== currentTreeHiddenStackCursor.current\n ? (shellBoundary = handler)\n : null !== current.memoizedState && (shellBoundary = handler));\n}\nfunction pushDehydratedActivitySuspenseHandler(fiber) {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, fiber);\n null === shellBoundary && (shellBoundary = fiber);\n}\nfunction pushOffscreenSuspenseHandler(fiber) {\n 22 === fiber.tag\n ? (push(suspenseStackCursor, suspenseStackCursor.current),\n push(suspenseHandlerStackCursor, fiber),\n null === shellBoundary && (shellBoundary = fiber))\n : reuseSuspenseHandlerOnStack(fiber);\n}\nfunction reuseSuspenseHandlerOnStack() {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, suspenseHandlerStackCursor.current);\n}\nfunction popSuspenseHandler(fiber) {\n pop(suspenseHandlerStackCursor);\n shellBoundary === fiber && (shellBoundary = null);\n pop(suspenseStackCursor);\n}\nvar suspenseStackCursor = createCursor(0);\nfunction findFirstSuspended(row) {\n for (var node = row; null !== node; ) {\n if (13 === node.tag) {\n var state = node.memoizedState;\n if (\n null !== state &&\n ((state = state.dehydrated),\n null === state ||\n isSuspenseInstancePending(state) ||\n isSuspenseInstanceFallback(state))\n )\n return node;\n } else if (\n 19 === node.tag &&\n (\"forwards\" === node.memoizedProps.revealOrder ||\n \"backwards\" === node.memoizedProps.revealOrder ||\n \"unstable_legacy-backwards\" === node.memoizedProps.revealOrder ||\n \"together\" === node.memoizedProps.revealOrder)\n ) {\n if (0 !== (node.flags & 128)) return node;\n } else if (null !== node.child) {\n node.child.return = node;\n node = node.child;\n continue;\n }\n if (node === row) break;\n for (; null === node.sibling; ) {\n if (null === node.return || node.return === row) return null;\n node = node.return;\n }\n node.sibling.return = node.return;\n node = node.sibling;\n }\n return null;\n}\nvar renderLanes = 0,\n currentlyRenderingFiber = null,\n currentHook = null,\n workInProgressHook = null,\n didScheduleRenderPhaseUpdate = !1,\n didScheduleRenderPhaseUpdateDuringThisPass = !1,\n shouldDoubleInvokeUserFnsInHooksDEV = !1,\n localIdCounter = 0,\n thenableIndexCounter = 0,\n thenableState = null,\n globalClientIdCounter = 0;\nfunction throwInvalidHookError() {\n throw Error(formatProdErrorMessage(321));\n}\nfunction areHookInputsEqual(nextDeps, prevDeps) {\n if (null === prevDeps) return !1;\n for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)\n if (!objectIs(nextDeps[i], prevDeps[i])) return !1;\n return !0;\n}\nfunction renderWithHooks(\n current,\n workInProgress,\n Component,\n props,\n secondArg,\n nextRenderLanes\n) {\n renderLanes = nextRenderLanes;\n currentlyRenderingFiber = workInProgress;\n workInProgress.memoizedState = null;\n workInProgress.updateQueue = null;\n workInProgress.lanes = 0;\n ReactSharedInternals.H =\n null === current || null === current.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate;\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n nextRenderLanes = Component(props, secondArg);\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n didScheduleRenderPhaseUpdateDuringThisPass &&\n (nextRenderLanes = renderWithHooksAgain(\n workInProgress,\n Component,\n props,\n secondArg\n ));\n finishRenderingHooks(current);\n return nextRenderLanes;\n}\nfunction finishRenderingHooks(current) {\n ReactSharedInternals.H = ContextOnlyDispatcher;\n var didRenderTooFewHooks = null !== currentHook && null !== currentHook.next;\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdate = !1;\n thenableIndexCounter = 0;\n thenableState = null;\n if (didRenderTooFewHooks) throw Error(formatProdErrorMessage(300));\n null === current ||\n didReceiveUpdate ||\n ((current = current.dependencies),\n null !== current &&\n checkIfContextChanged(current) &&\n (didReceiveUpdate = !0));\n}\nfunction renderWithHooksAgain(workInProgress, Component, props, secondArg) {\n currentlyRenderingFiber = workInProgress;\n var numberOfReRenders = 0;\n do {\n didScheduleRenderPhaseUpdateDuringThisPass && (thenableState = null);\n thenableIndexCounter = 0;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n if (25 <= numberOfReRenders) throw Error(formatProdErrorMessage(301));\n numberOfReRenders += 1;\n workInProgressHook = currentHook = null;\n if (null != workInProgress.updateQueue) {\n var children = workInProgress.updateQueue;\n children.lastEffect = null;\n children.events = null;\n children.stores = null;\n null != children.memoCache && (children.memoCache.index = 0);\n }\n ReactSharedInternals.H = HooksDispatcherOnRerender;\n children = Component(props, secondArg);\n } while (didScheduleRenderPhaseUpdateDuringThisPass);\n return children;\n}\nfunction TransitionAwareHostComponent() {\n var dispatcher = ReactSharedInternals.H,\n maybeThenable = dispatcher.useState()[0];\n maybeThenable =\n \"function\" === typeof maybeThenable.then\n ? useThenable(maybeThenable)\n : maybeThenable;\n dispatcher = dispatcher.useState()[0];\n (null !== currentHook ? currentHook.memoizedState : null) !== dispatcher &&\n (currentlyRenderingFiber.flags |= 1024);\n return maybeThenable;\n}\nfunction checkDidRenderIdHook() {\n var didRenderIdHook = 0 !== localIdCounter;\n localIdCounter = 0;\n return didRenderIdHook;\n}\nfunction bailoutHooks(current, workInProgress, lanes) {\n workInProgress.updateQueue = current.updateQueue;\n workInProgress.flags &= -2053;\n current.lanes &= ~lanes;\n}\nfunction resetHooksOnUnwind(workInProgress) {\n if (didScheduleRenderPhaseUpdate) {\n for (\n workInProgress = workInProgress.memoizedState;\n null !== workInProgress;\n\n ) {\n var queue = workInProgress.queue;\n null !== queue && (queue.pending = null);\n workInProgress = workInProgress.next;\n }\n didScheduleRenderPhaseUpdate = !1;\n }\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n thenableIndexCounter = localIdCounter = 0;\n thenableState = null;\n}\nfunction mountWorkInProgressHook() {\n var hook = {\n memoizedState: null,\n baseState: null,\n baseQueue: null,\n queue: null,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook = hook)\n : (workInProgressHook = workInProgressHook.next = hook);\n return workInProgressHook;\n}\nfunction updateWorkInProgressHook() {\n if (null === currentHook) {\n var nextCurrentHook = currentlyRenderingFiber.alternate;\n nextCurrentHook =\n null !== nextCurrentHook ? nextCurrentHook.memoizedState : null;\n } else nextCurrentHook = currentHook.next;\n var nextWorkInProgressHook =\n null === workInProgressHook\n ? currentlyRenderingFiber.memoizedState\n : workInProgressHook.next;\n if (null !== nextWorkInProgressHook)\n (workInProgressHook = nextWorkInProgressHook),\n (currentHook = nextCurrentHook);\n else {\n if (null === nextCurrentHook) {\n if (null === currentlyRenderingFiber.alternate)\n throw Error(formatProdErrorMessage(467));\n throw Error(formatProdErrorMessage(310));\n }\n currentHook = nextCurrentHook;\n nextCurrentHook = {\n memoizedState: currentHook.memoizedState,\n baseState: currentHook.baseState,\n baseQueue: currentHook.baseQueue,\n queue: currentHook.queue,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook =\n nextCurrentHook)\n : (workInProgressHook = workInProgressHook.next = nextCurrentHook);\n }\n return workInProgressHook;\n}\nfunction createFunctionComponentUpdateQueue() {\n return { lastEffect: null, events: null, stores: null, memoCache: null };\n}\nfunction useThenable(thenable) {\n var index = thenableIndexCounter;\n thenableIndexCounter += 1;\n null === thenableState && (thenableState = []);\n thenable = trackUsedThenable(thenableState, thenable, index);\n index = currentlyRenderingFiber;\n null ===\n (null === workInProgressHook\n ? index.memoizedState\n : workInProgressHook.next) &&\n ((index = index.alternate),\n (ReactSharedInternals.H =\n null === index || null === index.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate));\n return thenable;\n}\nfunction use(usable) {\n if (null !== usable && \"object\" === typeof usable) {\n if (\"function\" === typeof usable.then) return useThenable(usable);\n if (usable.$$typeof === REACT_CONTEXT_TYPE) return readContext(usable);\n }\n throw Error(formatProdErrorMessage(438, String(usable)));\n}\nfunction useMemoCache(size) {\n var memoCache = null,\n updateQueue = currentlyRenderingFiber.updateQueue;\n null !== updateQueue && (memoCache = updateQueue.memoCache);\n if (null == memoCache) {\n var current = currentlyRenderingFiber.alternate;\n null !== current &&\n ((current = current.updateQueue),\n null !== current &&\n ((current = current.memoCache),\n null != current &&\n (memoCache = {\n data: current.data.map(function (array) {\n return array.slice();\n }),\n index: 0\n })));\n }\n null == memoCache && (memoCache = { data: [], index: 0 });\n null === updateQueue &&\n ((updateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = updateQueue));\n updateQueue.memoCache = memoCache;\n updateQueue = memoCache.data[memoCache.index];\n if (void 0 === updateQueue)\n for (\n updateQueue = memoCache.data[memoCache.index] = Array(size), current = 0;\n current < size;\n current++\n )\n updateQueue[current] = REACT_MEMO_CACHE_SENTINEL;\n memoCache.index++;\n return updateQueue;\n}\nfunction basicStateReducer(state, action) {\n return \"function\" === typeof action ? action(state) : action;\n}\nfunction updateReducer(reducer) {\n var hook = updateWorkInProgressHook();\n return updateReducerImpl(hook, currentHook, reducer);\n}\nfunction updateReducerImpl(hook, current, reducer) {\n var queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var baseQueue = hook.baseQueue,\n pendingQueue = queue.pending;\n if (null !== pendingQueue) {\n if (null !== baseQueue) {\n var baseFirst = baseQueue.next;\n baseQueue.next = pendingQueue.next;\n pendingQueue.next = baseFirst;\n }\n current.baseQueue = baseQueue = pendingQueue;\n queue.pending = null;\n }\n pendingQueue = hook.baseState;\n if (null === baseQueue) hook.memoizedState = pendingQueue;\n else {\n current = baseQueue.next;\n var newBaseQueueFirst = (baseFirst = null),\n newBaseQueueLast = null,\n update = current,\n didReadFromEntangledAsyncAction$60 = !1;\n do {\n var updateLane = update.lane & -536870913;\n if (\n updateLane !== update.lane\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n var revertLane = update.revertLane;\n if (0 === revertLane)\n null !== newBaseQueueLast &&\n (newBaseQueueLast = newBaseQueueLast.next =\n {\n lane: 0,\n revertLane: 0,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n else if ((renderLanes & revertLane) === revertLane) {\n update = update.next;\n revertLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n continue;\n } else\n (updateLane = {\n lane: 0,\n revertLane: update.revertLane,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = updateLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = updateLane),\n (currentlyRenderingFiber.lanes |= revertLane),\n (workInProgressRootSkippedLanes |= revertLane);\n updateLane = update.action;\n shouldDoubleInvokeUserFnsInHooksDEV &&\n reducer(pendingQueue, updateLane);\n pendingQueue = update.hasEagerState\n ? update.eagerState\n : reducer(pendingQueue, updateLane);\n } else\n (revertLane = {\n lane: updateLane,\n revertLane: update.revertLane,\n gesture: update.gesture,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = revertLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = revertLane),\n (currentlyRenderingFiber.lanes |= updateLane),\n (workInProgressRootSkippedLanes |= updateLane);\n update = update.next;\n } while (null !== update && update !== current);\n null === newBaseQueueLast\n ? (baseFirst = pendingQueue)\n : (newBaseQueueLast.next = newBaseQueueFirst);\n if (\n !objectIs(pendingQueue, hook.memoizedState) &&\n ((didReceiveUpdate = !0),\n didReadFromEntangledAsyncAction$60 &&\n ((reducer = currentEntangledActionThenable), null !== reducer))\n )\n throw reducer;\n hook.memoizedState = pendingQueue;\n hook.baseState = baseFirst;\n hook.baseQueue = newBaseQueueLast;\n queue.lastRenderedState = pendingQueue;\n }\n null === baseQueue && (queue.lanes = 0);\n return [hook.memoizedState, queue.dispatch];\n}\nfunction rerenderReducer(reducer) {\n var hook = updateWorkInProgressHook(),\n queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var dispatch = queue.dispatch,\n lastRenderPhaseUpdate = queue.pending,\n newState = hook.memoizedState;\n if (null !== lastRenderPhaseUpdate) {\n queue.pending = null;\n var update = (lastRenderPhaseUpdate = lastRenderPhaseUpdate.next);\n do (newState = reducer(newState, update.action)), (update = update.next);\n while (update !== lastRenderPhaseUpdate);\n objectIs(newState, hook.memoizedState) || (didReceiveUpdate = !0);\n hook.memoizedState = newState;\n null === hook.baseQueue && (hook.baseState = newState);\n queue.lastRenderedState = newState;\n }\n return [newState, dispatch];\n}\nfunction updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = updateWorkInProgressHook(),\n isHydrating$jscomp$0 = isHydrating;\n if (isHydrating$jscomp$0) {\n if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else getServerSnapshot = getSnapshot();\n var snapshotChanged = !objectIs(\n (currentHook || hook).memoizedState,\n getServerSnapshot\n );\n snapshotChanged &&\n ((hook.memoizedState = getServerSnapshot), (didReceiveUpdate = !0));\n hook = hook.queue;\n updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [\n subscribe\n ]);\n if (\n hook.getSnapshot !== getSnapshot ||\n snapshotChanged ||\n (null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1)\n ) {\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n hook,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));\n isHydrating$jscomp$0 ||\n 0 !== (renderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n return getServerSnapshot;\n}\nfunction pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {\n fiber.flags |= 16384;\n fiber = { getSnapshot: getSnapshot, value: renderedSnapshot };\n getSnapshot = currentlyRenderingFiber.updateQueue;\n null === getSnapshot\n ? ((getSnapshot = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = getSnapshot),\n (getSnapshot.stores = [fiber]))\n : ((renderedSnapshot = getSnapshot.stores),\n null === renderedSnapshot\n ? (getSnapshot.stores = [fiber])\n : renderedSnapshot.push(fiber));\n}\nfunction updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {\n inst.value = nextSnapshot;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n}\nfunction subscribeToStore(fiber, inst, subscribe) {\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n });\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction forceStoreRerender(fiber) {\n var root = enqueueConcurrentRenderForLane(fiber, 2);\n null !== root && scheduleUpdateOnFiber(root, fiber, 2);\n}\nfunction mountStateImpl(initialState) {\n var hook = mountWorkInProgressHook();\n if (\"function\" === typeof initialState) {\n var initialStateInitializer = initialState;\n initialState = initialStateInitializer();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n initialStateInitializer();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n }\n hook.memoizedState = hook.baseState = initialState;\n hook.queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState\n };\n return hook;\n}\nfunction updateOptimisticImpl(hook, current, passthrough, reducer) {\n hook.baseState = passthrough;\n return updateReducerImpl(\n hook,\n currentHook,\n \"function\" === typeof reducer ? reducer : basicStateReducer\n );\n}\nfunction dispatchActionState(\n fiber,\n actionQueue,\n setPendingState,\n setState,\n payload\n) {\n if (isRenderPhaseUpdate(fiber)) throw Error(formatProdErrorMessage(485));\n fiber = actionQueue.action;\n if (null !== fiber) {\n var actionNode = {\n payload: payload,\n action: fiber,\n next: null,\n isTransition: !0,\n status: \"pending\",\n value: null,\n reason: null,\n listeners: [],\n then: function (listener) {\n actionNode.listeners.push(listener);\n }\n };\n null !== ReactSharedInternals.T\n ? setPendingState(!0)\n : (actionNode.isTransition = !1);\n setState(actionNode);\n setPendingState = actionQueue.pending;\n null === setPendingState\n ? ((actionNode.next = actionQueue.pending = actionNode),\n runActionStateAction(actionQueue, actionNode))\n : ((actionNode.next = setPendingState.next),\n (actionQueue.pending = setPendingState.next = actionNode));\n }\n}\nfunction runActionStateAction(actionQueue, node) {\n var action = node.action,\n payload = node.payload,\n prevState = actionQueue.state;\n if (node.isTransition) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = action(prevState, payload),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n handleActionReturnValue(actionQueue, node, returnValue);\n } catch (error) {\n onActionError(actionQueue, node, error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n } else\n try {\n (prevTransition = action(prevState, payload)),\n handleActionReturnValue(actionQueue, node, prevTransition);\n } catch (error$66) {\n onActionError(actionQueue, node, error$66);\n }\n}\nfunction handleActionReturnValue(actionQueue, node, returnValue) {\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ? returnValue.then(\n function (nextState) {\n onActionSuccess(actionQueue, node, nextState);\n },\n function (error) {\n return onActionError(actionQueue, node, error);\n }\n )\n : onActionSuccess(actionQueue, node, returnValue);\n}\nfunction onActionSuccess(actionQueue, actionNode, nextState) {\n actionNode.status = \"fulfilled\";\n actionNode.value = nextState;\n notifyActionListeners(actionNode);\n actionQueue.state = nextState;\n actionNode = actionQueue.pending;\n null !== actionNode &&\n ((nextState = actionNode.next),\n nextState === actionNode\n ? (actionQueue.pending = null)\n : ((nextState = nextState.next),\n (actionNode.next = nextState),\n runActionStateAction(actionQueue, nextState)));\n}\nfunction onActionError(actionQueue, actionNode, error) {\n var last = actionQueue.pending;\n actionQueue.pending = null;\n if (null !== last) {\n last = last.next;\n do\n (actionNode.status = \"rejected\"),\n (actionNode.reason = error),\n notifyActionListeners(actionNode),\n (actionNode = actionNode.next);\n while (actionNode !== last);\n }\n actionQueue.action = null;\n}\nfunction notifyActionListeners(actionNode) {\n actionNode = actionNode.listeners;\n for (var i = 0; i < actionNode.length; i++) (0, actionNode[i])();\n}\nfunction actionStateReducer(oldState, newState) {\n return newState;\n}\nfunction mountActionState(action, initialStateProp) {\n if (isHydrating) {\n var ssrFormState = workInProgressRoot.formState;\n if (null !== ssrFormState) {\n a: {\n var JSCompiler_inline_result = currentlyRenderingFiber;\n if (isHydrating) {\n if (nextHydratableInstance) {\n b: {\n var JSCompiler_inline_result$jscomp$0 = nextHydratableInstance;\n for (\n var inRootOrSingleton = rootOrSingletonContext;\n 8 !== JSCompiler_inline_result$jscomp$0.nodeType;\n\n ) {\n if (!inRootOrSingleton) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n JSCompiler_inline_result$jscomp$0 = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n if (null === JSCompiler_inline_result$jscomp$0) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n }\n inRootOrSingleton = JSCompiler_inline_result$jscomp$0.data;\n JSCompiler_inline_result$jscomp$0 =\n \"F!\" === inRootOrSingleton || \"F\" === inRootOrSingleton\n ? JSCompiler_inline_result$jscomp$0\n : null;\n }\n if (JSCompiler_inline_result$jscomp$0) {\n nextHydratableInstance = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n JSCompiler_inline_result =\n \"F!\" === JSCompiler_inline_result$jscomp$0.data;\n break a;\n }\n }\n throwOnHydrationMismatch(JSCompiler_inline_result);\n }\n JSCompiler_inline_result = !1;\n }\n JSCompiler_inline_result && (initialStateProp = ssrFormState[0]);\n }\n }\n ssrFormState = mountWorkInProgressHook();\n ssrFormState.memoizedState = ssrFormState.baseState = initialStateProp;\n JSCompiler_inline_result = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: actionStateReducer,\n lastRenderedState: initialStateProp\n };\n ssrFormState.queue = JSCompiler_inline_result;\n ssrFormState = dispatchSetState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result\n );\n JSCompiler_inline_result.dispatch = ssrFormState;\n JSCompiler_inline_result = mountStateImpl(!1);\n inRootOrSingleton = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !1,\n JSCompiler_inline_result.queue\n );\n JSCompiler_inline_result = mountWorkInProgressHook();\n JSCompiler_inline_result$jscomp$0 = {\n state: initialStateProp,\n dispatch: null,\n action: action,\n pending: null\n };\n JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;\n ssrFormState = dispatchActionState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result$jscomp$0,\n inRootOrSingleton,\n ssrFormState\n );\n JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;\n JSCompiler_inline_result.memoizedState = action;\n return [initialStateProp, ssrFormState, !1];\n}\nfunction updateActionState(action) {\n var stateHook = updateWorkInProgressHook();\n return updateActionStateImpl(stateHook, currentHook, action);\n}\nfunction updateActionStateImpl(stateHook, currentStateHook, action) {\n currentStateHook = updateReducerImpl(\n stateHook,\n currentStateHook,\n actionStateReducer\n )[0];\n stateHook = updateReducer(basicStateReducer)[0];\n if (\n \"object\" === typeof currentStateHook &&\n null !== currentStateHook &&\n \"function\" === typeof currentStateHook.then\n )\n try {\n var state = useThenable(currentStateHook);\n } catch (x) {\n if (x === SuspenseException) throw SuspenseActionException;\n throw x;\n }\n else state = currentStateHook;\n currentStateHook = updateWorkInProgressHook();\n var actionQueue = currentStateHook.queue,\n dispatch = actionQueue.dispatch;\n action !== currentStateHook.memoizedState &&\n ((currentlyRenderingFiber.flags |= 2048),\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n actionStateActionEffect.bind(null, actionQueue, action),\n null\n ));\n return [state, dispatch, stateHook];\n}\nfunction actionStateActionEffect(actionQueue, action) {\n actionQueue.action = action;\n}\nfunction rerenderActionState(action) {\n var stateHook = updateWorkInProgressHook(),\n currentStateHook = currentHook;\n if (null !== currentStateHook)\n return updateActionStateImpl(stateHook, currentStateHook, action);\n updateWorkInProgressHook();\n stateHook = stateHook.memoizedState;\n currentStateHook = updateWorkInProgressHook();\n var dispatch = currentStateHook.queue.dispatch;\n currentStateHook.memoizedState = action;\n return [stateHook, dispatch, !1];\n}\nfunction pushSimpleEffect(tag, inst, create, deps) {\n tag = { tag: tag, create: create, deps: deps, inst: inst, next: null };\n inst = currentlyRenderingFiber.updateQueue;\n null === inst &&\n ((inst = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = inst));\n create = inst.lastEffect;\n null === create\n ? (inst.lastEffect = tag.next = tag)\n : ((deps = create.next),\n (create.next = tag),\n (tag.next = deps),\n (inst.lastEffect = tag));\n return tag;\n}\nfunction updateRef() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = mountWorkInProgressHook();\n currentlyRenderingFiber.flags |= fiberFlags;\n hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n { destroy: void 0 },\n create,\n void 0 === deps ? null : deps\n );\n}\nfunction updateEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var inst = hook.memoizedState.inst;\n null !== currentHook &&\n null !== deps &&\n areHookInputsEqual(deps, currentHook.memoizedState.deps)\n ? (hook.memoizedState = pushSimpleEffect(hookFlags, inst, create, deps))\n : ((currentlyRenderingFiber.flags |= fiberFlags),\n (hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n inst,\n create,\n deps\n )));\n}\nfunction mountEffect(create, deps) {\n mountEffectImpl(8390656, 8, create, deps);\n}\nfunction updateEffect(create, deps) {\n updateEffectImpl(2048, 8, create, deps);\n}\nfunction useEffectEventImpl(payload) {\n currentlyRenderingFiber.flags |= 4;\n var componentUpdateQueue = currentlyRenderingFiber.updateQueue;\n if (null === componentUpdateQueue)\n (componentUpdateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = componentUpdateQueue),\n (componentUpdateQueue.events = [payload]);\n else {\n var events = componentUpdateQueue.events;\n null === events\n ? (componentUpdateQueue.events = [payload])\n : events.push(payload);\n }\n}\nfunction updateEvent(callback) {\n var ref = updateWorkInProgressHook().memoizedState;\n useEffectEventImpl({ ref: ref, nextImpl: callback });\n return function () {\n if (0 !== (executionContext & 2)) throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n}\nfunction updateInsertionEffect(create, deps) {\n return updateEffectImpl(4, 2, create, deps);\n}\nfunction updateLayoutEffect(create, deps) {\n return updateEffectImpl(4, 4, create, deps);\n}\nfunction imperativeHandleEffect(create, ref) {\n if (\"function\" === typeof ref) {\n create = create();\n var refCleanup = ref(create);\n return function () {\n \"function\" === typeof refCleanup ? refCleanup() : ref(null);\n };\n }\n if (null !== ref && void 0 !== ref)\n return (\n (create = create()),\n (ref.current = create),\n function () {\n ref.current = null;\n }\n );\n}\nfunction updateImperativeHandle(ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n updateEffectImpl(4, 4, imperativeHandleEffect.bind(null, create, ref), deps);\n}\nfunction mountDebugValue() {}\nfunction updateCallback(callback, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n hook.memoizedState = [callback, deps];\n return callback;\n}\nfunction updateMemo(nextCreate, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n prevState = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [prevState, deps];\n return prevState;\n}\nfunction mountDeferredValueImpl(hook, value, initialValue) {\n if (\n void 0 === initialValue ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (hook.memoizedState = value);\n hook.memoizedState = initialValue;\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return initialValue;\n}\nfunction updateDeferredValueImpl(hook, prevValue, value, initialValue) {\n if (objectIs(value, prevValue)) return value;\n if (null !== currentTreeHiddenStackCursor.current)\n return (\n (hook = mountDeferredValueImpl(hook, value, initialValue)),\n objectIs(hook, prevValue) || (didReceiveUpdate = !0),\n hook\n );\n if (\n 0 === (renderLanes & 42) ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (didReceiveUpdate = !0), (hook.memoizedState = value);\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return prevValue;\n}\nfunction startTransition(fiber, queue, pendingState, finishedState, callback) {\n var previousPriority = ReactDOMSharedInternals.p;\n ReactDOMSharedInternals.p =\n 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8;\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n dispatchOptimisticSetState(fiber, !1, queue, pendingState);\n try {\n var returnValue = callback(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n if (\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ) {\n var thenableForFinishedState = chainThenableValue(\n returnValue,\n finishedState\n );\n dispatchSetStateInternal(\n fiber,\n queue,\n thenableForFinishedState,\n requestUpdateLane(fiber)\n );\n } else\n dispatchSetStateInternal(\n fiber,\n queue,\n finishedState,\n requestUpdateLane(fiber)\n );\n } catch (error) {\n dispatchSetStateInternal(\n fiber,\n queue,\n { then: function () {}, status: \"rejected\", reason: error },\n requestUpdateLane()\n );\n } finally {\n (ReactDOMSharedInternals.p = previousPriority),\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n}\nfunction noop() {}\nfunction startHostTransition(formFiber, pendingState, action, formData) {\n if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));\n var queue = ensureFormComponentIsStateful(formFiber).queue;\n startTransition(\n formFiber,\n queue,\n pendingState,\n sharedNotPendingObject,\n null === action\n ? noop\n : function () {\n requestFormReset$1(formFiber);\n return action(formData);\n }\n );\n}\nfunction ensureFormComponentIsStateful(formFiber) {\n var existingStateHook = formFiber.memoizedState;\n if (null !== existingStateHook) return existingStateHook;\n existingStateHook = {\n memoizedState: sharedNotPendingObject,\n baseState: sharedNotPendingObject,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: sharedNotPendingObject\n },\n next: null\n };\n var initialResetState = {};\n existingStateHook.next = {\n memoizedState: initialResetState,\n baseState: initialResetState,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialResetState\n },\n next: null\n };\n formFiber.memoizedState = existingStateHook;\n formFiber = formFiber.alternate;\n null !== formFiber && (formFiber.memoizedState = existingStateHook);\n return existingStateHook;\n}\nfunction requestFormReset$1(formFiber) {\n var stateHook = ensureFormComponentIsStateful(formFiber);\n null === stateHook.next && (stateHook = formFiber.alternate.memoizedState);\n dispatchSetStateInternal(\n formFiber,\n stateHook.next.queue,\n {},\n requestUpdateLane()\n );\n}\nfunction useHostTransitionStatus() {\n return readContext(HostTransitionContext);\n}\nfunction updateId() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction updateRefresh() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction refreshCache(fiber) {\n for (var provider = fiber.return; null !== provider; ) {\n switch (provider.tag) {\n case 24:\n case 3:\n var lane = requestUpdateLane();\n fiber = createUpdate(lane);\n var root$69 = enqueueUpdate(provider, fiber, lane);\n null !== root$69 &&\n (scheduleUpdateOnFiber(root$69, provider, lane),\n entangleTransitions(root$69, provider, lane));\n provider = { cache: createCache() };\n fiber.payload = provider;\n return;\n }\n provider = provider.return;\n }\n}\nfunction dispatchReducerAction(fiber, queue, action) {\n var lane = requestUpdateLane();\n action = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n isRenderPhaseUpdate(fiber)\n ? enqueueRenderPhaseUpdate(queue, action)\n : ((action = enqueueConcurrentHookUpdate(fiber, queue, action, lane)),\n null !== action &&\n (scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane)));\n}\nfunction dispatchSetState(fiber, queue, action) {\n var lane = requestUpdateLane();\n dispatchSetStateInternal(fiber, queue, action, lane);\n}\nfunction dispatchSetStateInternal(fiber, queue, action, lane) {\n var update = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) enqueueRenderPhaseUpdate(queue, update);\n else {\n var alternate = fiber.alternate;\n if (\n 0 === fiber.lanes &&\n (null === alternate || 0 === alternate.lanes) &&\n ((alternate = queue.lastRenderedReducer), null !== alternate)\n )\n try {\n var currentState = queue.lastRenderedState,\n eagerState = alternate(currentState, action);\n update.hasEagerState = !0;\n update.eagerState = eagerState;\n if (objectIs(eagerState, currentState))\n return (\n enqueueUpdate$1(fiber, queue, update, 0),\n null === workInProgressRoot && finishQueueingConcurrentUpdates(),\n !1\n );\n } catch (error) {\n } finally {\n }\n action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n if (null !== action)\n return (\n scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane),\n !0\n );\n }\n return !1;\n}\nfunction dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {\n action = {\n lane: 2,\n revertLane: requestTransitionLane(),\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) {\n if (throwIfDuringRender) throw Error(formatProdErrorMessage(479));\n } else\n (throwIfDuringRender = enqueueConcurrentHookUpdate(\n fiber,\n queue,\n action,\n 2\n )),\n null !== throwIfDuringRender &&\n scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);\n}\nfunction isRenderPhaseUpdate(fiber) {\n var alternate = fiber.alternate;\n return (\n fiber === currentlyRenderingFiber ||\n (null !== alternate && alternate === currentlyRenderingFiber)\n );\n}\nfunction enqueueRenderPhaseUpdate(queue, update) {\n didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =\n !0;\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n}\nfunction entangleTransitionUpdate(root, queue, lane) {\n if (0 !== (lane & 4194048)) {\n var queueLanes = queue.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n queue.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nvar ContextOnlyDispatcher = {\n readContext: readContext,\n use: use,\n useCallback: throwInvalidHookError,\n useContext: throwInvalidHookError,\n useEffect: throwInvalidHookError,\n useImperativeHandle: throwInvalidHookError,\n useLayoutEffect: throwInvalidHookError,\n useInsertionEffect: throwInvalidHookError,\n useMemo: throwInvalidHookError,\n useReducer: throwInvalidHookError,\n useRef: throwInvalidHookError,\n useState: throwInvalidHookError,\n useDebugValue: throwInvalidHookError,\n useDeferredValue: throwInvalidHookError,\n useTransition: throwInvalidHookError,\n useSyncExternalStore: throwInvalidHookError,\n useId: throwInvalidHookError,\n useHostTransitionStatus: throwInvalidHookError,\n useFormState: throwInvalidHookError,\n useActionState: throwInvalidHookError,\n useOptimistic: throwInvalidHookError,\n useMemoCache: throwInvalidHookError,\n useCacheRefresh: throwInvalidHookError\n};\nContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;\nvar HooksDispatcherOnMount = {\n readContext: readContext,\n use: use,\n useCallback: function (callback, deps) {\n mountWorkInProgressHook().memoizedState = [\n callback,\n void 0 === deps ? null : deps\n ];\n return callback;\n },\n useContext: readContext,\n useEffect: mountEffect,\n useImperativeHandle: function (ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n mountEffectImpl(\n 4194308,\n 4,\n imperativeHandleEffect.bind(null, create, ref),\n deps\n );\n },\n useLayoutEffect: function (create, deps) {\n return mountEffectImpl(4194308, 4, create, deps);\n },\n useInsertionEffect: function (create, deps) {\n mountEffectImpl(4, 2, create, deps);\n },\n useMemo: function (nextCreate, deps) {\n var hook = mountWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var nextValue = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [nextValue, deps];\n return nextValue;\n },\n useReducer: function (reducer, initialArg, init) {\n var hook = mountWorkInProgressHook();\n if (void 0 !== init) {\n var initialState = init(initialArg);\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n init(initialArg);\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n } else initialState = initialArg;\n hook.memoizedState = hook.baseState = initialState;\n reducer = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: reducer,\n lastRenderedState: initialState\n };\n hook.queue = reducer;\n reducer = reducer.dispatch = dispatchReducerAction.bind(\n null,\n currentlyRenderingFiber,\n reducer\n );\n return [hook.memoizedState, reducer];\n },\n useRef: function (initialValue) {\n var hook = mountWorkInProgressHook();\n initialValue = { current: initialValue };\n return (hook.memoizedState = initialValue);\n },\n useState: function (initialState) {\n initialState = mountStateImpl(initialState);\n var queue = initialState.queue,\n dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);\n queue.dispatch = dispatch;\n return [initialState.memoizedState, dispatch];\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = mountWorkInProgressHook();\n return mountDeferredValueImpl(hook, value, initialValue);\n },\n useTransition: function () {\n var stateHook = mountStateImpl(!1);\n stateHook = startTransition.bind(\n null,\n currentlyRenderingFiber,\n stateHook.queue,\n !0,\n !1\n );\n mountWorkInProgressHook().memoizedState = stateHook;\n return [!1, stateHook];\n },\n useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = mountWorkInProgressHook();\n if (isHydrating) {\n if (void 0 === getServerSnapshot)\n throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else {\n getServerSnapshot = getSnapshot();\n if (null === workInProgressRoot)\n throw Error(formatProdErrorMessage(349));\n 0 !== (workInProgressRootRenderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n hook.memoizedState = getServerSnapshot;\n var inst = { value: getServerSnapshot, getSnapshot: getSnapshot };\n hook.queue = inst;\n mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [\n subscribe\n ]);\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n inst,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n return getServerSnapshot;\n },\n useId: function () {\n var hook = mountWorkInProgressHook(),\n identifierPrefix = workInProgressRoot.identifierPrefix;\n if (isHydrating) {\n var JSCompiler_inline_result = treeContextOverflow;\n var idWithLeadingBit = treeContextId;\n JSCompiler_inline_result =\n (\n idWithLeadingBit & ~(1 << (32 - clz32(idWithLeadingBit) - 1))\n ).toString(32) + JSCompiler_inline_result;\n identifierPrefix =\n \"_\" + identifierPrefix + \"R_\" + JSCompiler_inline_result;\n JSCompiler_inline_result = localIdCounter++;\n 0 < JSCompiler_inline_result &&\n (identifierPrefix += \"H\" + JSCompiler_inline_result.toString(32));\n identifierPrefix += \"_\";\n } else\n (JSCompiler_inline_result = globalClientIdCounter++),\n (identifierPrefix =\n \"_\" +\n identifierPrefix +\n \"r_\" +\n JSCompiler_inline_result.toString(32) +\n \"_\");\n return (hook.memoizedState = identifierPrefix);\n },\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: mountActionState,\n useActionState: mountActionState,\n useOptimistic: function (passthrough) {\n var hook = mountWorkInProgressHook();\n hook.memoizedState = hook.baseState = passthrough;\n var queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: null,\n lastRenderedState: null\n };\n hook.queue = queue;\n hook = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !0,\n queue\n );\n queue.dispatch = hook;\n return [passthrough, hook];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: function () {\n return (mountWorkInProgressHook().memoizedState = refreshCache.bind(\n null,\n currentlyRenderingFiber\n ));\n },\n useEffectEvent: function (callback) {\n var hook = mountWorkInProgressHook(),\n ref = { impl: callback };\n hook.memoizedState = ref;\n return function () {\n if (0 !== (executionContext & 2))\n throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n }\n },\n HooksDispatcherOnUpdate = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: updateReducer,\n useRef: updateRef,\n useState: function () {\n return updateReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = updateReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: updateActionState,\n useActionState: updateActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n };\nHooksDispatcherOnUpdate.useEffectEvent = updateEvent;\nvar HooksDispatcherOnRerender = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: rerenderReducer,\n useRef: updateRef,\n useState: function () {\n return rerenderReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return null === currentHook\n ? mountDeferredValueImpl(hook, value, initialValue)\n : updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = rerenderReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: rerenderActionState,\n useActionState: rerenderActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n if (null !== currentHook)\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n hook.baseState = passthrough;\n return [passthrough, hook.queue.dispatch];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n};\nHooksDispatcherOnRerender.useEffectEvent = updateEvent;\nfunction applyDerivedStateFromProps(\n workInProgress,\n ctor,\n getDerivedStateFromProps,\n nextProps\n) {\n ctor = workInProgress.memoizedState;\n getDerivedStateFromProps = getDerivedStateFromProps(nextProps, ctor);\n getDerivedStateFromProps =\n null === getDerivedStateFromProps || void 0 === getDerivedStateFromProps\n ? ctor\n : assign({}, ctor, getDerivedStateFromProps);\n workInProgress.memoizedState = getDerivedStateFromProps;\n 0 === workInProgress.lanes &&\n (workInProgress.updateQueue.baseState = getDerivedStateFromProps);\n}\nvar classComponentUpdater = {\n enqueueSetState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueReplaceState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 1;\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueForceUpdate: function (inst, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 2;\n void 0 !== callback && null !== callback && (update.callback = callback);\n callback = enqueueUpdate(inst, update, lane);\n null !== callback &&\n (scheduleUpdateOnFiber(callback, inst, lane),\n entangleTransitions(callback, inst, lane));\n }\n};\nfunction checkShouldComponentUpdate(\n workInProgress,\n ctor,\n oldProps,\n newProps,\n oldState,\n newState,\n nextContext\n) {\n workInProgress = workInProgress.stateNode;\n return \"function\" === typeof workInProgress.shouldComponentUpdate\n ? workInProgress.shouldComponentUpdate(newProps, newState, nextContext)\n : ctor.prototype && ctor.prototype.isPureReactComponent\n ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)\n : !0;\n}\nfunction callComponentWillReceiveProps(\n workInProgress,\n instance,\n newProps,\n nextContext\n) {\n workInProgress = instance.state;\n \"function\" === typeof instance.componentWillReceiveProps &&\n instance.componentWillReceiveProps(newProps, nextContext);\n \"function\" === typeof instance.UNSAFE_componentWillReceiveProps &&\n instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);\n instance.state !== workInProgress &&\n classComponentUpdater.enqueueReplaceState(instance, instance.state, null);\n}\nfunction resolveClassComponentProps(Component, baseProps) {\n var newProps = baseProps;\n if (\"ref\" in baseProps) {\n newProps = {};\n for (var propName in baseProps)\n \"ref\" !== propName && (newProps[propName] = baseProps[propName]);\n }\n if ((Component = Component.defaultProps)) {\n newProps === baseProps && (newProps = assign({}, newProps));\n for (var propName$73 in Component)\n void 0 === newProps[propName$73] &&\n (newProps[propName$73] = Component[propName$73]);\n }\n return newProps;\n}\nfunction defaultOnUncaughtError(error) {\n reportGlobalError(error);\n}\nfunction defaultOnCaughtError(error) {\n console.error(error);\n}\nfunction defaultOnRecoverableError(error) {\n reportGlobalError(error);\n}\nfunction logUncaughtError(root, errorInfo) {\n try {\n var onUncaughtError = root.onUncaughtError;\n onUncaughtError(errorInfo.value, { componentStack: errorInfo.stack });\n } catch (e$74) {\n setTimeout(function () {\n throw e$74;\n });\n }\n}\nfunction logCaughtError(root, boundary, errorInfo) {\n try {\n var onCaughtError = root.onCaughtError;\n onCaughtError(errorInfo.value, {\n componentStack: errorInfo.stack,\n errorBoundary: 1 === boundary.tag ? boundary.stateNode : null\n });\n } catch (e$75) {\n setTimeout(function () {\n throw e$75;\n });\n }\n}\nfunction createRootErrorUpdate(root, errorInfo, lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n lane.payload = { element: null };\n lane.callback = function () {\n logUncaughtError(root, errorInfo);\n };\n return lane;\n}\nfunction createClassErrorUpdate(lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n return lane;\n}\nfunction initializeClassErrorUpdate(update, root, fiber, errorInfo) {\n var getDerivedStateFromError = fiber.type.getDerivedStateFromError;\n if (\"function\" === typeof getDerivedStateFromError) {\n var error = errorInfo.value;\n update.payload = function () {\n return getDerivedStateFromError(error);\n };\n update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n };\n }\n var inst = fiber.stateNode;\n null !== inst &&\n \"function\" === typeof inst.componentDidCatch &&\n (update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n \"function\" !== typeof getDerivedStateFromError &&\n (null === legacyErrorBoundariesThatAlreadyFailed\n ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this]))\n : legacyErrorBoundariesThatAlreadyFailed.add(this));\n var stack = errorInfo.stack;\n this.componentDidCatch(errorInfo.value, {\n componentStack: null !== stack ? stack : \"\"\n });\n });\n}\nfunction throwException(\n root,\n returnFiber,\n sourceFiber,\n value,\n rootRenderLanes\n) {\n sourceFiber.flags |= 32768;\n if (\n null !== value &&\n \"object\" === typeof value &&\n \"function\" === typeof value.then\n ) {\n returnFiber = sourceFiber.alternate;\n null !== returnFiber &&\n propagateParentContextChanges(\n returnFiber,\n sourceFiber,\n rootRenderLanes,\n !0\n );\n sourceFiber = suspenseHandlerStackCursor.current;\n if (null !== sourceFiber) {\n switch (sourceFiber.tag) {\n case 31:\n case 13:\n return (\n null === shellBoundary\n ? renderDidSuspendDelayIfPossible()\n : null === sourceFiber.alternate &&\n 0 === workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 3),\n (sourceFiber.flags &= -257),\n (sourceFiber.flags |= 65536),\n (sourceFiber.lanes = rootRenderLanes),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? (sourceFiber.updateQueue = new Set([value]))\n : returnFiber.add(value),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n case 22:\n return (\n (sourceFiber.flags |= 65536),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? ((returnFiber = {\n transitions: null,\n markerInstances: null,\n retryQueue: new Set([value])\n }),\n (sourceFiber.updateQueue = returnFiber))\n : ((sourceFiber = returnFiber.retryQueue),\n null === sourceFiber\n ? (returnFiber.retryQueue = new Set([value]))\n : sourceFiber.add(value)),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n }\n throw Error(formatProdErrorMessage(435, sourceFiber.tag));\n }\n attachPingListener(root, value, rootRenderLanes);\n renderDidSuspendDelayIfPossible();\n return !1;\n }\n if (isHydrating)\n return (\n (returnFiber = suspenseHandlerStackCursor.current),\n null !== returnFiber\n ? (0 === (returnFiber.flags & 65536) && (returnFiber.flags |= 256),\n (returnFiber.flags |= 65536),\n (returnFiber.lanes = rootRenderLanes),\n value !== HydrationMismatchException &&\n ((root = Error(formatProdErrorMessage(422), { cause: value })),\n queueHydrationError(createCapturedValueAtFiber(root, sourceFiber))))\n : (value !== HydrationMismatchException &&\n ((returnFiber = Error(formatProdErrorMessage(423), {\n cause: value\n })),\n queueHydrationError(\n createCapturedValueAtFiber(returnFiber, sourceFiber)\n )),\n (root = root.current.alternate),\n (root.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (root.lanes |= rootRenderLanes),\n (value = createCapturedValueAtFiber(value, sourceFiber)),\n (rootRenderLanes = createRootErrorUpdate(\n root.stateNode,\n value,\n rootRenderLanes\n )),\n enqueueCapturedUpdate(root, rootRenderLanes),\n 4 !== workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 2)),\n !1\n );\n var wrapperError = Error(formatProdErrorMessage(520), { cause: value });\n wrapperError = createCapturedValueAtFiber(wrapperError, sourceFiber);\n null === workInProgressRootConcurrentErrors\n ? (workInProgressRootConcurrentErrors = [wrapperError])\n : workInProgressRootConcurrentErrors.push(wrapperError);\n 4 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2);\n if (null === returnFiber) return !0;\n value = createCapturedValueAtFiber(value, sourceFiber);\n sourceFiber = returnFiber;\n do {\n switch (sourceFiber.tag) {\n case 3:\n return (\n (sourceFiber.flags |= 65536),\n (root = rootRenderLanes & -rootRenderLanes),\n (sourceFiber.lanes |= root),\n (root = createRootErrorUpdate(sourceFiber.stateNode, value, root)),\n enqueueCapturedUpdate(sourceFiber, root),\n !1\n );\n case 1:\n if (\n ((returnFiber = sourceFiber.type),\n (wrapperError = sourceFiber.stateNode),\n 0 === (sourceFiber.flags & 128) &&\n (\"function\" === typeof returnFiber.getDerivedStateFromError ||\n (null !== wrapperError &&\n \"function\" === typeof wrapperError.componentDidCatch &&\n (null === legacyErrorBoundariesThatAlreadyFailed ||\n !legacyErrorBoundariesThatAlreadyFailed.has(wrapperError)))))\n )\n return (\n (sourceFiber.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (sourceFiber.lanes |= rootRenderLanes),\n (rootRenderLanes = createClassErrorUpdate(rootRenderLanes)),\n initializeClassErrorUpdate(\n rootRenderLanes,\n root,\n sourceFiber,\n value\n ),\n enqueueCapturedUpdate(sourceFiber, rootRenderLanes),\n !1\n );\n }\n sourceFiber = sourceFiber.return;\n } while (null !== sourceFiber);\n return !1;\n}\nvar SelectiveHydrationException = Error(formatProdErrorMessage(461)),\n didReceiveUpdate = !1;\nfunction reconcileChildren(current, workInProgress, nextChildren, renderLanes) {\n workInProgress.child =\n null === current\n ? mountChildFibers(workInProgress, null, nextChildren, renderLanes)\n : reconcileChildFibers(\n workInProgress,\n current.child,\n nextChildren,\n renderLanes\n );\n}\nfunction updateForwardRef(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n Component = Component.render;\n var ref = workInProgress.ref;\n if (\"ref\" in nextProps) {\n var propsWithoutRef = {};\n for (var key in nextProps)\n \"ref\" !== key && (propsWithoutRef[key] = nextProps[key]);\n } else propsWithoutRef = nextProps;\n prepareToReadContext(workInProgress);\n nextProps = renderWithHooks(\n current,\n workInProgress,\n Component,\n propsWithoutRef,\n ref,\n renderLanes\n );\n key = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && key && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null === current) {\n var type = Component.type;\n if (\n \"function\" === typeof type &&\n !shouldConstruct(type) &&\n void 0 === type.defaultProps &&\n null === Component.compare\n )\n return (\n (workInProgress.tag = 15),\n (workInProgress.type = type),\n updateSimpleMemoComponent(\n current,\n workInProgress,\n type,\n nextProps,\n renderLanes\n )\n );\n current = createFiberFromTypeAndProps(\n Component.type,\n null,\n nextProps,\n workInProgress,\n workInProgress.mode,\n renderLanes\n );\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n }\n type = current.child;\n if (!checkScheduledUpdateOrContext(current, renderLanes)) {\n var prevProps = type.memoizedProps;\n Component = Component.compare;\n Component = null !== Component ? Component : shallowEqual;\n if (Component(prevProps, nextProps) && current.ref === workInProgress.ref)\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n }\n workInProgress.flags |= 1;\n current = createWorkInProgress(type, nextProps);\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n}\nfunction updateSimpleMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null !== current) {\n var prevProps = current.memoizedProps;\n if (\n shallowEqual(prevProps, nextProps) &&\n current.ref === workInProgress.ref\n )\n if (\n ((didReceiveUpdate = !1),\n (workInProgress.pendingProps = nextProps = prevProps),\n checkScheduledUpdateOrContext(current, renderLanes))\n )\n 0 !== (current.flags & 131072) && (didReceiveUpdate = !0);\n else\n return (\n (workInProgress.lanes = current.lanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n }\n return updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n );\n}\nfunction updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n nextProps\n) {\n var nextChildren = nextProps.children,\n prevState = null !== current ? current.memoizedState : null;\n null === current &&\n null === workInProgress.stateNode &&\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n if (\"hidden\" === nextProps.mode) {\n if (0 !== (workInProgress.flags & 128)) {\n prevState =\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes;\n if (null !== current) {\n nextProps = workInProgress.child = current.child;\n for (nextChildren = 0; null !== nextProps; )\n (nextChildren =\n nextChildren | nextProps.lanes | nextProps.childLanes),\n (nextProps = nextProps.sibling);\n nextProps = nextChildren & ~prevState;\n } else (nextProps = 0), (workInProgress.child = null);\n return deferHiddenOffscreenComponent(\n current,\n workInProgress,\n prevState,\n renderLanes,\n nextProps\n );\n }\n if (0 !== (renderLanes & 536870912))\n (workInProgress.memoizedState = { baseLanes: 0, cachePool: null }),\n null !== current &&\n pushTransition(\n workInProgress,\n null !== prevState ? prevState.cachePool : null\n ),\n null !== prevState\n ? pushHiddenContext(workInProgress, prevState)\n : reuseHiddenContextOnStack(),\n pushOffscreenSuspenseHandler(workInProgress);\n else\n return (\n (nextProps = workInProgress.lanes = 536870912),\n deferHiddenOffscreenComponent(\n current,\n workInProgress,\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes,\n renderLanes,\n nextProps\n )\n );\n } else\n null !== prevState\n ? (pushTransition(workInProgress, prevState.cachePool),\n pushHiddenContext(workInProgress, prevState),\n reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.memoizedState = null))\n : (null !== current && pushTransition(workInProgress, null),\n reuseHiddenContextOnStack(),\n reuseSuspenseHandlerOnStack(workInProgress));\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nfunction bailoutOffscreenComponent(current, workInProgress) {\n (null !== current && 22 === current.tag) ||\n null !== workInProgress.stateNode ||\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n return workInProgress.sibling;\n}\nfunction deferHiddenOffscreenComponent(\n current,\n workInProgress,\n nextBaseLanes,\n renderLanes,\n remainingChildLanes\n) {\n var JSCompiler_inline_result = peekCacheFromPool();\n JSCompiler_inline_result =\n null === JSCompiler_inline_result\n ? null\n : { parent: CacheContext._currentValue, pool: JSCompiler_inline_result };\n workInProgress.memoizedState = {\n baseLanes: nextBaseLanes,\n cachePool: JSCompiler_inline_result\n };\n null !== current && pushTransition(workInProgress, null);\n reuseHiddenContextOnStack();\n pushOffscreenSuspenseHandler(workInProgress);\n null !== current &&\n propagateParentContextChanges(current, workInProgress, renderLanes, !0);\n workInProgress.childLanes = remainingChildLanes;\n return null;\n}\nfunction mountActivityChildren(workInProgress, nextProps) {\n nextProps = mountWorkInProgressOffscreenFiber(\n { mode: nextProps.mode, children: nextProps.children },\n workInProgress.mode\n );\n nextProps.ref = workInProgress.ref;\n workInProgress.child = nextProps;\n nextProps.return = workInProgress;\n return nextProps;\n}\nfunction retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountActivityChildren(workInProgress, workInProgress.pendingProps);\n current.flags |= 2;\n popSuspenseHandler(workInProgress);\n workInProgress.memoizedState = null;\n return current;\n}\nfunction updateActivityComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n didSuspend = 0 !== (workInProgress.flags & 128);\n workInProgress.flags &= -129;\n if (null === current) {\n if (isHydrating) {\n if (\"hidden\" === nextProps.mode)\n return (\n (current = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.lanes = 536870912),\n bailoutOffscreenComponent(null, current)\n );\n pushDehydratedActivitySuspenseHandler(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" === current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n workInProgress.lanes = 536870912;\n return null;\n }\n return mountActivityChildren(workInProgress, nextProps);\n }\n var prevState = current.memoizedState;\n if (null !== prevState) {\n var dehydrated = prevState.dehydrated;\n pushDehydratedActivitySuspenseHandler(workInProgress);\n if (didSuspend)\n if (workInProgress.flags & 256)\n (workInProgress.flags &= -257),\n (workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n ));\n else if (null !== workInProgress.memoizedState)\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null);\n else throw Error(formatProdErrorMessage(558));\n else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (didSuspend = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || didSuspend)\n ) {\n nextProps = workInProgressRoot;\n if (\n null !== nextProps &&\n ((dehydrated = getBumpedLaneForHydration(nextProps, renderLanes)),\n 0 !== dehydrated && dehydrated !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = dehydrated),\n enqueueConcurrentRenderForLane(current, dehydrated),\n scheduleUpdateOnFiber(nextProps, current, dehydrated),\n SelectiveHydrationException)\n );\n renderDidSuspendDelayIfPossible();\n workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n (current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(dehydrated.nextSibling)),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.flags |= 4096);\n return workInProgress;\n }\n current = createWorkInProgress(current.child, {\n mode: nextProps.mode,\n children: nextProps.children\n });\n current.ref = workInProgress.ref;\n workInProgress.child = current;\n current.return = workInProgress;\n return current;\n}\nfunction markRef(current, workInProgress) {\n var ref = workInProgress.ref;\n if (null === ref)\n null !== current &&\n null !== current.ref &&\n (workInProgress.flags |= 4194816);\n else {\n if (\"function\" !== typeof ref && \"object\" !== typeof ref)\n throw Error(formatProdErrorMessage(284));\n if (null === current || current.ref !== ref)\n workInProgress.flags |= 4194816;\n }\n}\nfunction updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n Component = renderWithHooks(\n current,\n workInProgress,\n Component,\n nextProps,\n void 0,\n renderLanes\n );\n nextProps = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && nextProps && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, Component, renderLanes);\n return workInProgress.child;\n}\nfunction replayFunctionComponent(\n current,\n workInProgress,\n nextProps,\n Component,\n secondArg,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n workInProgress.updateQueue = null;\n nextProps = renderWithHooksAgain(\n workInProgress,\n Component,\n nextProps,\n secondArg\n );\n finishRenderingHooks(current);\n Component = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && Component && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateClassComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n if (null === workInProgress.stateNode) {\n var context = emptyContextObject,\n contextType = Component.contextType;\n \"object\" === typeof contextType &&\n null !== contextType &&\n (context = readContext(contextType));\n context = new Component(nextProps, context);\n workInProgress.memoizedState =\n null !== context.state && void 0 !== context.state ? context.state : null;\n context.updater = classComponentUpdater;\n workInProgress.stateNode = context;\n context._reactInternals = workInProgress;\n context = workInProgress.stateNode;\n context.props = nextProps;\n context.state = workInProgress.memoizedState;\n context.refs = {};\n initializeUpdateQueue(workInProgress);\n contextType = Component.contextType;\n context.context =\n \"object\" === typeof contextType && null !== contextType\n ? readContext(contextType)\n : emptyContextObject;\n context.state = workInProgress.memoizedState;\n contextType = Component.getDerivedStateFromProps;\n \"function\" === typeof contextType &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n contextType,\n nextProps\n ),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof Component.getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n ((contextType = context.state),\n \"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount(),\n contextType !== context.state &&\n classComponentUpdater.enqueueReplaceState(context, context.state, null),\n processUpdateQueue(workInProgress, nextProps, context, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction(),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308);\n nextProps = !0;\n } else if (null === current) {\n context = workInProgress.stateNode;\n var unresolvedOldProps = workInProgress.memoizedProps,\n oldProps = resolveClassComponentProps(Component, unresolvedOldProps);\n context.props = oldProps;\n var oldContext = context.context,\n contextType$jscomp$0 = Component.contextType;\n contextType = emptyContextObject;\n \"object\" === typeof contextType$jscomp$0 &&\n null !== contextType$jscomp$0 &&\n (contextType = readContext(contextType$jscomp$0));\n var getDerivedStateFromProps = Component.getDerivedStateFromProps;\n contextType$jscomp$0 =\n \"function\" === typeof getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate;\n unresolvedOldProps = workInProgress.pendingProps !== unresolvedOldProps;\n contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((unresolvedOldProps || oldContext !== contextType) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n contextType\n ));\n hasForceUpdate = !1;\n var oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n oldContext = workInProgress.memoizedState;\n unresolvedOldProps || oldState !== oldContext || hasForceUpdate\n ? (\"function\" === typeof getDerivedStateFromProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n getDerivedStateFromProps,\n nextProps\n ),\n (oldContext = workInProgress.memoizedState)),\n (oldProps =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n oldProps,\n nextProps,\n oldState,\n oldContext,\n contextType\n ))\n ? (contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n (\"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount()),\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = oldContext)),\n (context.props = nextProps),\n (context.state = oldContext),\n (context.context = contextType),\n (nextProps = oldProps))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (nextProps = !1));\n } else {\n context = workInProgress.stateNode;\n cloneUpdateQueue(current, workInProgress);\n contextType = workInProgress.memoizedProps;\n contextType$jscomp$0 = resolveClassComponentProps(Component, contextType);\n context.props = contextType$jscomp$0;\n getDerivedStateFromProps = workInProgress.pendingProps;\n oldState = context.context;\n oldContext = Component.contextType;\n oldProps = emptyContextObject;\n \"object\" === typeof oldContext &&\n null !== oldContext &&\n (oldProps = readContext(oldContext));\n unresolvedOldProps = Component.getDerivedStateFromProps;\n (oldContext =\n \"function\" === typeof unresolvedOldProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate) ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((contextType !== getDerivedStateFromProps || oldState !== oldProps) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n oldProps\n ));\n hasForceUpdate = !1;\n oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n var newState = workInProgress.memoizedState;\n contextType !== getDerivedStateFromProps ||\n oldState !== newState ||\n hasForceUpdate ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies))\n ? (\"function\" === typeof unresolvedOldProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n unresolvedOldProps,\n nextProps\n ),\n (newState = workInProgress.memoizedState)),\n (contextType$jscomp$0 =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n contextType$jscomp$0,\n nextProps,\n oldState,\n newState,\n oldProps\n ) ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies)))\n ? (oldContext ||\n (\"function\" !== typeof context.UNSAFE_componentWillUpdate &&\n \"function\" !== typeof context.componentWillUpdate) ||\n (\"function\" === typeof context.componentWillUpdate &&\n context.componentWillUpdate(nextProps, newState, oldProps),\n \"function\" === typeof context.UNSAFE_componentWillUpdate &&\n context.UNSAFE_componentWillUpdate(\n nextProps,\n newState,\n oldProps\n )),\n \"function\" === typeof context.componentDidUpdate &&\n (workInProgress.flags |= 4),\n \"function\" === typeof context.getSnapshotBeforeUpdate &&\n (workInProgress.flags |= 1024))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = newState)),\n (context.props = nextProps),\n (context.state = newState),\n (context.context = oldProps),\n (nextProps = contextType$jscomp$0))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (nextProps = !1));\n }\n context = nextProps;\n markRef(current, workInProgress);\n nextProps = 0 !== (workInProgress.flags & 128);\n context || nextProps\n ? ((context = workInProgress.stateNode),\n (Component =\n nextProps && \"function\" !== typeof Component.getDerivedStateFromError\n ? null\n : context.render()),\n (workInProgress.flags |= 1),\n null !== current && nextProps\n ? ((workInProgress.child = reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n )),\n (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n Component,\n renderLanes\n )))\n : reconcileChildren(current, workInProgress, Component, renderLanes),\n (workInProgress.memoizedState = context.state),\n (current = workInProgress.child))\n : (current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n ));\n return current;\n}\nfunction mountHostRootWithoutHydrating(\n current,\n workInProgress,\n nextChildren,\n renderLanes\n) {\n resetHydrationState();\n workInProgress.flags |= 256;\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nvar SUSPENDED_MARKER = {\n dehydrated: null,\n treeContext: null,\n retryLane: 0,\n hydrationErrors: null\n};\nfunction mountSuspenseOffscreenState(renderLanes) {\n return { baseLanes: renderLanes, cachePool: getSuspendedCache() };\n}\nfunction getRemainingWorkInPrimaryTree(\n current,\n primaryTreeDidDefer,\n renderLanes\n) {\n current = null !== current ? current.childLanes & ~renderLanes : 0;\n primaryTreeDidDefer && (current |= workInProgressDeferredLane);\n return current;\n}\nfunction updateSuspenseComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n showFallback = !1,\n didSuspend = 0 !== (workInProgress.flags & 128),\n JSCompiler_temp;\n (JSCompiler_temp = didSuspend) ||\n (JSCompiler_temp =\n null !== current && null === current.memoizedState\n ? !1\n : 0 !== (suspenseStackCursor.current & 2));\n JSCompiler_temp && ((showFallback = !0), (workInProgress.flags &= -129));\n JSCompiler_temp = 0 !== (workInProgress.flags & 32);\n workInProgress.flags &= -33;\n if (null === current) {\n if (isHydrating) {\n showFallback\n ? pushPrimaryTreeSuspenseHandler(workInProgress)\n : reuseSuspenseHandlerOnStack(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" !== current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n isSuspenseInstanceFallback(current)\n ? (workInProgress.lanes = 32)\n : (workInProgress.lanes = 536870912);\n return null;\n }\n var nextPrimaryChildren = nextProps.children;\n nextProps = nextProps.fallback;\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (showFallback = workInProgress.mode),\n (nextPrimaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"hidden\", children: nextPrimaryChildren },\n showFallback\n )),\n (nextProps = createFiberFromFragment(\n nextProps,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.sibling = nextProps),\n (workInProgress.child = nextPrimaryChildren),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState = mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(null, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);\n }\n var prevState = current.memoizedState;\n if (\n null !== prevState &&\n ((nextPrimaryChildren = prevState.dehydrated), null !== nextPrimaryChildren)\n ) {\n if (didSuspend)\n workInProgress.flags & 256\n ? (pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags &= -257),\n (workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n )))\n : null !== workInProgress.memoizedState\n ? (reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null))\n : (reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (nextProps = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: nextProps.children },\n showFallback\n )),\n (nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n ),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState =\n mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n (workInProgress = bailoutOffscreenComponent(null, nextProps)));\n else if (\n (pushPrimaryTreeSuspenseHandler(workInProgress),\n isSuspenseInstanceFallback(nextPrimaryChildren))\n ) {\n JSCompiler_temp =\n nextPrimaryChildren.nextSibling &&\n nextPrimaryChildren.nextSibling.dataset;\n if (JSCompiler_temp) var digest = JSCompiler_temp.dgst;\n JSCompiler_temp = digest;\n nextProps = Error(formatProdErrorMessage(419));\n nextProps.stack = \"\";\n nextProps.digest = JSCompiler_temp;\n queueHydrationError({ value: nextProps, source: null, stack: null });\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (JSCompiler_temp = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || JSCompiler_temp)\n ) {\n JSCompiler_temp = workInProgressRoot;\n if (\n null !== JSCompiler_temp &&\n ((nextProps = getBumpedLaneForHydration(JSCompiler_temp, renderLanes)),\n 0 !== nextProps && nextProps !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = nextProps),\n enqueueConcurrentRenderForLane(current, nextProps),\n scheduleUpdateOnFiber(JSCompiler_temp, current, nextProps),\n SelectiveHydrationException)\n );\n isSuspenseInstancePending(nextPrimaryChildren) ||\n renderDidSuspendDelayIfPossible();\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n isSuspenseInstancePending(nextPrimaryChildren)\n ? ((workInProgress.flags |= 192),\n (workInProgress.child = current.child),\n (workInProgress = null))\n : ((current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(\n nextPrimaryChildren.nextSibling\n )),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountSuspensePrimaryChildren(\n workInProgress,\n nextProps.children\n )),\n (workInProgress.flags |= 4096));\n return workInProgress;\n }\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (prevState = current.child),\n (digest = prevState.sibling),\n (nextProps = createWorkInProgress(prevState, {\n mode: \"hidden\",\n children: nextProps.children\n })),\n (nextProps.subtreeFlags = prevState.subtreeFlags & 65011712),\n null !== digest\n ? (nextPrimaryChildren = createWorkInProgress(\n digest,\n nextPrimaryChildren\n ))\n : ((nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2)),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n bailoutOffscreenComponent(null, nextProps),\n (nextProps = workInProgress.child),\n (nextPrimaryChildren = current.child.memoizedState),\n null === nextPrimaryChildren\n ? (nextPrimaryChildren = mountSuspenseOffscreenState(renderLanes))\n : ((showFallback = nextPrimaryChildren.cachePool),\n null !== showFallback\n ? ((prevState = CacheContext._currentValue),\n (showFallback =\n showFallback.parent !== prevState\n ? { parent: prevState, pool: prevState }\n : showFallback))\n : (showFallback = getSuspendedCache()),\n (nextPrimaryChildren = {\n baseLanes: nextPrimaryChildren.baseLanes | renderLanes,\n cachePool: showFallback\n })),\n (nextProps.memoizedState = nextPrimaryChildren),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(current.child, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n renderLanes = current.child;\n current = renderLanes.sibling;\n renderLanes = createWorkInProgress(renderLanes, {\n mode: \"visible\",\n children: nextProps.children\n });\n renderLanes.return = workInProgress;\n renderLanes.sibling = null;\n null !== current &&\n ((JSCompiler_temp = workInProgress.deletions),\n null === JSCompiler_temp\n ? ((workInProgress.deletions = [current]), (workInProgress.flags |= 16))\n : JSCompiler_temp.push(current));\n workInProgress.child = renderLanes;\n workInProgress.memoizedState = null;\n return renderLanes;\n}\nfunction mountSuspensePrimaryChildren(workInProgress, primaryChildren) {\n primaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: primaryChildren },\n workInProgress.mode\n );\n primaryChildren.return = workInProgress;\n return (workInProgress.child = primaryChildren);\n}\nfunction mountWorkInProgressOffscreenFiber(offscreenProps, mode) {\n offscreenProps = createFiberImplClass(22, offscreenProps, null, mode);\n offscreenProps.lanes = 0;\n return offscreenProps;\n}\nfunction retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountSuspensePrimaryChildren(\n workInProgress,\n workInProgress.pendingProps.children\n );\n current.flags |= 2;\n workInProgress.memoizedState = null;\n return current;\n}\nfunction scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {\n fiber.lanes |= renderLanes;\n var alternate = fiber.alternate;\n null !== alternate && (alternate.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);\n}\nfunction initSuspenseListRenderState(\n workInProgress,\n isBackwards,\n tail,\n lastContentRow,\n tailMode,\n treeForkCount\n) {\n var renderState = workInProgress.memoizedState;\n null === renderState\n ? (workInProgress.memoizedState = {\n isBackwards: isBackwards,\n rendering: null,\n renderingStartTime: 0,\n last: lastContentRow,\n tail: tail,\n tailMode: tailMode,\n treeForkCount: treeForkCount\n })\n : ((renderState.isBackwards = isBackwards),\n (renderState.rendering = null),\n (renderState.renderingStartTime = 0),\n (renderState.last = lastContentRow),\n (renderState.tail = tail),\n (renderState.tailMode = tailMode),\n (renderState.treeForkCount = treeForkCount));\n}\nfunction updateSuspenseListComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n revealOrder = nextProps.revealOrder,\n tailMode = nextProps.tail;\n nextProps = nextProps.children;\n var suspenseContext = suspenseStackCursor.current,\n shouldForceFallback = 0 !== (suspenseContext & 2);\n shouldForceFallback\n ? ((suspenseContext = (suspenseContext & 1) | 2),\n (workInProgress.flags |= 128))\n : (suspenseContext &= 1);\n push(suspenseStackCursor, suspenseContext);\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n nextProps = isHydrating ? treeForkCount : 0;\n if (!shouldForceFallback && null !== current && 0 !== (current.flags & 128))\n a: for (current = workInProgress.child; null !== current; ) {\n if (13 === current.tag)\n null !== current.memoizedState &&\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (19 === current.tag)\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (null !== current.child) {\n current.child.return = current;\n current = current.child;\n continue;\n }\n if (current === workInProgress) break a;\n for (; null === current.sibling; ) {\n if (null === current.return || current.return === workInProgress)\n break a;\n current = current.return;\n }\n current.sibling.return = current.return;\n current = current.sibling;\n }\n switch (revealOrder) {\n case \"forwards\":\n renderLanes = workInProgress.child;\n for (revealOrder = null; null !== renderLanes; )\n (current = renderLanes.alternate),\n null !== current &&\n null === findFirstSuspended(current) &&\n (revealOrder = renderLanes),\n (renderLanes = renderLanes.sibling);\n renderLanes = revealOrder;\n null === renderLanes\n ? ((revealOrder = workInProgress.child), (workInProgress.child = null))\n : ((revealOrder = renderLanes.sibling), (renderLanes.sibling = null));\n initSuspenseListRenderState(\n workInProgress,\n !1,\n revealOrder,\n renderLanes,\n tailMode,\n nextProps\n );\n break;\n case \"backwards\":\n case \"unstable_legacy-backwards\":\n renderLanes = null;\n revealOrder = workInProgress.child;\n for (workInProgress.child = null; null !== revealOrder; ) {\n current = revealOrder.alternate;\n if (null !== current && null === findFirstSuspended(current)) {\n workInProgress.child = revealOrder;\n break;\n }\n current = revealOrder.sibling;\n revealOrder.sibling = renderLanes;\n renderLanes = revealOrder;\n revealOrder = current;\n }\n initSuspenseListRenderState(\n workInProgress,\n !0,\n renderLanes,\n null,\n tailMode,\n nextProps\n );\n break;\n case \"together\":\n initSuspenseListRenderState(\n workInProgress,\n !1,\n null,\n null,\n void 0,\n nextProps\n );\n break;\n default:\n workInProgress.memoizedState = null;\n }\n return workInProgress.child;\n}\nfunction bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {\n null !== current && (workInProgress.dependencies = current.dependencies);\n workInProgressRootSkippedLanes |= workInProgress.lanes;\n if (0 === (renderLanes & workInProgress.childLanes))\n if (null !== current) {\n if (\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n 0 === (renderLanes & workInProgress.childLanes))\n )\n return null;\n } else return null;\n if (null !== current && workInProgress.child !== current.child)\n throw Error(formatProdErrorMessage(153));\n if (null !== workInProgress.child) {\n current = workInProgress.child;\n renderLanes = createWorkInProgress(current, current.pendingProps);\n workInProgress.child = renderLanes;\n for (renderLanes.return = workInProgress; null !== current.sibling; )\n (current = current.sibling),\n (renderLanes = renderLanes.sibling =\n createWorkInProgress(current, current.pendingProps)),\n (renderLanes.return = workInProgress);\n renderLanes.sibling = null;\n }\n return workInProgress.child;\n}\nfunction checkScheduledUpdateOrContext(current, renderLanes) {\n if (0 !== (current.lanes & renderLanes)) return !0;\n current = current.dependencies;\n return null !== current && checkIfContextChanged(current) ? !0 : !1;\n}\nfunction attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n) {\n switch (workInProgress.tag) {\n case 3:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n resetHydrationState();\n break;\n case 27:\n case 5:\n pushHostContext(workInProgress);\n break;\n case 4:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n break;\n case 10:\n pushProvider(\n workInProgress,\n workInProgress.type,\n workInProgress.memoizedProps.value\n );\n break;\n case 31:\n if (null !== workInProgress.memoizedState)\n return (\n (workInProgress.flags |= 128),\n pushDehydratedActivitySuspenseHandler(workInProgress),\n null\n );\n break;\n case 13:\n var state$102 = workInProgress.memoizedState;\n if (null !== state$102) {\n if (null !== state$102.dehydrated)\n return (\n pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags |= 128),\n null\n );\n if (0 !== (renderLanes & workInProgress.child.childLanes))\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n pushPrimaryTreeSuspenseHandler(workInProgress);\n current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n return null !== current ? current.sibling : null;\n }\n pushPrimaryTreeSuspenseHandler(workInProgress);\n break;\n case 19:\n var didSuspendBefore = 0 !== (current.flags & 128);\n state$102 = 0 !== (renderLanes & workInProgress.childLanes);\n state$102 ||\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n (state$102 = 0 !== (renderLanes & workInProgress.childLanes)));\n if (didSuspendBefore) {\n if (state$102)\n return updateSuspenseListComponent(\n current,\n workInProgress,\n renderLanes\n );\n workInProgress.flags |= 128;\n }\n didSuspendBefore = workInProgress.memoizedState;\n null !== didSuspendBefore &&\n ((didSuspendBefore.rendering = null),\n (didSuspendBefore.tail = null),\n (didSuspendBefore.lastEffect = null));\n push(suspenseStackCursor, suspenseStackCursor.current);\n if (state$102) break;\n else return null;\n case 22:\n return (\n (workInProgress.lanes = 0),\n updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n )\n );\n case 24:\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n }\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n}\nfunction beginWork(current, workInProgress, renderLanes) {\n if (null !== current)\n if (current.memoizedProps !== workInProgress.pendingProps)\n didReceiveUpdate = !0;\n else {\n if (\n !checkScheduledUpdateOrContext(current, renderLanes) &&\n 0 === (workInProgress.flags & 128)\n )\n return (\n (didReceiveUpdate = !1),\n attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n )\n );\n didReceiveUpdate = 0 !== (current.flags & 131072) ? !0 : !1;\n }\n else\n (didReceiveUpdate = !1),\n isHydrating &&\n 0 !== (workInProgress.flags & 1048576) &&\n pushTreeId(workInProgress, treeForkCount, workInProgress.index);\n workInProgress.lanes = 0;\n switch (workInProgress.tag) {\n case 16:\n a: {\n var props = workInProgress.pendingProps;\n current = resolveLazy(workInProgress.elementType);\n workInProgress.type = current;\n if (\"function\" === typeof current)\n shouldConstruct(current)\n ? ((props = resolveClassComponentProps(current, props)),\n (workInProgress.tag = 1),\n (workInProgress = updateClassComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )))\n : ((workInProgress.tag = 0),\n (workInProgress = updateFunctionComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )));\n else {\n if (void 0 !== current && null !== current) {\n var $$typeof = current.$$typeof;\n if ($$typeof === REACT_FORWARD_REF_TYPE) {\n workInProgress.tag = 11;\n workInProgress = updateForwardRef(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n } else if ($$typeof === REACT_MEMO_TYPE) {\n workInProgress.tag = 14;\n workInProgress = updateMemoComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n }\n }\n workInProgress = getComponentNameFromType(current) || current;\n throw Error(formatProdErrorMessage(306, workInProgress, \"\"));\n }\n }\n return workInProgress;\n case 0:\n return updateFunctionComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 1:\n return (\n (props = workInProgress.type),\n ($$typeof = resolveClassComponentProps(\n props,\n workInProgress.pendingProps\n )),\n updateClassComponent(\n current,\n workInProgress,\n props,\n $$typeof,\n renderLanes\n )\n );\n case 3:\n a: {\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n );\n if (null === current) throw Error(formatProdErrorMessage(387));\n props = workInProgress.pendingProps;\n var prevState = workInProgress.memoizedState;\n $$typeof = prevState.element;\n cloneUpdateQueue(current, workInProgress);\n processUpdateQueue(workInProgress, props, null, renderLanes);\n var nextState = workInProgress.memoizedState;\n props = nextState.cache;\n pushProvider(workInProgress, CacheContext, props);\n props !== prevState.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n );\n suspendIfUpdateReadFromEntangledAsyncAction();\n props = nextState.element;\n if (prevState.isDehydrated)\n if (\n ((prevState = {\n element: props,\n isDehydrated: !1,\n cache: nextState.cache\n }),\n (workInProgress.updateQueue.baseState = prevState),\n (workInProgress.memoizedState = prevState),\n workInProgress.flags & 256)\n ) {\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else if (props !== $$typeof) {\n $$typeof = createCapturedValueAtFiber(\n Error(formatProdErrorMessage(424)),\n workInProgress\n );\n queueHydrationError($$typeof);\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else {\n current = workInProgress.stateNode.containerInfo;\n switch (current.nodeType) {\n case 9:\n current = current.body;\n break;\n default:\n current =\n \"HTML\" === current.nodeName\n ? current.ownerDocument.body\n : current;\n }\n nextHydratableInstance = getNextHydratable(current.firstChild);\n hydrationParentFiber = workInProgress;\n isHydrating = !0;\n hydrationErrors = null;\n rootOrSingletonContext = !0;\n renderLanes = mountChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n );\n for (workInProgress.child = renderLanes; renderLanes; )\n (renderLanes.flags = (renderLanes.flags & -3) | 4096),\n (renderLanes = renderLanes.sibling);\n }\n else {\n resetHydrationState();\n if (props === $$typeof) {\n workInProgress = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n break a;\n }\n reconcileChildren(current, workInProgress, props, renderLanes);\n }\n workInProgress = workInProgress.child;\n }\n return workInProgress;\n case 26:\n return (\n markRef(current, workInProgress),\n null === current\n ? (renderLanes = getResource(\n workInProgress.type,\n null,\n workInProgress.pendingProps,\n null\n ))\n ? (workInProgress.memoizedState = renderLanes)\n : isHydrating ||\n ((renderLanes = workInProgress.type),\n (current = workInProgress.pendingProps),\n (props = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n ).createElement(renderLanes)),\n (props[internalInstanceKey] = workInProgress),\n (props[internalPropsKey] = current),\n setInitialProperties(props, renderLanes, current),\n markNodeAsHoistable(props),\n (workInProgress.stateNode = props))\n : (workInProgress.memoizedState = getResource(\n workInProgress.type,\n current.memoizedProps,\n workInProgress.pendingProps,\n current.memoizedState\n )),\n null\n );\n case 27:\n return (\n pushHostContext(workInProgress),\n null === current &&\n isHydrating &&\n ((props = workInProgress.stateNode =\n resolveSingletonInstance(\n workInProgress.type,\n workInProgress.pendingProps,\n rootInstanceStackCursor.current\n )),\n (hydrationParentFiber = workInProgress),\n (rootOrSingletonContext = !0),\n ($$typeof = nextHydratableInstance),\n isSingletonScope(workInProgress.type)\n ? ((previousHydratableOnEnteringScopedSingleton = $$typeof),\n (nextHydratableInstance = getNextHydratable(props.firstChild)))\n : (nextHydratableInstance = $$typeof)),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n markRef(current, workInProgress),\n null === current && (workInProgress.flags |= 4194304),\n workInProgress.child\n );\n case 5:\n if (null === current && isHydrating) {\n if (($$typeof = props = nextHydratableInstance))\n (props = canHydrateInstance(\n props,\n workInProgress.type,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== props\n ? ((workInProgress.stateNode = props),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = getNextHydratable(props.firstChild)),\n (rootOrSingletonContext = !1),\n ($$typeof = !0))\n : ($$typeof = !1);\n $$typeof || throwOnHydrationMismatch(workInProgress);\n }\n pushHostContext(workInProgress);\n $$typeof = workInProgress.type;\n prevState = workInProgress.pendingProps;\n nextState = null !== current ? current.memoizedProps : null;\n props = prevState.children;\n shouldSetTextContent($$typeof, prevState)\n ? (props = null)\n : null !== nextState &&\n shouldSetTextContent($$typeof, nextState) &&\n (workInProgress.flags |= 32);\n null !== workInProgress.memoizedState &&\n (($$typeof = renderWithHooks(\n current,\n workInProgress,\n TransitionAwareHostComponent,\n null,\n null,\n renderLanes\n )),\n (HostTransitionContext._currentValue = $$typeof));\n markRef(current, workInProgress);\n reconcileChildren(current, workInProgress, props, renderLanes);\n return workInProgress.child;\n case 6:\n if (null === current && isHydrating) {\n if ((current = renderLanes = nextHydratableInstance))\n (renderLanes = canHydrateTextInstance(\n renderLanes,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== renderLanes\n ? ((workInProgress.stateNode = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null),\n (current = !0))\n : (current = !1);\n current || throwOnHydrationMismatch(workInProgress);\n }\n return null;\n case 13:\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n case 4:\n return (\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n ),\n (props = workInProgress.pendingProps),\n null === current\n ? (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n ))\n : reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 11:\n return updateForwardRef(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 7:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps,\n renderLanes\n ),\n workInProgress.child\n );\n case 8:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 12:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 10:\n return (\n (props = workInProgress.pendingProps),\n pushProvider(workInProgress, workInProgress.type, props.value),\n reconcileChildren(current, workInProgress, props.children, renderLanes),\n workInProgress.child\n );\n case 9:\n return (\n ($$typeof = workInProgress.type._context),\n (props = workInProgress.pendingProps.children),\n prepareToReadContext(workInProgress),\n ($$typeof = readContext($$typeof)),\n (props = props($$typeof)),\n (workInProgress.flags |= 1),\n reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 14:\n return updateMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 15:\n return updateSimpleMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 19:\n return updateSuspenseListComponent(current, workInProgress, renderLanes);\n case 31:\n return updateActivityComponent(current, workInProgress, renderLanes);\n case 22:\n return updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n );\n case 24:\n return (\n prepareToReadContext(workInProgress),\n (props = readContext(CacheContext)),\n null === current\n ? (($$typeof = peekCacheFromPool()),\n null === $$typeof &&\n (($$typeof = workInProgressRoot),\n (prevState = createCache()),\n ($$typeof.pooledCache = prevState),\n prevState.refCount++,\n null !== prevState && ($$typeof.pooledCacheLanes |= renderLanes),\n ($$typeof = prevState)),\n (workInProgress.memoizedState = { parent: props, cache: $$typeof }),\n initializeUpdateQueue(workInProgress),\n pushProvider(workInProgress, CacheContext, $$typeof))\n : (0 !== (current.lanes & renderLanes) &&\n (cloneUpdateQueue(current, workInProgress),\n processUpdateQueue(workInProgress, null, null, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction()),\n ($$typeof = current.memoizedState),\n (prevState = workInProgress.memoizedState),\n $$typeof.parent !== props\n ? (($$typeof = { parent: props, cache: props }),\n (workInProgress.memoizedState = $$typeof),\n 0 === workInProgress.lanes &&\n (workInProgress.memoizedState =\n workInProgress.updateQueue.baseState =\n $$typeof),\n pushProvider(workInProgress, CacheContext, props))\n : ((props = prevState.cache),\n pushProvider(workInProgress, CacheContext, props),\n props !== $$typeof.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n ))),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 29:\n throw workInProgress.pendingProps;\n }\n throw Error(formatProdErrorMessage(156, workInProgress.tag));\n}\nfunction markUpdate(workInProgress) {\n workInProgress.flags |= 4;\n}\nfunction preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n oldProps,\n newProps,\n renderLanes\n) {\n if ((type = 0 !== (workInProgress.mode & 32))) type = !1;\n if (type) {\n if (\n ((workInProgress.flags |= 16777216),\n (renderLanes & 335544128) === renderLanes)\n )\n if (workInProgress.stateNode.complete) workInProgress.flags |= 8192;\n else if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n } else workInProgress.flags &= -16777217;\n}\nfunction preloadResourceAndSuspendIfNeeded(workInProgress, resource) {\n if (\"stylesheet\" !== resource.type || 0 !== (resource.state.loading & 4))\n workInProgress.flags &= -16777217;\n else if (((workInProgress.flags |= 16777216), !preloadResource(resource)))\n if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n}\nfunction scheduleRetryEffect(workInProgress, retryQueue) {\n null !== retryQueue && (workInProgress.flags |= 4);\n workInProgress.flags & 16384 &&\n ((retryQueue =\n 22 !== workInProgress.tag ? claimNextRetryLane() : 536870912),\n (workInProgress.lanes |= retryQueue),\n (workInProgressSuspendedRetryLanes |= retryQueue));\n}\nfunction cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {\n if (!isHydrating)\n switch (renderState.tailMode) {\n case \"hidden\":\n hasRenderedATailFallback = renderState.tail;\n for (var lastTailNode = null; null !== hasRenderedATailFallback; )\n null !== hasRenderedATailFallback.alternate &&\n (lastTailNode = hasRenderedATailFallback),\n (hasRenderedATailFallback = hasRenderedATailFallback.sibling);\n null === lastTailNode\n ? (renderState.tail = null)\n : (lastTailNode.sibling = null);\n break;\n case \"collapsed\":\n lastTailNode = renderState.tail;\n for (var lastTailNode$106 = null; null !== lastTailNode; )\n null !== lastTailNode.alternate && (lastTailNode$106 = lastTailNode),\n (lastTailNode = lastTailNode.sibling);\n null === lastTailNode$106\n ? hasRenderedATailFallback || null === renderState.tail\n ? (renderState.tail = null)\n : (renderState.tail.sibling = null)\n : (lastTailNode$106.sibling = null);\n }\n}\nfunction bubbleProperties(completedWork) {\n var didBailout =\n null !== completedWork.alternate &&\n completedWork.alternate.child === completedWork.child,\n newChildLanes = 0,\n subtreeFlags = 0;\n if (didBailout)\n for (var child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags & 65011712),\n (subtreeFlags |= child$107.flags & 65011712),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n else\n for (child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags),\n (subtreeFlags |= child$107.flags),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n completedWork.subtreeFlags |= subtreeFlags;\n completedWork.childLanes = newChildLanes;\n return didBailout;\n}\nfunction completeWork(current, workInProgress, renderLanes) {\n var newProps = workInProgress.pendingProps;\n popTreeContext(workInProgress);\n switch (workInProgress.tag) {\n case 16:\n case 15:\n case 0:\n case 11:\n case 7:\n case 8:\n case 12:\n case 9:\n case 14:\n return bubbleProperties(workInProgress), null;\n case 1:\n return bubbleProperties(workInProgress), null;\n case 3:\n renderLanes = workInProgress.stateNode;\n newProps = null;\n null !== current && (newProps = current.memoizedState.cache);\n workInProgress.memoizedState.cache !== newProps &&\n (workInProgress.flags |= 2048);\n popProvider(CacheContext);\n popHostContainer();\n renderLanes.pendingContext &&\n ((renderLanes.context = renderLanes.pendingContext),\n (renderLanes.pendingContext = null));\n if (null === current || null === current.child)\n popHydrationState(workInProgress)\n ? markUpdate(workInProgress)\n : null === current ||\n (current.memoizedState.isDehydrated &&\n 0 === (workInProgress.flags & 256)) ||\n ((workInProgress.flags |= 1024),\n upgradeHydrationErrorsToRecoverable());\n bubbleProperties(workInProgress);\n return null;\n case 26:\n var type = workInProgress.type,\n nextResource = workInProgress.memoizedState;\n null === current\n ? (markUpdate(workInProgress),\n null !== nextResource\n ? (bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n null,\n newProps,\n renderLanes\n )))\n : nextResource\n ? nextResource !== current.memoizedState\n ? (markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n (workInProgress.flags &= -16777217))\n : ((current = current.memoizedProps),\n current !== newProps && markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n current,\n newProps,\n renderLanes\n ));\n return null;\n case 27:\n popHostContext(workInProgress);\n renderLanes = rootInstanceStackCursor.current;\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n current = contextStackCursor.current;\n popHydrationState(workInProgress)\n ? prepareToHydrateHostInstance(workInProgress, current)\n : ((current = resolveSingletonInstance(type, newProps, renderLanes)),\n (workInProgress.stateNode = current),\n markUpdate(workInProgress));\n }\n bubbleProperties(workInProgress);\n return null;\n case 5:\n popHostContext(workInProgress);\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n nextResource = contextStackCursor.current;\n if (popHydrationState(workInProgress))\n prepareToHydrateHostInstance(workInProgress, nextResource);\n else {\n var ownerDocument = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n );\n switch (nextResource) {\n case 1:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case 2:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n default:\n switch (type) {\n case \"svg\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case \"math\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n case \"script\":\n nextResource = ownerDocument.createElement(\"div\");\n nextResource.innerHTML = \" + diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 02bd6371..0a33873f 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.869", + "version": "1.0.870", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index c60af960..7fb5dfa1 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1394", + "version": "1.0.1395", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d3fc4fe0..acf6cd43 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index c7fab8a7..7ed8b9eb 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1399", + "version": "0.5.1400", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 6a6e1fac..6f8bd072 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 19315073..6b518236 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 2f7879a7..f69ffd79 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 6d1129c0..4a1be499 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 7c0e3b0c..8bcd7e7b 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index e4543eb3..677ab957 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 96c09e4c..a45228ef 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index ad6ae41e..d6664a14 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index e4ec552d..98777aa3 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1420", + "version": "0.5.1421", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 107a0d4b..e7e71752 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 018a2fa8..47553218 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 8af122a3..2c0aa6be 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index d1be3319..ec3622d2 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 9e2166eb..5b334117 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 2d4dd878..340915ce 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index e88072ea..a236543d 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1412", + "version": "0.5.1413", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx index 8de99d07..2cc3459d 100755 --- a/pages/options/src/components/LLMSelector.tsx +++ b/pages/options/src/components/LLMSelector.tsx @@ -23,7 +23,7 @@ const LLMSelector: React.FC = ({ const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings(); const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl); const [apiKey, setApiKey] = useState(''); - const saveTimeoutRef = useRef(); + const saveTimeoutRef = useRef(null); // Загружаем API-ключ при монтировании компонента useEffect(() => { @@ -65,6 +65,8 @@ const LLMSelector: React.FC = ({ : settings.deep_analysis[language].custom_prompt, }); + + // API-ключ передаем только для Default LLM onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined); }; @@ -106,7 +108,6 @@ const LLMSelector: React.FC = ({ \n\n {selectedLLM === 'default' && (\n
\n \n handleApiKeyChange(e.target.value)}\n placeholder=\"Введите API ключ\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n />\n
\n )}\n
\n );\n};\n\nexport default LLMSelector;","import * as React from 'react';\nimport { useTranslations } from './useTranslations';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface AIKey {\n id: string;\n name: string;\n key: string;\n status: 'configured' | 'not_configured' | 'testing';\n isFixed?: boolean;\n isFree?: boolean;\n}\n\nexport const useAIKeys = () => {\n const { t } = useTranslations('ru');\n const [aiKeys, setAiKeys] = React.useState([\n {\n id: 'gemini-flash-lite',\n name: 'Google Gemini Flash Lite - Базовый анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n {\n id: 'gemini-pro',\n name: 'Gemini 2.5 Pro - Глубокий анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n ]);\n const [customKeys, setCustomKeys] = React.useState([]);\n\n // Рефы для отложенного сохранения ключей\n const saveTimeoutsRef = React.useRef>({});\n\n // Load AI keys on mount and cleanup timeouts on unmount\n React.useEffect(() => {\n loadAIKeys();\n\n // Cleanup function\n return () => {\n // Очищаем все активные таймауты\n Object.values(saveTimeoutsRef.current).forEach(timeout => {\n clearTimeout(timeout);\n });\n saveTimeoutsRef.current = {};\n };\n }, []);\n\n const loadAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to load AI keys...');\n\n // Загружаем зашифрованные ключи\n const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro'];\n const fixedKeysPromises = fixedKeyIds.map(async (keyId) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(keyId);\n console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty');\n return { keyId, decryptedKey };\n });\n\n const fixedKeysResults = await Promise.all(fixedKeysPromises);\n console.log('[useAIKeys] Fixed keys results:', fixedKeysResults);\n\n setAiKeys(prev =>\n prev.map(key => {\n const result = fixedKeysResults.find(r => r.keyId === key.id);\n console.log(`[useAIKeys] Setting key ${key.id}:`, result?.decryptedKey ? 'configured' : 'not_configured');\n return {\n ...key,\n key: result?.decryptedKey || '',\n status: (result?.decryptedKey ? 'configured' : 'not_configured') as AIKey['status'],\n };\n }),\n );\n\n // Загружаем пользовательские ключи (метаданные)\n const result = await chrome.storage.local.get(['customKeys']);\n console.log('[useAIKeys] Custom keys metadata:', result.customKeys);\n if (result.customKeys) {\n // Для пользовательских ключей также используем шифрование\n const customKeysWithDecryption = await Promise.all(\n (result.customKeys as AIKey[]).map(async (key: AIKey) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(key.id);\n console.log(`[useAIKeys] Custom key ${key.id}:`, decryptedKey ? 'present' : 'empty');\n return {\n ...key,\n key: decryptedKey || '',\n status: (decryptedKey ? 'configured' : 'not_configured') as AIKey['status']\n };\n })\n );\n console.log('[useAIKeys] Setting custom keys:', customKeysWithDecryption);\n setCustomKeys(customKeysWithDecryption);\n } else {\n console.log('[useAIKeys] No custom keys found');\n setCustomKeys([]);\n }\n\n console.log('[useAIKeys] AI keys loaded successfully');\n } catch (error) {\n console.error('Failed to load AI keys:', error);\n // В случае ошибки шифрования показываем пустые ключи\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n key: '',\n status: 'not_configured' as AIKey['status'],\n })),\n );\n setCustomKeys([]);\n }\n };\n\n const saveAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to save AI keys...');\n console.log('[useAIKeys] Current aiKeys:', aiKeys);\n console.log('[useAIKeys] Current customKeys:', customKeys);\n\n // Сохраняем фиксированные ключи с шифрованием\n const saveFixedKeysPromises = aiKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving fixed key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing fixed key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveFixedKeysPromises);\n\n // Сохраняем пользовательские ключи с шифрованием\n const saveCustomKeysPromises = customKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving custom key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing custom key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveCustomKeysPromises);\n\n // Сохраняем метаданные пользовательских ключей (без самих ключей)\n const customKeysMetadata = customKeys.map(key => ({\n id: key.id,\n name: key.name,\n isFixed: false,\n isFree: false,\n // key и status не сохраняем в метаданных для безопасности\n }));\n\n console.log('[useAIKeys] Saving custom keys metadata:', customKeysMetadata);\n await chrome.storage.local.set({\n customKeys: customKeysMetadata,\n });\n\n // Обновляем статусы в состоянии\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status'],\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status']\n }))\n );\n\n console.log('[useAIKeys] AI keys saved successfully');\n alert(t('options.settings.aiKeys.messages.saved'));\n } catch (error) {\n console.error('Failed to save AI keys:', error);\n alert(t('options.settings.aiKeys.messages.saveError'));\n }\n };\n\n const testAIKeys = async () => {\n // Set testing status\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n // Simulate API testing with timeout\n setTimeout(() => {\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n alert(t('options.settings.aiKeys.messages.testComplete'));\n }, 2000);\n };\n\n const addCustomKey = () => {\n const newKey: AIKey = {\n id: `custom-${Date.now()}`,\n name: `Пользовательский ключ ${customKeys.length + 1}`,\n key: '',\n status: 'not_configured' as const,\n };\n setCustomKeys(prev => [...prev, newKey]);\n };\n\n const removeCustomKey = async (id: string) => {\n try {\n // Удаляем зашифрованный ключ\n await APIKeyManager.removeKey(id);\n\n // Удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n } catch (error) {\n console.error('Failed to remove custom key:', error);\n // Даже если удаление зашифрованного ключа не удалось, удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n }\n };\n\n const updateKey = (id: string, value: string, isCustom = false) => {\n console.log(`[useAIKeys] Updating key ${id} with value:`, value ? 'present' : 'empty');\n\n // Обновляем состояние немедленно для лучшего UX\n if (isCustom) {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n } else {\n setAiKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n }\n\n // Отменяем предыдущий таймаут для этого ключа\n if (saveTimeoutsRef.current[id]) {\n clearTimeout(saveTimeoutsRef.current[id]);\n }\n\n // Устанавливаем новый таймаут для отложенного сохранения\n saveTimeoutsRef.current[id] = setTimeout(async () => {\n try {\n console.log(`[useAIKeys] Auto-saving key ${id} after delay`);\n if (value) {\n await APIKeyManager.saveEncryptedKey(id, value);\n } else {\n await APIKeyManager.removeKey(id);\n }\n\n // Для пользовательских ключей также обновляем метаданные\n if (isCustom) {\n const result = await chrome.storage.local.get(['customKeys']);\n const customKeysMetadata = result.customKeys || [];\n const updatedMetadata = customKeysMetadata.map((key: any) =>\n key.id === id ? { ...key, name: key.name } : key\n );\n\n // Если ключ не существует в метаданных, добавляем его\n const existingKeyIndex = updatedMetadata.findIndex((key: any) => key.id === id);\n if (existingKeyIndex === -1 && value) {\n updatedMetadata.push({\n id,\n name: `Пользовательский ключ ${customKeysMetadata.length + 1}`,\n isFixed: false,\n isFree: false,\n });\n }\n\n await chrome.storage.local.set({\n customKeys: updatedMetadata.filter((key: any) => {\n // Убираем из метаданных ключи, которые были удалены\n return value || key.id !== id;\n }),\n });\n }\n\n console.log(`[useAIKeys] Key ${id} auto-saved successfully`);\n } catch (error) {\n console.error(`[useAIKeys] Failed to auto-save key ${id}:`, error);\n } finally {\n // Убираем таймаут из рефа\n delete saveTimeoutsRef.current[id];\n }\n }, 1000); // 1 секунда задержки перед сохранением\n };\n\n const updateCustomKeyName = (id: string, name: string) => {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n name,\n status: key.key ? 'configured' : 'not_configured'\n } : key)));\n };\n\n // Функция для получения текста статуса с поддержкой локализации\n const getStatusText = (status: string) => {\n switch (status) {\n case 'configured':\n return t('options.settings.aiKeys.status.configured');\n case 'not_configured':\n return t('options.settings.aiKeys.status.notConfigured');\n case 'testing':\n return t('options.settings.aiKeys.status.testing');\n default:\n return t('options.settings.aiKeys.status.notConfigured');\n }\n };\n\n const getStatusClass = (status: string) => {\n switch (status) {\n case 'configured':\n return 'status-configured';\n case 'not_configured':\n return 'status-not-configured';\n case 'testing':\n return 'status-testing';\n default:\n return '';\n }\n };\n\n // Функция для получения API ключа для использования в MCP-серверах\n const getAPIKeyForMCP = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.getDecryptedKey(keyId);\n } catch (error) {\n console.error('Failed to get API key for MCP:', error);\n return null;\n }\n };\n\n // Функция для проверки, настроен ли определенный ключ\n const isKeyConfigured = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.keyExists(keyId);\n } catch (error) {\n console.error('Failed to check if key is configured:', error);\n return false;\n }\n };\n\n return {\n aiKeys,\n customKeys,\n saveAIKeys,\n testAIKeys,\n addCustomKey,\n removeCustomKey,\n updateKey,\n updateCustomKeyName,\n getStatusText,\n getStatusClass,\n getAPIKeyForMCP,\n isKeyConfigured,\n };\n};\n","import { useTranslations } from '../hooks/useTranslations';\nimport type { Plugin } from '../hooks/usePlugins';\nimport type { PluginSettings } from '@extension/storage';\nimport ToggleButton from './ToggleButton';\nimport LocalErrorBoundary from './LocalErrorBoundary';\nimport LLMSelector from './LLMSelector';\nimport { useAIKeys, AIKey } from '../hooks/useAIKeys';\nimport { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings';\nimport { useState, useEffect } from 'react';\nimport type { ReactNode } from 'react';\n\n// Типы для структуры промптов\ninterface PromptData {\n [key: string]: any;\n}\n\ninterface LanguagePrompts {\n ru: PromptData;\n en: PromptData;\n}\n\ninterface PromptsStructure {\n basic_analysis: LanguagePrompts;\n deep_analysis: LanguagePrompts;\n}\n\nconst cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' ');\n\ninterface PluginDetailsProps {\n selectedPlugin: Plugin | null;\n locale?: 'en' | 'ru';\n onUpdateSetting?: (pluginId: string, setting: keyof PluginSettings, value: boolean) => Promise;\n}\n\ninterface CustomSetting {\n type: 'boolean' | 'select' | 'text' | 'number' | 'prompts';\n default: boolean | string | number | PromptsStructure;\n label: string | { ru: string; en: string };\n description?: string | { ru: string; en: string };\n values?: string[];\n labels?: Record;\n min?: number;\n max?: number;\n step?: number;\n}\n\n// Компонент для редактирования промптов\ninterface PromptsEditorProps {\n value: PromptsStructure;\n manifest: any; // manifest.json структура\n disabled: boolean;\n onSave: (value: PromptsStructure) => void;\n locale: 'en' | 'ru';\n t: (key: string) => string; // функция перевода\n globalAIKeys: AIKey[];\n pluginSettings: PluginSettingsType;\n}\n\nconst PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => {\n const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis');\n const [language, setLanguage] = useState<'ru' | 'en'>('ru');\n const [customPrompt, setCustomPrompt] = useState('');\n\n // Получить дефолтную LLM для конкретного промпта и языка\n const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return 'default';\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n const llmConfig = langPrompts.LLM?.default;\n\n if (!llmConfig) return 'default';\n\n // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro\n if (type === 'basic_analysis') {\n return 'gemini-flash-lite';\n } else if (type === 'deep_analysis') {\n return 'gemini-pro';\n }\n\n return 'default';\n } catch {\n return 'default';\n }\n };\n\n // Проверить наличие дефолтной LLM для конкретного промпта и языка\n const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return false;\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n return !!langPrompts.LLM?.default;\n } catch {\n return false;\n }\n };\n\n // Получаем оригинальный промпт из manifest\n const getOriginalPrompt = (): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return '';\n\n const typePrompts = promptsConfig[promptType] || {};\n const langPrompts = typePrompts[language] || {};\n const defaultPrompt = langPrompts.default || '';\n\n // Если defaultPrompt - это объект, преобразуем его\n if (typeof defaultPrompt === 'object') {\n return JSON.stringify(defaultPrompt, null, 2);\n }\n\n return defaultPrompt;\n } catch {\n return '';\n }\n };\n\n // Получаем кастомный промпт\n const getCustomPrompt = (): string => {\n try {\n const prompts = value || {};\n const typePrompts = (prompts as any)[promptType] || {};\n const langPrompts = (typePrompts as any)[language];\n\n // If stored as plain text, show as-is. Only stringify objects.\n if (typeof langPrompts === 'string') return langPrompts;\n if (langPrompts == null) return '';\n return JSON.stringify(langPrompts, null, 2);\n } catch {\n return '';\n }\n };\n\n // Загружаем кастомный промпт при изменении типа или языка\n useEffect(() => {\n setCustomPrompt(getCustomPrompt());\n }, [promptType, language, value]);\n\n const handleCopyToCustom = () => {\n setCustomPrompt(getOriginalPrompt());\n };\n\n const handleSave = () => {\n try {\n const newValue: any = { ...value };\n\n // Ensure container objects exist\n if (!newValue[promptType]) newValue[promptType] = { ru: '', en: '' };\n\n // Store verbatim text (plain string), no JSON requirement\n newValue[promptType][language] = customPrompt;\n\n onSave(newValue);\n } catch (error) {\n console.error('Failed to save custom prompt:', error);\n // Можно добавить уведомление об ошибке\n }\n };\n\n return (\n
\n
\n \n\n {/* Переключатели */}\n
\n
\n \n setPromptType(e.target.value as 'basic_analysis' | 'deep_analysis')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n\n
\n \n setLanguage(e.target.value as 'ru' | 'en')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n
\n\n {/* LLM Selector для текущего промпта и языка */}\n {\n console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey);\n // Сохраняем выбранную LLM в pluginSettings для передачи в mcp_server.py\n const updatedPluginSettings = { ...pluginSettings } as any;\n if (!updatedPluginSettings.selected_llms) {\n updatedPluginSettings.selected_llms = {};\n }\n if (!updatedPluginSettings.selected_llms[promptType]) {\n updatedPluginSettings.selected_llms[promptType] = {};\n }\n // Сохраняем только выбранную LLM (без api_key, так как он уже сохранен через APIKeyManager)\n updatedPluginSettings.selected_llms[promptType][language] = llm;\n // Обновляем pluginSettings через глобальный объект\n if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n }\n }}\n />\n\n {/* Textarea */}\n
\n
\n \n \n
\n\n
\n \n {t('options.plugins.prompts.copyToCustom')}\n \n
\n\n
\n \n setCustomPrompt(e.target.value)}\n disabled={disabled}\n style={{\n width: '100%',\n height: '300px',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '12px',\n fontFamily: 'monospace',\n resize: 'vertical'\n }}\n />\n
\n
\n\n {/* Кнопка сохранения */}\n
\n \n {t('options.plugins.prompts.save')}\n \n
\n
\n
\n );\n};\n\nconst PluginDetails = (props: PluginDetailsProps) => {\n const { selectedPlugin, locale = 'en', onUpdateSetting } = props;\n const { t } = useTranslations(locale);\n const { aiKeys } = useAIKeys();\n const { settings: pluginSettings } = usePluginSettings();\n const [isUpdating, setIsUpdating] = useState(null);\n const [customSettings, setCustomSettings] = useState | null>(null);\n\n // Хелперы для работы с локализацией\n const getLocalizedText = (text: string | { ru: string; en: string } | undefined): string => {\n if (!text) return '';\n if (typeof text === 'string') return text;\n return text[locale] || text.ru || text.en || '';\n };\n\n // Загружаем пользовательские настройки при выборе плагина\n useEffect(() => {\n if (selectedPlugin?.manifest?.options) {\n loadCustomSettings();\n }\n }, [selectedPlugin?.id]);\n\n // Передаем выбранные LLM в mcp_server.py через pyodide.globals\n useEffect(() => {\n if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n // Создаем структуру selected_llms из pluginSettings\n const selected_llms = {\n basic_analysis: {\n ru: pluginSettings.basic_analysis?.ru?.llm || 'default',\n en: pluginSettings.basic_analysis?.en?.llm || 'default',\n },\n deep_analysis: {\n ru: pluginSettings.deep_analysis?.ru?.llm || 'default',\n en: pluginSettings.deep_analysis?.en?.llm || 'default',\n }\n };\n \n // Обновляем pluginSettings в pyodide.globals\n const updatedPluginSettings = {\n ...(window as any).pyodide.globals.pluginSettings || {},\n selected_llms: selected_llms\n };\n \n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings);\n }\n }, [pluginSettings]);\n\n if (!selectedPlugin || typeof selectedPlugin !== 'object') {\n return (\n
\n

{t('options_plugins_details_title')}

\n

{t('options.plugins.details.selectPlugin')}

\n
\n );\n }\n\n const settings = selectedPlugin.settings || { enabled: true, autorun: false };\n const hostPermissions = selectedPlugin.manifest?.host_permissions || [];\n\n // Функции для работы с chrome.storage.local\n const loadCustomSettings = async () => {\n if (!selectedPlugin || !selectedPlugin.manifest?.options || customSettings !== null) return;\n\n try {\n // Проверяем доступность chrome.storage\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const optionKeys = Object.keys(selectedPlugin.manifest.options);\n const keys = optionKeys.map(key => `${selectedPlugin.id}_${key}`);\n\n const result = await chrome.storage.local.get(keys);\n const loadedSettings: Record = {};\n\n // Преобразуем ключи обратно и применяем значения\n Object.entries(result).forEach(([key, value]) => {\n const settingName = key.replace(`${selectedPlugin.id}_`, '');\n if (value !== undefined) {\n loadedSettings[settingName] = value;\n }\n });\n\n setCustomSettings(loadedSettings);\n }\n } catch (error) {\n console.warn('Failed to load custom settings from chrome.storage.local:', error);\n // Fallback: используем дефолтные значения из manifest\n }\n };\n\n const saveCustomSetting = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n try {\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const key = `${selectedPlugin.id}_${setting}`;\n await chrome.storage.local.set({ [key]: value });\n\n // Обновляем локальное состояние\n setCustomSettings(prev => ({\n ...prev,\n [setting]: value\n }));\n\n // Диагностика: проверяем правильность сохранения промптов\n if (setting === 'prompts') {\n await verifyPromptStorage();\n }\n } else {\n console.warn('chrome.storage.local is not available');\n }\n } catch (error) {\n console.error(`Failed to save setting ${setting} to chrome.storage.local:`, error);\n throw error; // Пробрасываем ошибку для обработки в handleSettingChange\n }\n };\n\n // Диагностическая функция для проверки сохранения промптов\n const verifyPromptStorage = async () => {\n if (!selectedPlugin) return;\n\n try {\n const key = `${selectedPlugin.id}_prompts`;\n const stored = await chrome.storage.local.get([key]);\n console.log('🔍 Диагностика промптов:');\n console.log(` Plugin ID: ${selectedPlugin.id}`);\n console.log(` Storage key: ${key}`);\n console.log(' Сохраненные промпты:', stored[key]);\n\n if (stored[key]) {\n const prompts = stored[key] as PromptsStructure;\n console.log(' Структура промптов:');\n console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`);\n console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`);\n console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`);\n console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`);\n }\n } catch (error) {\n console.error('Ошибка диагностики промптов:', error);\n }\n };\n\n\n // Хелпер для получения значения настройки с приоритетом: chrome.storage -> manifest\n const getCustomSettingValue = (settingName: string, defaultValue: boolean | string | number | PromptsStructure): boolean | string | number | PromptsStructure => {\n if (customSettings && customSettings[settingName] !== undefined) {\n return customSettings[settingName];\n }\n\n // Специальная обработка для промптов: преобразуем структуру из manifest в PromptsStructure\n if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) {\n const promptsConfig = defaultValue as any;\n const result: PromptsStructure = {\n basic_analysis: { ru: {}, en: {} },\n deep_analysis: { ru: {}, en: {} }\n };\n\n // Извлекаем default значения из структуры manifest\n if (promptsConfig.basic_analysis?.ru?.default) {\n result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default;\n }\n if (promptsConfig.basic_analysis?.en?.default) {\n result.basic_analysis.en = promptsConfig.basic_analysis.en.default;\n }\n if (promptsConfig.deep_analysis?.ru?.default) {\n result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default;\n }\n if (promptsConfig.deep_analysis?.en?.default) {\n result.deep_analysis.en = promptsConfig.deep_analysis.en.default;\n }\n\n return result;\n }\n\n return defaultValue;\n };\n\n const renderCustomSetting = (key: string, config: CustomSetting): ReactNode | null => {\n const value = getCustomSettingValue(key, config.default);\n const disabled = isUpdating === key || !(settings.enabled ?? true);\n const localizedLabel = getLocalizedText(config.label);\n const localizedDescription = getLocalizedText(config.description);\n\n // Специальная обработка для промптов\n if (key === 'prompts') {\n return (\n
\n handleSettingChange(key, newValue)}\n locale={locale}\n t={t}\n globalAIKeys={aiKeys}\n pluginSettings={pluginSettings}\n />\n
\n );\n }\n\n if (config.type === 'boolean') {\n return (\n
\n handleSettingChange(key, val)}\n label={\n <>\n {localizedLabel}\n {localizedDescription && (\n \n i\n \n )}\n \n }\n />\n
\n );\n }\n\n if (config.type === 'select') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'text') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'number') {\n const handleNumberChange = (e: React.ChangeEvent) => {\n const numValue = parseFloat(e.target.value);\n if (isNaN(numValue)) return;\n\n // Валидация диапазона\n let validatedValue = numValue;\n if (config.min !== undefined && validatedValue < config.min) {\n validatedValue = config.min;\n }\n if (config.max !== undefined && validatedValue > config.max) {\n validatedValue = config.max;\n }\n\n handleSettingChange(key, validatedValue);\n };\n\n return (\n
\n \n
\n );\n }\n\n return null;\n };\n\n const handleSettingChange = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n // Проверяем, является ли настройка пользовательской\n const options = selectedPlugin.manifest?.options;\n if (options && options[setting as keyof typeof options]) {\n // Ленивая загрузка: загружаем настройки только при первом взаимодействии\n if (customSettings === null) {\n await loadCustomSettings();\n }\n\n try {\n setIsUpdating(setting);\n await saveCustomSetting(setting, value);\n } catch (error) {\n console.error(`Failed to update custom setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n } else {\n // Стандартные настройки (enabled/autorun) используем через callback\n if (onUpdateSetting) {\n try {\n setIsUpdating(setting);\n await onUpdateSetting(selectedPlugin.id, setting as keyof PluginSettings, value as boolean);\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n }\n }\n };\n\n // Precompute custom settings elements to satisfy TypeScript\n const optionEntries = Object.entries(selectedPlugin.manifest?.options ?? {}) as [string, CustomSetting][];\n const customSettingElements: ReactNode[] = optionEntries\n .map(([key, config]) => renderCustomSetting(key, config))\n .filter((item): item is ReactNode => item !== null);\n\n // Render helper to avoid union/unknown in JSX for plugin settings section\n const PluginSettingsSection = () => (\n
\n

Настройки плагина

\n
\n handleSettingChange('enabled', val)}\n label={\n <>\n Включен\n \n i\n \n \n }\n />\n
\n
\n handleSettingChange('autorun', val)}\n label={\n <>\n Автоматический запуск\n \n i\n \n \n }\n />\n
\n
\n );\n\n return (\n \n
\n

{selectedPlugin.name}

\n
\n
\n
\n

\n Версия: v{selectedPlugin.version}\n

\n

\n Статус:\n \n {settings.enabled ? 'Активен' : 'Неактивен'}\n \n

\n

\n Автор: {selectedPlugin.manifest?.author || 'Не указан'}\n

\n

\n Последнее обновление: {selectedPlugin.manifest?.last_updated || 'Неизвестно'}\n

\n
\n\n
\n

Описание

\n

{selectedPlugin.description}

\n
\n\n {/* Сайты/домены, на которых работает плагин */}\n {hostPermissions.length > 0 && (\n
\n

Сайты/домены

\n
    \n {hostPermissions.map((host: string, idx: number) => (\n
  • {host}
  • \n ))}\n
\n
\n )}\n\n {Array.isArray(selectedPlugin.manifest?.permissions) ? (\n
\n

Разрешения

\n
    \n {(selectedPlugin.manifest?.permissions ?? []).map((permission: string, idx: number) => (\n
  • {permission}
  • \n ))}\n
\n
\n ) : null}\n\n \n\n {/* Пользовательские настройки */}\n {(selectedPlugin.manifest?.options && Object.keys(selectedPlugin.manifest.options).length > 0) ? (\n
\n

Дополнительные настройки

\n {customSettingElements}\n
\n ) : null}\n\n\n
\n
\n
\n );\n};\n\nexport { PluginDetails };\n\nexport default PluginDetails;\n","import '../../../options/src/Options.css'; // Импорт стилей из options для идентичности\nimport type React from 'react';\nimport OptionsPluginDetails from '../../../options/src/components/PluginDetails';\nimport type { PluginSettings } from '@extension/storage';\n\ninterface PluginDetailsProps {\n plugin: Plugin;\n onUpdateSetting?: (pluginId: string, setting: string, value: boolean) => Promise;\n}\n\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\nexport const PluginDetails: React.FC = ({ plugin, onUpdateSetting }) => {\n // Адаптируем onUpdateSetting для совместимости с OptionsPluginDetails\n const adaptedOnUpdateSetting = onUpdateSetting ? async (pluginId: string, setting: keyof PluginSettings, value: boolean): Promise => {\n try {\n await onUpdateSetting(pluginId, setting as string, value);\n return true; // Успешно\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n return false; // Неудача\n }\n } : undefined;\n\n const adaptedProps = {\n selectedPlugin: {\n ...plugin,\n description: plugin.description || 'Описание не указано',\n icon: plugin.icon || '',\n manifest: plugin.manifest || {} as any\n },\n locale: 'ru' as const,\n onUpdateSetting: adaptedOnUpdateSetting\n };\n\n return ;\n};\n","import type { ExcludeValuesFromBaseArrayType } from './types.js';\n\nexport const excludeValuesFromBaseArray = (\n baseArray: B,\n excludeArray: E,\n) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType;\n\nexport const sleep = async (time: number) => new Promise(r => setTimeout(r, time));\n\n/**\n * Унифицированное получение pageKey из URL страницы.\n * Удаляет search/hash, возвращает нормализованный URL или 'unknown-page'.\n */\nexport const getPageKey = function (currentTabUrl: string | null): string {\n if (!currentTabUrl) return 'unknown-page';\n try {\n const url = new URL(currentTabUrl);\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return currentTabUrl;\n }\n};\n","import { useState, useEffect, useRef, useCallback } from 'react';\n\ninterface UseLazyChatSyncOptions {\n pluginId: string;\n pageKey: string;\n debounceMs?: number; // Задержка перед синхронизацией (по умолчанию 1000ms)\n}\n\ninterface UseLazyChatSyncReturn {\n message: string;\n setMessage: (text: string) => void;\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n loadDraft: () => Promise;\n clearDraft: () => Promise;\n draftText: string;\n}\n\nexport const useLazyChatSync = ({\n pluginId,\n pageKey,\n debounceMs = 1000,\n}: UseLazyChatSyncOptions): UseLazyChatSyncReturn => {\n const [message, setMessageState] = useState('');\n const [isDraftSaved, setIsDraftSaved] = useState(false);\n const [isDraftLoading, setIsDraftLoading] = useState(false);\n const [draftError, setDraftError] = useState(null);\n const [draftText, setDraftText] = useState('');\n\n const debounceRef = useRef(null);\n const lastSavedText = useRef('');\n\n // Функция для сохранения черновика\n const saveDraft = useCallback(\n async (text: string) => {\n if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился\n console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: text,\n });\n lastSavedText.current = text;\n setIsDraftSaved(true);\n setDraftError(null);\n setDraftText(text);\n console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text });\n } catch (error) {\n console.error('[useLazyChatSync] Error saving draft:', error);\n setDraftError('Ошибка сохранения черновика');\n setIsDraftSaved(false);\n }\n },\n [pluginId, pageKey],\n );\n\n // Функция для загрузки черновика\n const loadDraft = useCallback(async () => {\n setIsDraftLoading(true);\n setDraftError(null);\n console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey });\n try {\n const response = await chrome.runtime.sendMessage({\n type: 'GET_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n });\n if (response?.draftText) {\n setMessageState(response.draftText);\n lastSavedText.current = response.draftText;\n setIsDraftSaved(true);\n setDraftText(response.draftText);\n console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText });\n } else {\n setMessageState('');\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftText('');\n console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey });\n }\n } catch (error) {\n console.error('[useLazyChatSync] Error loading draft:', error);\n setDraftError('Ошибка загрузки черновика');\n } finally {\n setIsDraftLoading(false);\n }\n }, [pluginId, pageKey]);\n\n // Функция для очистки черновика\n const clearDraft = useCallback(async () => {\n console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: '',\n });\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftError(null);\n setDraftText('');\n setMessageState('');\n console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey });\n } catch (error) {\n console.error('[useLazyChatSync] Error clearing draft:', error);\n setDraftError('Ошибка очистки черновика');\n }\n }, [pluginId, pageKey]);\n\n // Обновленная функция setMessage с ленивой синхронизацией\n const setMessage = useCallback(\n (text: string) => {\n setMessageState(text);\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (text.length === 0) {\n clearDraft();\n } else {\n debounceRef.current = setTimeout(() => {\n saveDraft(text);\n }, debounceMs);\n }\n console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text });\n },\n [debounceMs, saveDraft, clearDraft, pluginId, pageKey],\n );\n\n // Очистка таймера при размонтировании\n useEffect(\n () => () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n },\n [],\n );\n\n // Автоматическая загрузка черновика при монтировании\n useEffect(() => {\n loadDraft();\n }, [loadDraft]);\n\n // При смене pageKey всегда загружаем draft, не сбрасываем message вручную\n useEffect(() => {\n console.log('[useLazyChatSync] pageKey изменился:', pageKey);\n setIsDraftSaved(false);\n setIsDraftLoading(false);\n setDraftError(null);\n lastSavedText.current = '';\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n loadDraft();\n }, [pageKey, loadDraft]);\n\n return {\n message,\n setMessage,\n isDraftSaved,\n isDraftLoading,\n draftError,\n loadDraft,\n clearDraft,\n draftText,\n };\n};\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","/**\n * Storage area type for persisting and exchanging data.\n * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview\n */\nexport var StorageEnum;\n(function (StorageEnum) {\n /**\n * Persist data locally against browser restarts. Will be deleted by uninstalling the extension.\n * @default\n */\n StorageEnum[\"Local\"] = \"local\";\n /**\n * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply.\n */\n StorageEnum[\"Sync\"] = \"sync\";\n /**\n * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a\n * json schema for company wide config.\n */\n StorageEnum[\"Managed\"] = \"managed\";\n /**\n * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and\n * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access.\n * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session)\n */\n StorageEnum[\"Session\"] = \"session\";\n})(StorageEnum || (StorageEnum = {}));\n/**\n * Global access level requirement for the {@link StorageEnum.Session} Storage Area.\n * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel)\n */\nexport var SessionAccessLevelEnum;\n(function (SessionAccessLevelEnum) {\n /**\n * Storage can only be accessed by Extension pages (not Content scripts).\n * @default\n */\n SessionAccessLevelEnum[\"ExtensionPagesOnly\"] = \"TRUSTED_CONTEXTS\";\n /**\n * Storage can be accessed by both Extension pages and Content scripts.\n */\n SessionAccessLevelEnum[\"ExtensionPagesAndContentScripts\"] = \"TRUSTED_AND_UNTRUSTED_CONTEXTS\";\n})(SessionAccessLevelEnum || (SessionAccessLevelEnum = {}));\n","import { SessionAccessLevelEnum, StorageEnum } from './enums.js';\n/**\n * Chrome reference error while running `processTailwindFeatures` in tailwindcss.\n * To avoid this, we need to check if globalThis.chrome is available and add fallback logic.\n */\nconst chrome = globalThis.chrome;\n/**\n * Sets or updates an arbitrary cache with a new value or the result of an update function.\n */\nconst updateCache = async (valueOrUpdate, cache) => {\n // Type guard to check if our value or update is a function\n const isFunction = (value) => typeof value === 'function';\n // Type guard to check in case of a function if it's a Promise\n const returnsPromise = (func) => \n // Use ReturnType to infer the return type of the function and check if it's a Promise\n func instanceof Promise;\n if (isFunction(valueOrUpdate)) {\n // Check if the function returns a Promise\n if (returnsPromise(valueOrUpdate)) {\n return valueOrUpdate(cache);\n }\n else {\n return valueOrUpdate(cache);\n }\n }\n else {\n return valueOrUpdate;\n }\n};\n/**\n * If one session storage needs access from content scripts, we need to enable it globally.\n * @default false\n */\nlet globalSessionAccessLevelFlag = false;\n/**\n * Checks if the storage permission is granted in the manifest.json.\n */\nconst checkStoragePermission = (storageEnum) => {\n if (!chrome) {\n return;\n }\n if (!chrome.storage[storageEnum]) {\n throw new Error(`\"storage\" permission in manifest.ts: \"storage ${storageEnum}\" isn't defined`);\n }\n};\n/**\n * Creates a storage area for persisting and exchanging data.\n */\nexport const createStorage = (key, fallback, config) => {\n let cache = null;\n let initialCache = false;\n let listeners = [];\n const storageEnum = config?.storageEnum ?? StorageEnum.Local;\n const liveUpdate = config?.liveUpdate ?? false;\n const serialize = config?.serialization?.serialize ?? ((v) => v);\n const deserialize = config?.serialization?.deserialize ?? (v => v);\n // Set global session storage access level for StoryType.Session, only when not already done but needed.\n if (globalSessionAccessLevelFlag === false &&\n storageEnum === StorageEnum.Session &&\n config?.sessionAccessForContentScripts === true) {\n checkStoragePermission(storageEnum);\n chrome?.storage[storageEnum]\n .setAccessLevel({\n accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts,\n })\n .catch(error => {\n console.error(error);\n console.error('Please call .setAccessLevel() into different context, like a background script.');\n });\n globalSessionAccessLevelFlag = true;\n }\n // Register life cycle methods\n const get = async () => {\n checkStoragePermission(storageEnum);\n const value = await chrome?.storage[storageEnum].get([key]);\n if (!value) {\n return fallback;\n }\n return deserialize(value[key]) ?? fallback;\n };\n const set = async (valueOrUpdate) => {\n if (!initialCache) {\n cache = await get();\n }\n cache = await updateCache(valueOrUpdate, cache);\n await chrome?.storage[storageEnum].set({ [key]: serialize(cache) });\n _emitChange();\n };\n const subscribe = (listener) => {\n listeners = [...listeners, listener];\n return () => {\n listeners = listeners.filter(l => l !== listener);\n };\n };\n const getSnapshot = () => cache;\n const _emitChange = () => {\n listeners.forEach(listener => listener());\n };\n // Listener for live updates from the browser\n const _updateFromStorageOnChanged = async (changes) => {\n // Check if the key we are listening for is in the changes object\n if (changes[key] === undefined)\n return;\n const valueOrUpdate = deserialize(changes[key].newValue);\n if (cache === valueOrUpdate)\n return;\n cache = await updateCache(valueOrUpdate, cache);\n _emitChange();\n };\n get().then(data => {\n cache = data;\n initialCache = true;\n _emitChange();\n });\n // Register listener for live updates for our storage area\n if (liveUpdate) {\n chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);\n }\n return {\n get,\n set,\n getSnapshot,\n subscribe,\n };\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('theme-storage-key', {\n theme: 'system',\n isLight: getSystemTheme(),\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\n// Функция для определения системной темы\nfunction getSystemTheme() {\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: light)').matches;\n }\n return true; // По умолчанию светлая тема\n}\nexport const exampleThemeStorage = {\n ...storage,\n toggle: async () => {\n await storage.set(currentState => {\n let newTheme;\n switch (currentState.theme) {\n case 'light':\n newTheme = 'dark';\n break;\n case 'dark':\n newTheme = 'system';\n break;\n case 'system':\n default:\n newTheme = 'light';\n break;\n }\n const isLight = newTheme === 'system' ? getSystemTheme() : newTheme === 'light';\n return {\n theme: newTheme,\n isLight,\n };\n });\n },\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('chat-alignment-storage-key', {\n alignment: 'left',\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\nexport const exampleChatAlignmentStorage = {\n ...storage,\n setAlignment: async (alignment) => {\n await storage.set({ alignment });\n },\n getAlignment: async () => {\n const state = await storage.get();\n return state.alignment;\n },\n};\n","/**\n * PluginControlPanel.tsx - Панель управления плагином с чатом\n *\n * ИСПРАВЛЕНИЕ ПРОБЛЕМЫ С ПОЛУЧЕНИЕМ ОТВЕТА GET_PLUGIN_CHAT:\n * ========================================================\n *\n * Проблема: Background отправлял ответ через sendResponse() callback, но компонент\n * ожидал ответ через chrome.runtime.sendMessage() с типом 'GET_PLUGIN_CHAT_RESPONSE'.\n *\n * Решение: Изменен механизм коммуникации на использование Promise-based подхода\n * с помощью sendMessageToBackgroundAsync(), который правильно работает с sendResponse().\n *\n * Теперь:\n * 1. Компонент отправляет GET_PLUGIN_CHAT через chrome.runtime.sendMessage()\n * 2. Background получает сообщение и отвечает через sendResponse()\n * 3. Компонент получает ответ через Promise и обрабатывает его\n *\n * Диагностика:\n * - Логи sendMessageToBackgroundAsync покажут отправку и получение ответа\n * - Логи loadChat покажут обработку ответа\n * - Логи processChatResponse покажут разбор данных чата\n */\n\nimport { DraftStatus } from './DraftStatus';\nimport { PluginDetails } from './PluginDetails';\nimport { getPageKey } from '../../../../packages/shared/lib/utils/helpers';\nimport { useLazyChatSync } from '../hooks/useLazyChatSync';\nimport { saveAs } from 'file-saver';\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport './PluginControlPanel.css';\nimport type React from 'react';\nimport { exampleChatAlignmentStorage, type ChatAlignment } from '@extension/storage';\n\n// Определение типа Plugin для PluginControlPanel\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Новый тип для сообщений чата\ninterface ChatMessage {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: number;\n}\n\ninterface PluginControlPanelProps {\n plugin: Plugin;\n currentView: PanelView;\n isRunning: boolean;\n isPaused: boolean;\n currentTabUrl: string | null;\n onStart: () => void;\n onPause: () => void;\n onStop: () => void;\n onClose: () => void;\n}\n\nexport type PanelView = 'chat' | 'details';\n\nexport const PluginControlPanel: React.FC = ({\n plugin,\n currentView,\n isRunning,\n isPaused,\n currentTabUrl,\n onStart,\n onPause,\n onStop,\n onClose,\n}) => {\n // Состояние для активной вкладки в панели управления\n const [activeTab, setActiveTab] = useState('chat');\n // Состояние для текущего pageKey с динамическим обновлением\n const [currentPageKey, setCurrentPageKey] = useState(getPageKey(currentTabUrl));\n\n const [chatTextAlign, setChatTextAlign] = useState('left');\n\n // useEffect для обновления pageKey при изменении currentTabUrl\n useEffect(() => {\n const newPageKey = getPageKey(currentTabUrl);\n console.log('[PluginControlPanel] currentTabUrl изменился:', {\n oldPageKey: currentPageKey,\n newPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n setCurrentPageKey(newPageKey);\n }, [currentTabUrl]);\n\n useEffect(() => {\n const loadAlignment = async () => {\n const alignment = await exampleChatAlignmentStorage.getAlignment();\n setChatTextAlign(alignment);\n };\n\n loadAlignment();\n\n const unsubscribe = exampleChatAlignmentStorage.subscribe(() => {\n loadAlignment();\n });\n\n return unsubscribe;\n }, []);\n // Используем хук для ленивой синхронизации\n const { message, setMessage, isDraftSaved, isDraftLoading, draftError, loadDraft, clearDraft, draftText } =\n useLazyChatSync({\n pluginId: plugin.id,\n pageKey: currentPageKey, // <-- Теперь динамический pageKey\n debounceMs: 1000, // 1 секунда задержки\n });\n\n const [messages, setMessages] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n const [inputHeight, setInputHeight] = useState(60); // Начальная высота поля ввода\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef(null);\n const textareaRef = useRef(null);\n\n useEffect(() => {\n if (!isRunning) {\n // Удалить все вызовы setStopped(...)\n }\n }, [isRunning]);\n\n const handleStart = () => {\n // Удалить все вызовы setStopped(...)\n onStart();\n };\n\n const pluginName =\n plugin.name || (typeof plugin.manifest?.name === 'string' ? plugin.manifest.name : '') || plugin.id;\n\n // Получаем ключ чата для текущего плагина и страницы\n const pluginId = plugin.id;\n\n // Вспомогательная функция для отправки сообщений в background с ожиданием ответа\n const sendMessageToBackgroundAsync = useCallback(async (message: any): Promise => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n try {\n const response = await chrome.runtime.sendMessage(messageWithId);\n return response;\n } catch (error) {\n console.error('[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:', error);\n throw error;\n }\n }, []);\n\n // Вспомогательная функция для отправки сообщений в background без ожидания ответа (для обратной совместимости)\n const sendMessageToBackground = useCallback((message: any): void => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n chrome.runtime.sendMessage(messageWithId);\n }, []);\n\n // Функция для тестирования обработки сообщений с проблемными данными\n const testMessageProcessing = useCallback(() => {\n console.log('[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными');\n\n // Тест 1: Сообщение с объектом вместо строки в text\n const testMessageWithObject = {\n messages: [{\n id: 'test_obj_1',\n text: { content: 'Это объект вместо строки', type: 'object' }, // Объект вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 2: Сообщение с null в text\n const testMessageWithNull = {\n messages: [{\n id: 'test_null_1',\n text: null, // null вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 3: Сообщение с undefined в text\n const testMessageWithUndefined = {\n messages: [{\n id: 'test_undef_1',\n text: undefined, // undefined вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Объявляем processChatResponse локально для избежания проблем с temporal dead zone\n const localProcessChatResponse = (response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(msg.timestamp || Date.now() + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: msg.timestamp || Date.now(),\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n };\n\n try {\n console.log('[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text');\n localProcessChatResponse(testMessageWithObject);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 1 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 2: Обработка сообщения с null в text');\n localProcessChatResponse(testMessageWithNull);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 2 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text');\n localProcessChatResponse(testMessageWithUndefined);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 3 провалился:', error);\n }\n\n console.log('[PluginControlPanel] ✅ Тестирование обработки сообщений завершено');\n }, []); // Убрали processChatResponse из зависимостей\n\n // Вспомогательная функция для обработки ответа чата\n const processChatResponse = useCallback((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n\n console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${index}:`, textContent);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n\n console.log(`[PluginControlPanel] TextContent after JSON parse for message ${index}:`, textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n console.log(`[PluginControlPanel] Final converted text for message ${index}:`, convertedMsg.text);\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n }, []);\n\n // Загрузка истории чата при монтировании или смене плагина/страницы\n const loadChat = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n console.log('[PluginControlPanel] ===== НАЧАЛО loadChat =====', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString(),\n isRunning,\n isPaused\n });\n\n try {\n console.log('[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT');\n const response = await sendMessageToBackgroundAsync({\n type: 'GET_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n console.log('[PluginControlPanel] loadChat - получен ответ от background:', response);\n console.log('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:', {\n loading,\n messagesCount: messages.length,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку при получении ответа\n console.log('[PluginControlPanel] loadChat - loading сброшен в false');\n\n if (response?.error) {\n console.error('[PluginControlPanel] loadChat - ошибка в ответе:', response.error);\n setError(`Ошибка загрузки чата: ${response.error}`);\n setMessages([]);\n } else {\n console.log('[PluginControlPanel] loadChat - обрабатываем успешный ответ');\n // Используем анонимную функцию вместо прямого вызова processChatResponse\n // чтобы избежать проблемы с temporal dead zone\n ((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n \n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n \n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распаршен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n }\n \n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n })(response);\n console.log('[PluginControlPanel] loadChat: чат успешно загружен');\n }\n\n } catch (error) {\n console.error('[PluginControlPanel] loadChat - ошибка при получении ответа:', error);\n console.error('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:', {\n loading,\n messagesCount: messages.length,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString()\n });\n\n // Детальное логирование ошибки с трассировкой стека\n console.error('[PluginControlPanel] loadChat - ERROR DETAILS:', {\n error,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : 'No stack trace',\n errorName: error instanceof Error ? error.name : 'Unknown error type',\n timestamp: new Date().toISOString(),\n pluginId,\n pageKey: currentPageKey\n });\n\n setLoading(false);\n console.log('[PluginControlPanel] loadChat - loading сброшен в false в catch блоке');\n\n // Улучшенная обработка ошибок с проверкой типа\n let errorMessage = 'Ошибка связи с background';\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Специальная обработка для TypeError с substring\n if (error.name === 'TypeError' && error.message.includes('substring')) {\n errorMessage += ' (ошибка обработки текста - проверьте тип данных)';\n console.error('[PluginControlPanel] CRITICAL: substring error detected:', {\n originalError: error,\n stack: error.stack,\n context: { pluginId, pageKey: currentPageKey }\n });\n }\n } else {\n errorMessage += `: ${String(error)}`;\n }\n\n setError(errorMessage);\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====');\n }, [pluginId, currentPageKey, sendMessageToBackgroundAsync, currentTabUrl, isRunning, isPaused]);\n\n // Добавить useEffect для вызова loadChat при монтировании и смене pluginId/pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n\n // Вызываем асинхронную функцию без await, так как useEffect не может быть async\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:', error);\n });\n }, [loadChat]);\n\n // useEffect для перезагрузки чата при смене pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик');\n // Перезагружаем чат для новой страницы\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] Ошибка при перезагрузке чата:', error);\n });\n // Перезагружаем черновик для новой страницы\n loadDraft();\n }, [currentPageKey, loadChat, loadDraft]);\n\n // Событийная синхронизация чата между вкладками и обработка результатов сохранения сообщений\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений', {\n pluginId,\n pageKey: currentPageKey,\n timestamp: new Date().toISOString()\n });\n\n const handleChatUpdate = (event: { type: string; pluginId: string; pageKey: string; messages?: ChatMessage[]; messageId?: string; response?: any; success?: boolean; error?: string; message?: any; timestamp?: number }) => {\n console.log('[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====', {\n type: event?.type,\n pluginId: event?.pluginId,\n pageKey: event?.pageKey,\n messageId: event?.messageId,\n hasResponse: !!event?.response,\n responseType: event?.response ? typeof event.response : 'none',\n timestamp: new Date().toISOString()\n });\n\n // Обработка обновлений чата от других компонентов/вкладок\n if (event?.type === 'PLUGIN_CHAT_UPDATED' && event.pluginId === pluginId && event.pageKey === currentPageKey) {\n console.log('[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные');\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:', {\n loading,\n messagesCount: messages.length,\n currentPageKey,\n pluginId,\n timestamp: new Date().toISOString()\n });\n setLoading(false); // Гарантированный сброс loading перед загрузкой чата\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat');\n // Запрашиваем актуальные данные чата асинхронно\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:', error);\n });\n }\n\n // NOTE: GET_PLUGIN_CHAT_RESPONSE больше не обрабатывается здесь,\n // поскольку ответы на GET_PLUGIN_CHAT теперь обрабатываются через Promise в loadChat()\n\n // Логируем все сообщения, которые приходят, но не обрабатываются\n if (event?.type !== 'PLUGIN_CHAT_UPDATED' && event?.type !== 'GET_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:', {\n type: event?.type,\n fullEvent: event,\n timestamp: new Date().toISOString()\n });\n }\n\n // Обработка результатов сохранения сообщений\n if (event?.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:', event);\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения', event.error);\n setError(`Ошибка сохранения сообщения: ${event.error}`);\n }\n }\n\n // Обработка результатов удаления чата\n if (event?.type === 'DELETE_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат удаления чата:', event);\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:', {\n loading,\n messagesCount: messages.length,\n eventSuccess: event.success,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE');\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: чат успешно удален');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка удаления чата', event.error);\n setError(`Ошибка удаления чата: ${event.error}`);\n }\n }\n\n // === PYODIDE MESSAGE HANDLER ===\n if (event?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', event.message);\n\n if (event.message?.content) {\n try {\n // Строгая проверка типа content для Pyodide сообщений\n let content = event.message.content;\n let messageTimestamp = event.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] PYODIDE content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] PYODIDE content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из PYODIDE content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: event.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:', pyodideError, event);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:', event.message);\n }\n }\n };\n\n // Слушатель для обработки результатов операций с чатом\n const handleChatOperationResult = (message: any) => {\n if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения', message);\n\n if (message.success) {\n console.log('[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено');\n // Не нужно ничего делать дополнительно - обновление придет через PLUGIN_CHAT_UPDATED\n } else {\n console.error('[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения', message.error);\n setError(`Ошибка сохранения сообщения: ${message.error}`);\n }\n }\n };\n\n chrome.runtime.onMessage.addListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы');\n\n return () => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений');\n chrome.runtime.onMessage.removeListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n };\n }, [pluginId, currentPageKey, sendMessageToBackground, loadChat]);\n\n // Восстановление черновика при возврате на вкладку 'Чат'\n useEffect(() => {\n if (currentView === 'chat') {\n loadDraft(); // Явно загружаем черновик при возврате на вкладку чата\n }\n }, [currentView, loadDraft]);\n\n // Логирование каждого рендера и ключевых параметров\n useEffect(() => {\n console.log('[PluginControlPanel] === РЕНДЕР ===', {\n pluginId,\n pageKey: currentPageKey,\n draftText,\n message,\n currentView,\n isRunning,\n isPaused,\n });\n });\n\n // Глобальный обработчик ошибок для ловли проблем с substring\n useEffect(() => {\n const originalConsoleError = console.error;\n console.error = (...args) => {\n // Перехватываем ошибки substring\n const errorMessage = args.join(' ');\n if (errorMessage.includes('substring') && errorMessage.includes('is not a function')) {\n console.error('[PluginControlPanel] 🔴 CRITICAL: substring error detected!');\n console.error('[PluginControlPanel] Error details:', args);\n console.error('[PluginControlPanel] Stack trace:', new Error().stack);\n // Не блокируем оригинальную обработку ошибки\n }\n originalConsoleError.apply(console, args);\n };\n\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring активирован');\n\n return () => {\n console.error = originalConsoleError;\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован');\n };\n }, []);\n\n // Слушатель для Pyodide сообщений через custom events\n useEffect(() => {\n console.log('[PluginControlPanel] Настройка слушателя для pyodide messages');\n\n const handlePyodideCustomEvent = (event: any) => {\n const data = event.detail;\n console.log('[PluginControlPanel] Получен Pyodide custom event:', data);\n\n if (data?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', data.message);\n\n if (data.message?.content) {\n try {\n // Строгая проверка типа content\n let content = data.message.content;\n let messageTimestamp = data.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] Pyodide content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] Pyodide content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из Pyodide content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Pyodide content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: data.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки Pyodide сообщения:', pyodideError, data);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] Pyodide сообщение без content:', data.message);\n }\n }\n };\n\n window.addEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован');\n\n return () => {\n window.removeEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events удален');\n };\n }, []);\n\n // --- Синхронизация message с draftText после загрузки черновика ---\n useEffect(() => {\n if (typeof draftText === 'string') {\n setMessage(draftText);\n console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText);\n }\n }, [draftText, setMessage]);\n\n const handleSendMessage = (): void => {\n console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message });\n if (!message.trim()) return;\n\n const newMessage: ChatMessage = {\n id: Date.now().toString(),\n text: message.trim(),\n isUser: true,\n timestamp: Date.now(),\n };\n\n // Очищаем сообщение через хук\n setMessage('');\n setError(null); // Сбрасываем предыдущие ошибки\n\n console.log('[PluginControlPanel] handleSendMessage: отправка сообщения в background');\n\n sendMessageToBackground({\n type: 'SAVE_PLUGIN_CHAT_MESSAGE',\n pluginId,\n pageKey: currentPageKey,\n message: {\n role: 'user',\n content: newMessage.text,\n timestamp: newMessage.timestamp,\n },\n });\n\n // Очищаем черновик сразу после отправки\n clearDraft();\n };\n\n // Обработка изменения размера разделителя\n useEffect(() => {\n const handleResizeMove = (event: MouseEvent): void => {\n if (!isResizing) return;\n\n const container = document.querySelector('.chat-view') as HTMLElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n const newHeight = containerRect.bottom - event.clientY;\n const minHeight = 100; // Минимальная высота чата\n const maxHeight = containerRect.height - 80; // Максимальная высота чата\n\n if (newHeight >= minHeight && newHeight <= maxHeight) {\n setInputHeight(containerRect.height - newHeight);\n }\n };\n\n const handleResizeEnd = (): void => {\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n if (isResizing) {\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n }\n\n return () => {\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isResizing]);\n\n // Автоскролл к последнему сообщению\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[messages] - состояние messages обновлено:', {\n messagesCount: messages.length,\n firstMessage: messages[0] ? {\n id: messages[0].id,\n text: typeof messages[0].text === 'string' ? messages[0].text.substring(0, 50) : String(messages[0].text || '').substring(0, 50),\n isUser: messages[0].isUser,\n timestamp: messages[0].timestamp,\n textType: typeof messages[0].text\n } : null,\n // Безопасная обработка всех сообщений с проверкой типов\n allMessages: messages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 30) : String(m.text || '').substring(0, 30);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (msgError) {\n console.warn('[PluginControlPanel] Error in message logging:', msgError, m);\n return { id: m.id, text: '[LOGGING ERROR]', isUser: m.isUser, textType: typeof m.text };\n }\n }),\n timestamp: new Date().toISOString()\n });\n }, [messages]);\n\n // Фокус на поле ввода при открытии чата\n useEffect(() => {\n if (currentView === 'chat') {\n setTimeout(() => textareaRef.current?.focus(), 100);\n }\n }, [currentView]);\n\n const handleTextareaChange = (event: React.ChangeEvent): void => {\n setMessage(event.target.value); // Используем хук вместо setMessage\n // Автоматическое изменение высоты\n const textarea = event.target;\n textarea.style.height = 'auto';\n const newHeight = Math.min(Math.max(textarea.scrollHeight, 60), 200); // Минимум 60px, максимум 200px\n textarea.style.height = `${newHeight}px`;\n setInputHeight(newHeight);\n };\n\n const handleKeyPress = (event: React.KeyboardEvent): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n // Очистка чата (удаление всей истории)\n const handleClearChat = (): void => {\n setLoading(true);\n setError(null);\n\n sendMessageToBackground({\n type: 'DELETE_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n // Очищаем локальное состояние сразу\n setMessages([]);\n clearDraft(); // Очищаем черновик\n };\n\n // Экспорт чата в JSON\n const handleExportChat = (): void => {\n const data = JSON.stringify(messages, null, 2);\n const blob = new Blob([data], { type: 'application/json' });\n saveAs(blob, `plugin-chat-${pluginId}.json`);\n };\n\n return (\n
\n
\n
\n {\n const firstChar = typeof pluginName === 'string' && pluginName.length > 0 ? pluginName.charAt(0) : 'P';\n (event.currentTarget as HTMLImageElement).src =\n `data:image/svg+xml;utf8,${firstChar}`;\n }}\n />\n

{pluginName}

\n
\n
\n \n {isRunning ? '⏹️' : '▶️'}\n \n \n ⏸️\n \n \n ⏹️\n \n \n ✕\n \n
\n
\n
\n setActiveTab('chat')}\n >\n Чат\n \n setActiveTab('details')}\n >\n Детали\n \n
\n
\n {activeTab === 'chat' && (\n
\n
\n

Чат

\n
\n \n \n \n 🧪 Тест\n \n
\n
\n
\n {loading &&
Загрузка сообщений...
}\n {error &&
{error}
}\n {!loading && !error && messages.length === 0 && (\n
\n

Нет сообщений

\n

Напишите первое сообщение!

\n
\n )}\n {/* Отображение сообщений чата */}\n
\n {messages.map((msg, idx) => {\n console.log('[PluginControlPanel] render message:', idx, msg);\n\n // Парсинг JSON в тексте сообщения перед рендерингом\n let displayText = msg.text;\n let displayTimestamp = msg.timestamp;\n\n console.log('[PluginControlPanel] Raw message text before parsing for message', idx, ':', msg.text);\n\n try {\n const parsed = JSON.parse(displayText);\n if (typeof parsed === 'object' && parsed !== null && 'content' in parsed) {\n console.log('[PluginControlPanel] Парсинг JSON в рендере:', parsed);\n let content = parsed.content;\n if (typeof content === 'object') {\n displayText = JSON.stringify(content);\n } else {\n displayText = String(content || '');\n }\n if (parsed.timestamp && typeof parsed.timestamp === 'number') {\n displayTimestamp = parsed.timestamp;\n }\n } else if (typeof parsed === 'string') {\n // Если JSON содержит просто строку\n displayText = parsed;\n } else if (typeof parsed === 'object' && parsed !== null) {\n // Если JSON содержит объект без поля content, берем первое строковое поле\n const stringFields = Object.values(parsed).filter(val => typeof val === 'string');\n if (stringFields.length > 0) {\n displayText = String(stringFields[0]);\n } else {\n displayText = JSON.stringify(parsed);\n }\n }\n } catch (parseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Текст не является JSON, рендерим как есть');\n }\n\n console.log('[PluginControlPanel] Display text after parsing for message', idx, ':', displayText);\n\n return (\n \n
\n {displayText}\n \n {new Date(displayTimestamp).toLocaleTimeString()}\n \n
\n
\n );\n })}\n
\n
\n
\n
\n \n \n 📤\n \n \n
\n
\n )}\n {activeTab === 'details' && (\n \n )}\n
\n
\n );\n};","import React, { useState, useEffect, useCallback } from 'react';\nimport './ToastNotifications.css';\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n message: string;\n type: ToastType;\n duration: number;\n timestamp: number;\n}\n\ninterface ToastNotificationsProps {\n toasts: Toast[];\n onRemove: (id: string) => void;\n}\n\nexport const ToastNotifications: React.FC = ({ toasts, onRemove }) => {\n return (\n
\n {toasts.map((toast) => (\n \n ))}\n
\n );\n};\n\ninterface ToastItemProps {\n toast: Toast;\n onRemove: (id: string) => void;\n}\n\nconst ToastItem: React.FC = ({ toast, onRemove }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [isHiding, setIsHiding] = useState(false);\n\n useEffect(() => {\n // Animation in\n requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n // Auto remove\n if (toast.duration > 0) {\n const timer = setTimeout(() => {\n handleRemove();\n }, toast.duration);\n\n return () => clearTimeout(timer);\n }\n\n return undefined; // Explicitly return undefined when no timer is set\n }, [toast.duration]);\n\n const handleRemove = useCallback(() => {\n setIsHiding(true);\n setTimeout(() => {\n onRemove(toast.id);\n }, 300); // Animation duration\n }, [toast.id, onRemove]);\n\n return (\n
\n
\n {toast.message}\n \n
\n
\n );\n};\n\n// Toast manager hook\nexport function useToastManager() {\n const [toasts, setToasts] = useState([]);\n\n const addToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {\n const id = `toast-${Date.now()}-${Math.random()}`;\n const newToast: Toast = {\n id,\n message,\n type,\n duration,\n timestamp: Date.now()\n };\n\n setToasts(prev => {\n const updated = [...prev, newToast];\n // Limit to 3 toasts\n return updated.slice(-3);\n });\n\n return id;\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts(prev => prev.filter(toast => toast.id !== id));\n }, []);\n\n const clearAll = useCallback(() => {\n setToasts([]);\n }, []);\n\n return {\n toasts,\n addToast,\n removeToast,\n clearAll\n };\n}\n\n// Convenience functions\nexport const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {\n // This will be used with the toast manager\n console.log(`[Toast] ${type}: ${message}`);\n};\n\nexport const showSuccessToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'success', duration);\n};\n\nexport const showErrorToast = (message: string, duration: number = 5000) => {\n return showToast(message, 'error', duration);\n};\n\nexport const showWarningToast = (message: string, duration: number = 4000) => {\n return showToast(message, 'warning', duration);\n};\n\nexport const showInfoToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'info', duration);\n}; ","import React from 'react';\n\ninterface ThemeSwitcherProps {\n theme: 'light' | 'dark' | 'system';\n isLight: boolean;\n onToggle: () => void;\n isInSidebar?: boolean; // Контекст использования: sidebar или options\n}\n\nconst ThemeSwitcher: React.FC = ({ theme, isLight, onToggle, isInSidebar = false }) => {\n const getIcon = () => {\n switch (theme) {\n case 'light':\n return '🌙'; // Moon - to switch to dark\n case 'dark':\n return '💻'; // System icon - to switch to system\n case 'system':\n return '☀️'; // Sun - to switch to light\n default:\n return '🌙';\n }\n };\n\n const getTitle = () => {\n switch (theme) {\n case 'light':\n return 'Переключить на темную тему';\n case 'dark':\n return 'Переключить на системную тему';\n case 'system':\n return 'Переключить на светлую тему';\n default:\n return 'Переключить тему';\n }\n };\n\n const buttonStyle: React.CSSProperties = {\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: '50%',\n width: '40px',\n height: '40px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '20px',\n // marginTop только для Options (не в sidebar)\n ...(isInSidebar ? {} : { marginTop: '20px' })\n };\n\n return (\n \n );\n};\n\nexport default ThemeSwitcher;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t {\n const classMap = createClassMap(config);\n const {\n conflictingClassGroups,\n conflictingClassGroupModifiers\n } = config;\n const getClassGroupId = className => {\n const classParts = className.split(CLASS_PART_SEPARATOR);\n // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.\n if (classParts[0] === '' && classParts.length !== 1) {\n classParts.shift();\n }\n return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);\n };\n const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {\n const conflicts = conflictingClassGroups[classGroupId] || [];\n if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {\n return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];\n }\n return conflicts;\n };\n return {\n getClassGroupId,\n getConflictingClassGroupIds\n };\n};\nconst getGroupRecursive = (classParts, classPartObject) => {\n if (classParts.length === 0) {\n return classPartObject.classGroupId;\n }\n const currentClassPart = classParts[0];\n const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);\n const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;\n if (classGroupFromNextClassPart) {\n return classGroupFromNextClassPart;\n }\n if (classPartObject.validators.length === 0) {\n return undefined;\n }\n const classRest = classParts.join(CLASS_PART_SEPARATOR);\n return classPartObject.validators.find(({\n validator\n }) => validator(classRest))?.classGroupId;\n};\nconst arbitraryPropertyRegex = /^\\[(.+)\\]$/;\nconst getGroupIdForArbitraryProperty = className => {\n if (arbitraryPropertyRegex.test(className)) {\n const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];\n const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(':'));\n if (property) {\n // I use two dots here because one dot is used as prefix for class groups in plugins\n return 'arbitrary..' + property;\n }\n }\n};\n/**\n * Exported for testing only\n */\nconst createClassMap = config => {\n const {\n theme,\n classGroups\n } = config;\n const classMap = {\n nextPart: new Map(),\n validators: []\n };\n for (const classGroupId in classGroups) {\n processClassesRecursively(classGroups[classGroupId], classMap, classGroupId, theme);\n }\n return classMap;\n};\nconst processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {\n classGroup.forEach(classDefinition => {\n if (typeof classDefinition === 'string') {\n const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);\n classPartObjectToEdit.classGroupId = classGroupId;\n return;\n }\n if (typeof classDefinition === 'function') {\n if (isThemeGetter(classDefinition)) {\n processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);\n return;\n }\n classPartObject.validators.push({\n validator: classDefinition,\n classGroupId\n });\n return;\n }\n Object.entries(classDefinition).forEach(([key, classGroup]) => {\n processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);\n });\n });\n};\nconst getPart = (classPartObject, path) => {\n let currentClassPartObject = classPartObject;\n path.split(CLASS_PART_SEPARATOR).forEach(pathPart => {\n if (!currentClassPartObject.nextPart.has(pathPart)) {\n currentClassPartObject.nextPart.set(pathPart, {\n nextPart: new Map(),\n validators: []\n });\n }\n currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);\n });\n return currentClassPartObject;\n};\nconst isThemeGetter = func => func.isThemeGetter;\n\n// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance\nconst createLruCache = maxCacheSize => {\n if (maxCacheSize < 1) {\n return {\n get: () => undefined,\n set: () => {}\n };\n }\n let cacheSize = 0;\n let cache = new Map();\n let previousCache = new Map();\n const update = (key, value) => {\n cache.set(key, value);\n cacheSize++;\n if (cacheSize > maxCacheSize) {\n cacheSize = 0;\n previousCache = cache;\n cache = new Map();\n }\n };\n return {\n get(key) {\n let value = cache.get(key);\n if (value !== undefined) {\n return value;\n }\n if ((value = previousCache.get(key)) !== undefined) {\n update(key, value);\n return value;\n }\n },\n set(key, value) {\n if (cache.has(key)) {\n cache.set(key, value);\n } else {\n update(key, value);\n }\n }\n };\n};\nconst IMPORTANT_MODIFIER = '!';\nconst MODIFIER_SEPARATOR = ':';\nconst MODIFIER_SEPARATOR_LENGTH = MODIFIER_SEPARATOR.length;\nconst createParseClassName = config => {\n const {\n prefix,\n experimentalParseClassName\n } = config;\n /**\n * Parse class name into parts.\n *\n * Inspired by `splitAtTopLevelOnly` used in Tailwind CSS\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js\n */\n let parseClassName = className => {\n const modifiers = [];\n let bracketDepth = 0;\n let parenDepth = 0;\n let modifierStart = 0;\n let postfixModifierPosition;\n for (let index = 0; index < className.length; index++) {\n let currentCharacter = className[index];\n if (bracketDepth === 0 && parenDepth === 0) {\n if (currentCharacter === MODIFIER_SEPARATOR) {\n modifiers.push(className.slice(modifierStart, index));\n modifierStart = index + MODIFIER_SEPARATOR_LENGTH;\n continue;\n }\n if (currentCharacter === '/') {\n postfixModifierPosition = index;\n continue;\n }\n }\n if (currentCharacter === '[') {\n bracketDepth++;\n } else if (currentCharacter === ']') {\n bracketDepth--;\n } else if (currentCharacter === '(') {\n parenDepth++;\n } else if (currentCharacter === ')') {\n parenDepth--;\n }\n }\n const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);\n const baseClassName = stripImportantModifier(baseClassNameWithImportantModifier);\n const hasImportantModifier = baseClassName !== baseClassNameWithImportantModifier;\n const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;\n return {\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n };\n };\n if (prefix) {\n const fullPrefix = prefix + MODIFIER_SEPARATOR;\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.substring(fullPrefix.length)) : {\n isExternal: true,\n modifiers: [],\n hasImportantModifier: false,\n baseClassName: className,\n maybePostfixModifierPosition: undefined\n };\n }\n if (experimentalParseClassName) {\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => experimentalParseClassName({\n className,\n parseClassName: parseClassNameOriginal\n });\n }\n return parseClassName;\n};\nconst stripImportantModifier = baseClassName => {\n if (baseClassName.endsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(0, baseClassName.length - 1);\n }\n /**\n * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.\n * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864\n */\n if (baseClassName.startsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(1);\n }\n return baseClassName;\n};\n\n/**\n * Sorts modifiers according to following schema:\n * - Predefined modifiers are sorted alphabetically\n * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it\n */\nconst createSortModifiers = config => {\n const orderSensitiveModifiers = Object.fromEntries(config.orderSensitiveModifiers.map(modifier => [modifier, true]));\n const sortModifiers = modifiers => {\n if (modifiers.length <= 1) {\n return modifiers;\n }\n const sortedModifiers = [];\n let unsortedModifiers = [];\n modifiers.forEach(modifier => {\n const isPositionSensitive = modifier[0] === '[' || orderSensitiveModifiers[modifier];\n if (isPositionSensitive) {\n sortedModifiers.push(...unsortedModifiers.sort(), modifier);\n unsortedModifiers = [];\n } else {\n unsortedModifiers.push(modifier);\n }\n });\n sortedModifiers.push(...unsortedModifiers.sort());\n return sortedModifiers;\n };\n return sortModifiers;\n};\nconst createConfigUtils = config => ({\n cache: createLruCache(config.cacheSize),\n parseClassName: createParseClassName(config),\n sortModifiers: createSortModifiers(config),\n ...createClassGroupUtils(config)\n});\nconst SPLIT_CLASSES_REGEX = /\\s+/;\nconst mergeClassList = (classList, configUtils) => {\n const {\n parseClassName,\n getClassGroupId,\n getConflictingClassGroupIds,\n sortModifiers\n } = configUtils;\n /**\n * Set of classGroupIds in following format:\n * `{importantModifier}{variantModifiers}{classGroupId}`\n * @example 'float'\n * @example 'hover:focus:bg-color'\n * @example 'md:!pr'\n */\n const classGroupsInConflict = [];\n const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);\n let result = '';\n for (let index = classNames.length - 1; index >= 0; index -= 1) {\n const originalClassName = classNames[index];\n const {\n isExternal,\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n } = parseClassName(originalClassName);\n if (isExternal) {\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n let hasPostfixModifier = !!maybePostfixModifierPosition;\n let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);\n if (!classGroupId) {\n if (!hasPostfixModifier) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n classGroupId = getClassGroupId(baseClassName);\n if (!classGroupId) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n hasPostfixModifier = false;\n }\n const variantModifier = sortModifiers(modifiers).join(':');\n const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;\n const classId = modifierId + classGroupId;\n if (classGroupsInConflict.includes(classId)) {\n // Tailwind class omitted due to conflict\n continue;\n }\n classGroupsInConflict.push(classId);\n const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);\n for (let i = 0; i < conflictGroups.length; ++i) {\n const group = conflictGroups[i];\n classGroupsInConflict.push(modifierId + group);\n }\n // Tailwind class not in conflict\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n }\n return result;\n};\n\n/**\n * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.\n *\n * Specifically:\n * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js\n * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts\n *\n * Original code has MIT license: Copyright (c) Luke Edwards (lukeed.com)\n */\nfunction twJoin() {\n let index = 0;\n let argument;\n let resolvedValue;\n let string = '';\n while (index < arguments.length) {\n if (argument = arguments[index++]) {\n if (resolvedValue = toValue(argument)) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n}\nconst toValue = mix => {\n if (typeof mix === 'string') {\n return mix;\n }\n let resolvedValue;\n let string = '';\n for (let k = 0; k < mix.length; k++) {\n if (mix[k]) {\n if (resolvedValue = toValue(mix[k])) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n};\nfunction createTailwindMerge(createConfigFirst, ...createConfigRest) {\n let configUtils;\n let cacheGet;\n let cacheSet;\n let functionToCall = initTailwindMerge;\n function initTailwindMerge(classList) {\n const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());\n configUtils = createConfigUtils(config);\n cacheGet = configUtils.cache.get;\n cacheSet = configUtils.cache.set;\n functionToCall = tailwindMerge;\n return tailwindMerge(classList);\n }\n function tailwindMerge(classList) {\n const cachedResult = cacheGet(classList);\n if (cachedResult) {\n return cachedResult;\n }\n const result = mergeClassList(classList, configUtils);\n cacheSet(classList, result);\n return result;\n }\n return function callTailwindMerge() {\n return functionToCall(twJoin.apply(null, arguments));\n };\n}\nconst fromTheme = key => {\n const themeGetter = theme => theme[key] || [];\n themeGetter.isThemeGetter = true;\n return themeGetter;\n};\nconst arbitraryValueRegex = /^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i;\nconst arbitraryVariableRegex = /^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i;\nconst fractionRegex = /^\\d+\\/\\d+$/;\nconst tshirtUnitRegex = /^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/;\nconst lengthUnitRegex = /\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/;\nconst colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/;\n// Shadow always begins with x and y offset separated by underscore optionally prepended by inset\nconst shadowRegex = /^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/;\nconst imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/;\nconst isFraction = value => fractionRegex.test(value);\nconst isNumber = value => !!value && !Number.isNaN(Number(value));\nconst isInteger = value => !!value && Number.isInteger(Number(value));\nconst isPercent = value => value.endsWith('%') && isNumber(value.slice(0, -1));\nconst isTshirtSize = value => tshirtUnitRegex.test(value);\nconst isAny = () => true;\nconst isLengthOnly = value =>\n// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.\n// For example, `hsl(0 0% 0%)` would be classified as a length without this check.\n// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.\nlengthUnitRegex.test(value) && !colorFunctionRegex.test(value);\nconst isNever = () => false;\nconst isShadow = value => shadowRegex.test(value);\nconst isImage = value => imageRegex.test(value);\nconst isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);\nconst isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);\nconst isArbitraryValue = value => arbitraryValueRegex.test(value);\nconst isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);\nconst isArbitraryNumber = value => getIsArbitraryValue(value, isLabelNumber, isNumber);\nconst isArbitraryPosition = value => getIsArbitraryValue(value, isLabelPosition, isNever);\nconst isArbitraryImage = value => getIsArbitraryValue(value, isLabelImage, isImage);\nconst isArbitraryShadow = value => getIsArbitraryValue(value, isLabelShadow, isShadow);\nconst isArbitraryVariable = value => arbitraryVariableRegex.test(value);\nconst isArbitraryVariableLength = value => getIsArbitraryVariable(value, isLabelLength);\nconst isArbitraryVariableFamilyName = value => getIsArbitraryVariable(value, isLabelFamilyName);\nconst isArbitraryVariablePosition = value => getIsArbitraryVariable(value, isLabelPosition);\nconst isArbitraryVariableSize = value => getIsArbitraryVariable(value, isLabelSize);\nconst isArbitraryVariableImage = value => getIsArbitraryVariable(value, isLabelImage);\nconst isArbitraryVariableShadow = value => getIsArbitraryVariable(value, isLabelShadow, true);\n// Helpers\nconst getIsArbitraryValue = (value, testLabel, testValue) => {\n const result = arbitraryValueRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return testValue(result[2]);\n }\n return false;\n};\nconst getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {\n const result = arbitraryVariableRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return shouldMatchNoLabel;\n }\n return false;\n};\n// Labels\nconst isLabelPosition = label => label === 'position' || label === 'percentage';\nconst isLabelImage = label => label === 'image' || label === 'url';\nconst isLabelSize = label => label === 'length' || label === 'size' || label === 'bg-size';\nconst isLabelLength = label => label === 'length';\nconst isLabelNumber = label => label === 'number';\nconst isLabelFamilyName = label => label === 'family-name';\nconst isLabelShadow = label => label === 'shadow';\nconst validators = /*#__PURE__*/Object.defineProperty({\n __proto__: null,\n isAny,\n isAnyNonArbitrary,\n isArbitraryImage,\n isArbitraryLength,\n isArbitraryNumber,\n isArbitraryPosition,\n isArbitraryShadow,\n isArbitrarySize,\n isArbitraryValue,\n isArbitraryVariable,\n isArbitraryVariableFamilyName,\n isArbitraryVariableImage,\n isArbitraryVariableLength,\n isArbitraryVariablePosition,\n isArbitraryVariableShadow,\n isArbitraryVariableSize,\n isFraction,\n isInteger,\n isNumber,\n isPercent,\n isTshirtSize\n}, Symbol.toStringTag, {\n value: 'Module'\n});\nconst getDefaultConfig = () => {\n /**\n * Theme getters for theme variable namespaces\n * @see https://tailwindcss.com/docs/theme#theme-variable-namespaces\n */\n /***/\n const themeColor = fromTheme('color');\n const themeFont = fromTheme('font');\n const themeText = fromTheme('text');\n const themeFontWeight = fromTheme('font-weight');\n const themeTracking = fromTheme('tracking');\n const themeLeading = fromTheme('leading');\n const themeBreakpoint = fromTheme('breakpoint');\n const themeContainer = fromTheme('container');\n const themeSpacing = fromTheme('spacing');\n const themeRadius = fromTheme('radius');\n const themeShadow = fromTheme('shadow');\n const themeInsetShadow = fromTheme('inset-shadow');\n const themeTextShadow = fromTheme('text-shadow');\n const themeDropShadow = fromTheme('drop-shadow');\n const themeBlur = fromTheme('blur');\n const themePerspective = fromTheme('perspective');\n const themeAspect = fromTheme('aspect');\n const themeEase = fromTheme('ease');\n const themeAnimate = fromTheme('animate');\n /**\n * Helpers to avoid repeating the same scales\n *\n * We use functions that create a new array every time they're called instead of static arrays.\n * This ensures that users who modify any scale by mutating the array (e.g. with `array.push(element)`) don't accidentally mutate arrays in other parts of the config.\n */\n /***/\n const scaleBreak = () => ['auto', 'avoid', 'all', 'avoid-page', 'page', 'left', 'right', 'column'];\n const scalePosition = () => ['center', 'top', 'bottom', 'left', 'right', 'top-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-top', 'top-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-top', 'bottom-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-bottom', 'bottom-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-bottom'];\n const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];\n const scaleOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'];\n const scaleOverscroll = () => ['auto', 'contain', 'none'];\n const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];\n const scaleInset = () => [isFraction, 'full', 'auto', ...scaleUnambiguousSpacing()];\n const scaleGridTemplateColsRows = () => [isInteger, 'none', 'subgrid', isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartAndEnd = () => ['auto', {\n span: ['full', isInteger, isArbitraryVariable, isArbitraryValue]\n }, isInteger, isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartOrEnd = () => [isInteger, 'auto', isArbitraryVariable, isArbitraryValue];\n const scaleGridAutoColsRows = () => ['auto', 'min', 'max', 'fr', isArbitraryVariable, isArbitraryValue];\n const scaleAlignPrimaryAxis = () => ['start', 'end', 'center', 'between', 'around', 'evenly', 'stretch', 'baseline', 'center-safe', 'end-safe'];\n const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];\n const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];\n const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];\n const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];\n const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {\n position: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleBgRepeat = () => ['no-repeat', {\n repeat: ['', 'x', 'y', 'space', 'round']\n }];\n const scaleBgSize = () => ['auto', 'cover', 'contain', isArbitraryVariableSize, isArbitrarySize, {\n size: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];\n const scaleRadius = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', 'full', themeRadius, isArbitraryVariable, isArbitraryValue];\n const scaleBorderWidth = () => ['', isNumber, isArbitraryVariableLength, isArbitraryLength];\n const scaleLineStyle = () => ['solid', 'dashed', 'dotted', 'double'];\n const scaleBlendMode = () => ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];\n const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];\n const scaleBlur = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeBlur, isArbitraryVariable, isArbitraryValue];\n const scaleRotate = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleScale = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleTranslate = () => [isFraction, 'full', ...scaleUnambiguousSpacing()];\n return {\n cacheSize: 500,\n theme: {\n animate: ['spin', 'ping', 'pulse', 'bounce'],\n aspect: ['video'],\n blur: [isTshirtSize],\n breakpoint: [isTshirtSize],\n color: [isAny],\n container: [isTshirtSize],\n 'drop-shadow': [isTshirtSize],\n ease: ['in', 'out', 'in-out'],\n font: [isAnyNonArbitrary],\n 'font-weight': ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black'],\n 'inset-shadow': [isTshirtSize],\n leading: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'],\n perspective: ['dramatic', 'near', 'normal', 'midrange', 'distant', 'none'],\n radius: [isTshirtSize],\n shadow: [isTshirtSize],\n spacing: ['px', isNumber],\n text: [isTshirtSize],\n 'text-shadow': [isTshirtSize],\n tracking: ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest']\n },\n classGroups: {\n // --------------\n // --- Layout ---\n // --------------\n /**\n * Aspect Ratio\n * @see https://tailwindcss.com/docs/aspect-ratio\n */\n aspect: [{\n aspect: ['auto', 'square', isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]\n }],\n /**\n * Container\n * @see https://tailwindcss.com/docs/container\n * @deprecated since Tailwind CSS v4.0.0\n */\n container: ['container'],\n /**\n * Columns\n * @see https://tailwindcss.com/docs/columns\n */\n columns: [{\n columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]\n }],\n /**\n * Break After\n * @see https://tailwindcss.com/docs/break-after\n */\n 'break-after': [{\n 'break-after': scaleBreak()\n }],\n /**\n * Break Before\n * @see https://tailwindcss.com/docs/break-before\n */\n 'break-before': [{\n 'break-before': scaleBreak()\n }],\n /**\n * Break Inside\n * @see https://tailwindcss.com/docs/break-inside\n */\n 'break-inside': [{\n 'break-inside': ['auto', 'avoid', 'avoid-page', 'avoid-column']\n }],\n /**\n * Box Decoration Break\n * @see https://tailwindcss.com/docs/box-decoration-break\n */\n 'box-decoration': [{\n 'box-decoration': ['slice', 'clone']\n }],\n /**\n * Box Sizing\n * @see https://tailwindcss.com/docs/box-sizing\n */\n box: [{\n box: ['border', 'content']\n }],\n /**\n * Display\n * @see https://tailwindcss.com/docs/display\n */\n display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],\n /**\n * Screen Reader Only\n * @see https://tailwindcss.com/docs/display#screen-reader-only\n */\n sr: ['sr-only', 'not-sr-only'],\n /**\n * Floats\n * @see https://tailwindcss.com/docs/float\n */\n float: [{\n float: ['right', 'left', 'none', 'start', 'end']\n }],\n /**\n * Clear\n * @see https://tailwindcss.com/docs/clear\n */\n clear: [{\n clear: ['left', 'right', 'both', 'none', 'start', 'end']\n }],\n /**\n * Isolation\n * @see https://tailwindcss.com/docs/isolation\n */\n isolation: ['isolate', 'isolation-auto'],\n /**\n * Object Fit\n * @see https://tailwindcss.com/docs/object-fit\n */\n 'object-fit': [{\n object: ['contain', 'cover', 'fill', 'none', 'scale-down']\n }],\n /**\n * Object Position\n * @see https://tailwindcss.com/docs/object-position\n */\n 'object-position': [{\n object: scalePositionWithArbitrary()\n }],\n /**\n * Overflow\n * @see https://tailwindcss.com/docs/overflow\n */\n overflow: [{\n overflow: scaleOverflow()\n }],\n /**\n * Overflow X\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-x': [{\n 'overflow-x': scaleOverflow()\n }],\n /**\n * Overflow Y\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-y': [{\n 'overflow-y': scaleOverflow()\n }],\n /**\n * Overscroll Behavior\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n overscroll: [{\n overscroll: scaleOverscroll()\n }],\n /**\n * Overscroll Behavior X\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-x': [{\n 'overscroll-x': scaleOverscroll()\n }],\n /**\n * Overscroll Behavior Y\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-y': [{\n 'overscroll-y': scaleOverscroll()\n }],\n /**\n * Position\n * @see https://tailwindcss.com/docs/position\n */\n position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],\n /**\n * Top / Right / Bottom / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n inset: [{\n inset: scaleInset()\n }],\n /**\n * Right / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-x': [{\n 'inset-x': scaleInset()\n }],\n /**\n * Top / Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-y': [{\n 'inset-y': scaleInset()\n }],\n /**\n * Start\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n start: [{\n start: scaleInset()\n }],\n /**\n * End\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n end: [{\n end: scaleInset()\n }],\n /**\n * Top\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n top: [{\n top: scaleInset()\n }],\n /**\n * Right\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n right: [{\n right: scaleInset()\n }],\n /**\n * Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n bottom: [{\n bottom: scaleInset()\n }],\n /**\n * Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n left: [{\n left: scaleInset()\n }],\n /**\n * Visibility\n * @see https://tailwindcss.com/docs/visibility\n */\n visibility: ['visible', 'invisible', 'collapse'],\n /**\n * Z-Index\n * @see https://tailwindcss.com/docs/z-index\n */\n z: [{\n z: [isInteger, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------------\n // --- Flexbox and Grid ---\n // ------------------------\n /**\n * Flex Basis\n * @see https://tailwindcss.com/docs/flex-basis\n */\n basis: [{\n basis: [isFraction, 'full', 'auto', themeContainer, ...scaleUnambiguousSpacing()]\n }],\n /**\n * Flex Direction\n * @see https://tailwindcss.com/docs/flex-direction\n */\n 'flex-direction': [{\n flex: ['row', 'row-reverse', 'col', 'col-reverse']\n }],\n /**\n * Flex Wrap\n * @see https://tailwindcss.com/docs/flex-wrap\n */\n 'flex-wrap': [{\n flex: ['nowrap', 'wrap', 'wrap-reverse']\n }],\n /**\n * Flex\n * @see https://tailwindcss.com/docs/flex\n */\n flex: [{\n flex: [isNumber, isFraction, 'auto', 'initial', 'none', isArbitraryValue]\n }],\n /**\n * Flex Grow\n * @see https://tailwindcss.com/docs/flex-grow\n */\n grow: [{\n grow: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Flex Shrink\n * @see https://tailwindcss.com/docs/flex-shrink\n */\n shrink: [{\n shrink: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Order\n * @see https://tailwindcss.com/docs/order\n */\n order: [{\n order: [isInteger, 'first', 'last', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Grid Template Columns\n * @see https://tailwindcss.com/docs/grid-template-columns\n */\n 'grid-cols': [{\n 'grid-cols': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Column Start / End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start-end': [{\n col: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Column Start\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start': [{\n 'col-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Column End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-end': [{\n 'col-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Template Rows\n * @see https://tailwindcss.com/docs/grid-template-rows\n */\n 'grid-rows': [{\n 'grid-rows': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Row Start / End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start-end': [{\n row: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Row Start\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start': [{\n 'row-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Row End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-end': [{\n 'row-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Auto Flow\n * @see https://tailwindcss.com/docs/grid-auto-flow\n */\n 'grid-flow': [{\n 'grid-flow': ['row', 'col', 'dense', 'row-dense', 'col-dense']\n }],\n /**\n * Grid Auto Columns\n * @see https://tailwindcss.com/docs/grid-auto-columns\n */\n 'auto-cols': [{\n 'auto-cols': scaleGridAutoColsRows()\n }],\n /**\n * Grid Auto Rows\n * @see https://tailwindcss.com/docs/grid-auto-rows\n */\n 'auto-rows': [{\n 'auto-rows': scaleGridAutoColsRows()\n }],\n /**\n * Gap\n * @see https://tailwindcss.com/docs/gap\n */\n gap: [{\n gap: scaleUnambiguousSpacing()\n }],\n /**\n * Gap X\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-x': [{\n 'gap-x': scaleUnambiguousSpacing()\n }],\n /**\n * Gap Y\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-y': [{\n 'gap-y': scaleUnambiguousSpacing()\n }],\n /**\n * Justify Content\n * @see https://tailwindcss.com/docs/justify-content\n */\n 'justify-content': [{\n justify: [...scaleAlignPrimaryAxis(), 'normal']\n }],\n /**\n * Justify Items\n * @see https://tailwindcss.com/docs/justify-items\n */\n 'justify-items': [{\n 'justify-items': [...scaleAlignSecondaryAxis(), 'normal']\n }],\n /**\n * Justify Self\n * @see https://tailwindcss.com/docs/justify-self\n */\n 'justify-self': [{\n 'justify-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n /**\n * Align Content\n * @see https://tailwindcss.com/docs/align-content\n */\n 'align-content': [{\n content: ['normal', ...scaleAlignPrimaryAxis()]\n }],\n /**\n * Align Items\n * @see https://tailwindcss.com/docs/align-items\n */\n 'align-items': [{\n items: [...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Align Self\n * @see https://tailwindcss.com/docs/align-self\n */\n 'align-self': [{\n self: ['auto', ...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Place Content\n * @see https://tailwindcss.com/docs/place-content\n */\n 'place-content': [{\n 'place-content': scaleAlignPrimaryAxis()\n }],\n /**\n * Place Items\n * @see https://tailwindcss.com/docs/place-items\n */\n 'place-items': [{\n 'place-items': [...scaleAlignSecondaryAxis(), 'baseline']\n }],\n /**\n * Place Self\n * @see https://tailwindcss.com/docs/place-self\n */\n 'place-self': [{\n 'place-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n // Spacing\n /**\n * Padding\n * @see https://tailwindcss.com/docs/padding\n */\n p: [{\n p: scaleUnambiguousSpacing()\n }],\n /**\n * Padding X\n * @see https://tailwindcss.com/docs/padding\n */\n px: [{\n px: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Y\n * @see https://tailwindcss.com/docs/padding\n */\n py: [{\n py: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Start\n * @see https://tailwindcss.com/docs/padding\n */\n ps: [{\n ps: scaleUnambiguousSpacing()\n }],\n /**\n * Padding End\n * @see https://tailwindcss.com/docs/padding\n */\n pe: [{\n pe: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Top\n * @see https://tailwindcss.com/docs/padding\n */\n pt: [{\n pt: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Right\n * @see https://tailwindcss.com/docs/padding\n */\n pr: [{\n pr: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Bottom\n * @see https://tailwindcss.com/docs/padding\n */\n pb: [{\n pb: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Left\n * @see https://tailwindcss.com/docs/padding\n */\n pl: [{\n pl: scaleUnambiguousSpacing()\n }],\n /**\n * Margin\n * @see https://tailwindcss.com/docs/margin\n */\n m: [{\n m: scaleMargin()\n }],\n /**\n * Margin X\n * @see https://tailwindcss.com/docs/margin\n */\n mx: [{\n mx: scaleMargin()\n }],\n /**\n * Margin Y\n * @see https://tailwindcss.com/docs/margin\n */\n my: [{\n my: scaleMargin()\n }],\n /**\n * Margin Start\n * @see https://tailwindcss.com/docs/margin\n */\n ms: [{\n ms: scaleMargin()\n }],\n /**\n * Margin End\n * @see https://tailwindcss.com/docs/margin\n */\n me: [{\n me: scaleMargin()\n }],\n /**\n * Margin Top\n * @see https://tailwindcss.com/docs/margin\n */\n mt: [{\n mt: scaleMargin()\n }],\n /**\n * Margin Right\n * @see https://tailwindcss.com/docs/margin\n */\n mr: [{\n mr: scaleMargin()\n }],\n /**\n * Margin Bottom\n * @see https://tailwindcss.com/docs/margin\n */\n mb: [{\n mb: scaleMargin()\n }],\n /**\n * Margin Left\n * @see https://tailwindcss.com/docs/margin\n */\n ml: [{\n ml: scaleMargin()\n }],\n /**\n * Space Between X\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x': [{\n 'space-x': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between X Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x-reverse': ['space-x-reverse'],\n /**\n * Space Between Y\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y': [{\n 'space-y': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between Y Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y-reverse': ['space-y-reverse'],\n // --------------\n // --- Sizing ---\n // --------------\n /**\n * Size\n * @see https://tailwindcss.com/docs/width#setting-both-width-and-height\n */\n size: [{\n size: scaleSizing()\n }],\n /**\n * Width\n * @see https://tailwindcss.com/docs/width\n */\n w: [{\n w: [themeContainer, 'screen', ...scaleSizing()]\n }],\n /**\n * Min-Width\n * @see https://tailwindcss.com/docs/min-width\n */\n 'min-w': [{\n 'min-w': [themeContainer, 'screen', /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'none', ...scaleSizing()]\n }],\n /**\n * Max-Width\n * @see https://tailwindcss.com/docs/max-width\n */\n 'max-w': [{\n 'max-w': [themeContainer, 'screen', 'none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'prose', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n {\n screen: [themeBreakpoint]\n }, ...scaleSizing()]\n }],\n /**\n * Height\n * @see https://tailwindcss.com/docs/height\n */\n h: [{\n h: ['screen', 'lh', ...scaleSizing()]\n }],\n /**\n * Min-Height\n * @see https://tailwindcss.com/docs/min-height\n */\n 'min-h': [{\n 'min-h': ['screen', 'lh', 'none', ...scaleSizing()]\n }],\n /**\n * Max-Height\n * @see https://tailwindcss.com/docs/max-height\n */\n 'max-h': [{\n 'max-h': ['screen', 'lh', ...scaleSizing()]\n }],\n // ------------------\n // --- Typography ---\n // ------------------\n /**\n * Font Size\n * @see https://tailwindcss.com/docs/font-size\n */\n 'font-size': [{\n text: ['base', themeText, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Font Smoothing\n * @see https://tailwindcss.com/docs/font-smoothing\n */\n 'font-smoothing': ['antialiased', 'subpixel-antialiased'],\n /**\n * Font Style\n * @see https://tailwindcss.com/docs/font-style\n */\n 'font-style': ['italic', 'not-italic'],\n /**\n * Font Weight\n * @see https://tailwindcss.com/docs/font-weight\n */\n 'font-weight': [{\n font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Font Stretch\n * @see https://tailwindcss.com/docs/font-stretch\n */\n 'font-stretch': [{\n 'font-stretch': ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', isPercent, isArbitraryValue]\n }],\n /**\n * Font Family\n * @see https://tailwindcss.com/docs/font-family\n */\n 'font-family': [{\n font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont]\n }],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-normal': ['normal-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-ordinal': ['ordinal'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-slashed-zero': ['slashed-zero'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-figure': ['lining-nums', 'oldstyle-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-spacing': ['proportional-nums', 'tabular-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-fraction': ['diagonal-fractions', 'stacked-fractions'],\n /**\n * Letter Spacing\n * @see https://tailwindcss.com/docs/letter-spacing\n */\n tracking: [{\n tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Line Clamp\n * @see https://tailwindcss.com/docs/line-clamp\n */\n 'line-clamp': [{\n 'line-clamp': [isNumber, 'none', isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Line Height\n * @see https://tailwindcss.com/docs/line-height\n */\n leading: [{\n leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n themeLeading, ...scaleUnambiguousSpacing()]\n }],\n /**\n * List Style Image\n * @see https://tailwindcss.com/docs/list-style-image\n */\n 'list-image': [{\n 'list-image': ['none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * List Style Position\n * @see https://tailwindcss.com/docs/list-style-position\n */\n 'list-style-position': [{\n list: ['inside', 'outside']\n }],\n /**\n * List Style Type\n * @see https://tailwindcss.com/docs/list-style-type\n */\n 'list-style-type': [{\n list: ['disc', 'decimal', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Alignment\n * @see https://tailwindcss.com/docs/text-align\n */\n 'text-alignment': [{\n text: ['left', 'center', 'right', 'justify', 'start', 'end']\n }],\n /**\n * Placeholder Color\n * @deprecated since Tailwind CSS v3.0.0\n * @see https://v3.tailwindcss.com/docs/placeholder-color\n */\n 'placeholder-color': [{\n placeholder: scaleColor()\n }],\n /**\n * Text Color\n * @see https://tailwindcss.com/docs/text-color\n */\n 'text-color': [{\n text: scaleColor()\n }],\n /**\n * Text Decoration\n * @see https://tailwindcss.com/docs/text-decoration\n */\n 'text-decoration': ['underline', 'overline', 'line-through', 'no-underline'],\n /**\n * Text Decoration Style\n * @see https://tailwindcss.com/docs/text-decoration-style\n */\n 'text-decoration-style': [{\n decoration: [...scaleLineStyle(), 'wavy']\n }],\n /**\n * Text Decoration Thickness\n * @see https://tailwindcss.com/docs/text-decoration-thickness\n */\n 'text-decoration-thickness': [{\n decoration: [isNumber, 'from-font', 'auto', isArbitraryVariable, isArbitraryLength]\n }],\n /**\n * Text Decoration Color\n * @see https://tailwindcss.com/docs/text-decoration-color\n */\n 'text-decoration-color': [{\n decoration: scaleColor()\n }],\n /**\n * Text Underline Offset\n * @see https://tailwindcss.com/docs/text-underline-offset\n */\n 'underline-offset': [{\n 'underline-offset': [isNumber, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Transform\n * @see https://tailwindcss.com/docs/text-transform\n */\n 'text-transform': ['uppercase', 'lowercase', 'capitalize', 'normal-case'],\n /**\n * Text Overflow\n * @see https://tailwindcss.com/docs/text-overflow\n */\n 'text-overflow': ['truncate', 'text-ellipsis', 'text-clip'],\n /**\n * Text Wrap\n * @see https://tailwindcss.com/docs/text-wrap\n */\n 'text-wrap': [{\n text: ['wrap', 'nowrap', 'balance', 'pretty']\n }],\n /**\n * Text Indent\n * @see https://tailwindcss.com/docs/text-indent\n */\n indent: [{\n indent: scaleUnambiguousSpacing()\n }],\n /**\n * Vertical Alignment\n * @see https://tailwindcss.com/docs/vertical-align\n */\n 'vertical-align': [{\n align: ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Whitespace\n * @see https://tailwindcss.com/docs/whitespace\n */\n whitespace: [{\n whitespace: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces']\n }],\n /**\n * Word Break\n * @see https://tailwindcss.com/docs/word-break\n */\n break: [{\n break: ['normal', 'words', 'all', 'keep']\n }],\n /**\n * Overflow Wrap\n * @see https://tailwindcss.com/docs/overflow-wrap\n */\n wrap: [{\n wrap: ['break-word', 'anywhere', 'normal']\n }],\n /**\n * Hyphens\n * @see https://tailwindcss.com/docs/hyphens\n */\n hyphens: [{\n hyphens: ['none', 'manual', 'auto']\n }],\n /**\n * Content\n * @see https://tailwindcss.com/docs/content\n */\n content: [{\n content: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // -------------------\n // --- Backgrounds ---\n // -------------------\n /**\n * Background Attachment\n * @see https://tailwindcss.com/docs/background-attachment\n */\n 'bg-attachment': [{\n bg: ['fixed', 'local', 'scroll']\n }],\n /**\n * Background Clip\n * @see https://tailwindcss.com/docs/background-clip\n */\n 'bg-clip': [{\n 'bg-clip': ['border', 'padding', 'content', 'text']\n }],\n /**\n * Background Origin\n * @see https://tailwindcss.com/docs/background-origin\n */\n 'bg-origin': [{\n 'bg-origin': ['border', 'padding', 'content']\n }],\n /**\n * Background Position\n * @see https://tailwindcss.com/docs/background-position\n */\n 'bg-position': [{\n bg: scaleBgPosition()\n }],\n /**\n * Background Repeat\n * @see https://tailwindcss.com/docs/background-repeat\n */\n 'bg-repeat': [{\n bg: scaleBgRepeat()\n }],\n /**\n * Background Size\n * @see https://tailwindcss.com/docs/background-size\n */\n 'bg-size': [{\n bg: scaleBgSize()\n }],\n /**\n * Background Image\n * @see https://tailwindcss.com/docs/background-image\n */\n 'bg-image': [{\n bg: ['none', {\n linear: [{\n to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']\n }, isInteger, isArbitraryVariable, isArbitraryValue],\n radial: ['', isArbitraryVariable, isArbitraryValue],\n conic: [isInteger, isArbitraryVariable, isArbitraryValue]\n }, isArbitraryVariableImage, isArbitraryImage]\n }],\n /**\n * Background Color\n * @see https://tailwindcss.com/docs/background-color\n */\n 'bg-color': [{\n bg: scaleColor()\n }],\n /**\n * Gradient Color Stops From Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from-pos': [{\n from: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops Via Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via-pos': [{\n via: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops To Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to-pos': [{\n to: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops From\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from': [{\n from: scaleColor()\n }],\n /**\n * Gradient Color Stops Via\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via': [{\n via: scaleColor()\n }],\n /**\n * Gradient Color Stops To\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to': [{\n to: scaleColor()\n }],\n // ---------------\n // --- Borders ---\n // ---------------\n /**\n * Border Radius\n * @see https://tailwindcss.com/docs/border-radius\n */\n rounded: [{\n rounded: scaleRadius()\n }],\n /**\n * Border Radius Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-s': [{\n 'rounded-s': scaleRadius()\n }],\n /**\n * Border Radius End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-e': [{\n 'rounded-e': scaleRadius()\n }],\n /**\n * Border Radius Top\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-t': [{\n 'rounded-t': scaleRadius()\n }],\n /**\n * Border Radius Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-r': [{\n 'rounded-r': scaleRadius()\n }],\n /**\n * Border Radius Bottom\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-b': [{\n 'rounded-b': scaleRadius()\n }],\n /**\n * Border Radius Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-l': [{\n 'rounded-l': scaleRadius()\n }],\n /**\n * Border Radius Start Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ss': [{\n 'rounded-ss': scaleRadius()\n }],\n /**\n * Border Radius Start End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-se': [{\n 'rounded-se': scaleRadius()\n }],\n /**\n * Border Radius End End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ee': [{\n 'rounded-ee': scaleRadius()\n }],\n /**\n * Border Radius End Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-es': [{\n 'rounded-es': scaleRadius()\n }],\n /**\n * Border Radius Top Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tl': [{\n 'rounded-tl': scaleRadius()\n }],\n /**\n * Border Radius Top Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tr': [{\n 'rounded-tr': scaleRadius()\n }],\n /**\n * Border Radius Bottom Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-br': [{\n 'rounded-br': scaleRadius()\n }],\n /**\n * Border Radius Bottom Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-bl': [{\n 'rounded-bl': scaleRadius()\n }],\n /**\n * Border Width\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w': [{\n border: scaleBorderWidth()\n }],\n /**\n * Border Width X\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-x': [{\n 'border-x': scaleBorderWidth()\n }],\n /**\n * Border Width Y\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-y': [{\n 'border-y': scaleBorderWidth()\n }],\n /**\n * Border Width Start\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-s': [{\n 'border-s': scaleBorderWidth()\n }],\n /**\n * Border Width End\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-e': [{\n 'border-e': scaleBorderWidth()\n }],\n /**\n * Border Width Top\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-t': [{\n 'border-t': scaleBorderWidth()\n }],\n /**\n * Border Width Right\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-r': [{\n 'border-r': scaleBorderWidth()\n }],\n /**\n * Border Width Bottom\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-b': [{\n 'border-b': scaleBorderWidth()\n }],\n /**\n * Border Width Left\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-l': [{\n 'border-l': scaleBorderWidth()\n }],\n /**\n * Divide Width X\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x': [{\n 'divide-x': scaleBorderWidth()\n }],\n /**\n * Divide Width X Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x-reverse': ['divide-x-reverse'],\n /**\n * Divide Width Y\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y': [{\n 'divide-y': scaleBorderWidth()\n }],\n /**\n * Divide Width Y Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y-reverse': ['divide-y-reverse'],\n /**\n * Border Style\n * @see https://tailwindcss.com/docs/border-style\n */\n 'border-style': [{\n border: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Divide Style\n * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style\n */\n 'divide-style': [{\n divide: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Border Color\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color': [{\n border: scaleColor()\n }],\n /**\n * Border Color X\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-x': [{\n 'border-x': scaleColor()\n }],\n /**\n * Border Color Y\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-y': [{\n 'border-y': scaleColor()\n }],\n /**\n * Border Color S\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-s': [{\n 'border-s': scaleColor()\n }],\n /**\n * Border Color E\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-e': [{\n 'border-e': scaleColor()\n }],\n /**\n * Border Color Top\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-t': [{\n 'border-t': scaleColor()\n }],\n /**\n * Border Color Right\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-r': [{\n 'border-r': scaleColor()\n }],\n /**\n * Border Color Bottom\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-b': [{\n 'border-b': scaleColor()\n }],\n /**\n * Border Color Left\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-l': [{\n 'border-l': scaleColor()\n }],\n /**\n * Divide Color\n * @see https://tailwindcss.com/docs/divide-color\n */\n 'divide-color': [{\n divide: scaleColor()\n }],\n /**\n * Outline Style\n * @see https://tailwindcss.com/docs/outline-style\n */\n 'outline-style': [{\n outline: [...scaleLineStyle(), 'none', 'hidden']\n }],\n /**\n * Outline Offset\n * @see https://tailwindcss.com/docs/outline-offset\n */\n 'outline-offset': [{\n 'outline-offset': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Outline Width\n * @see https://tailwindcss.com/docs/outline-width\n */\n 'outline-w': [{\n outline: ['', isNumber, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Outline Color\n * @see https://tailwindcss.com/docs/outline-color\n */\n 'outline-color': [{\n outline: scaleColor()\n }],\n // ---------------\n // --- Effects ---\n // ---------------\n /**\n * Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow\n */\n shadow: [{\n shadow: [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color\n */\n 'shadow-color': [{\n shadow: scaleColor()\n }],\n /**\n * Inset Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow\n */\n 'inset-shadow': [{\n 'inset-shadow': ['none', themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Inset Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color\n */\n 'inset-shadow-color': [{\n 'inset-shadow': scaleColor()\n }],\n /**\n * Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring\n */\n 'ring-w': [{\n ring: scaleBorderWidth()\n }],\n /**\n * Ring Width Inset\n * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-w-inset': ['ring-inset'],\n /**\n * Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color\n */\n 'ring-color': [{\n ring: scaleColor()\n }],\n /**\n * Ring Offset Width\n * @see https://v3.tailwindcss.com/docs/ring-offset-width\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-w': [{\n 'ring-offset': [isNumber, isArbitraryLength]\n }],\n /**\n * Ring Offset Color\n * @see https://v3.tailwindcss.com/docs/ring-offset-color\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-color': [{\n 'ring-offset': scaleColor()\n }],\n /**\n * Inset Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring\n */\n 'inset-ring-w': [{\n 'inset-ring': scaleBorderWidth()\n }],\n /**\n * Inset Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color\n */\n 'inset-ring-color': [{\n 'inset-ring': scaleColor()\n }],\n /**\n * Text Shadow\n * @see https://tailwindcss.com/docs/text-shadow\n */\n 'text-shadow': [{\n 'text-shadow': ['none', themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Text Shadow Color\n * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color\n */\n 'text-shadow-color': [{\n 'text-shadow': scaleColor()\n }],\n /**\n * Opacity\n * @see https://tailwindcss.com/docs/opacity\n */\n opacity: [{\n opacity: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Mix Blend Mode\n * @see https://tailwindcss.com/docs/mix-blend-mode\n */\n 'mix-blend': [{\n 'mix-blend': [...scaleBlendMode(), 'plus-darker', 'plus-lighter']\n }],\n /**\n * Background Blend Mode\n * @see https://tailwindcss.com/docs/background-blend-mode\n */\n 'bg-blend': [{\n 'bg-blend': scaleBlendMode()\n }],\n /**\n * Mask Clip\n * @see https://tailwindcss.com/docs/mask-clip\n */\n 'mask-clip': [{\n 'mask-clip': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }, 'mask-no-clip'],\n /**\n * Mask Composite\n * @see https://tailwindcss.com/docs/mask-composite\n */\n 'mask-composite': [{\n mask: ['add', 'subtract', 'intersect', 'exclude']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image-linear-pos': [{\n 'mask-linear': [isNumber]\n }],\n 'mask-image-linear-from-pos': [{\n 'mask-linear-from': scaleMaskImagePosition()\n }],\n 'mask-image-linear-to-pos': [{\n 'mask-linear-to': scaleMaskImagePosition()\n }],\n 'mask-image-linear-from-color': [{\n 'mask-linear-from': scaleColor()\n }],\n 'mask-image-linear-to-color': [{\n 'mask-linear-to': scaleColor()\n }],\n 'mask-image-t-from-pos': [{\n 'mask-t-from': scaleMaskImagePosition()\n }],\n 'mask-image-t-to-pos': [{\n 'mask-t-to': scaleMaskImagePosition()\n }],\n 'mask-image-t-from-color': [{\n 'mask-t-from': scaleColor()\n }],\n 'mask-image-t-to-color': [{\n 'mask-t-to': scaleColor()\n }],\n 'mask-image-r-from-pos': [{\n 'mask-r-from': scaleMaskImagePosition()\n }],\n 'mask-image-r-to-pos': [{\n 'mask-r-to': scaleMaskImagePosition()\n }],\n 'mask-image-r-from-color': [{\n 'mask-r-from': scaleColor()\n }],\n 'mask-image-r-to-color': [{\n 'mask-r-to': scaleColor()\n }],\n 'mask-image-b-from-pos': [{\n 'mask-b-from': scaleMaskImagePosition()\n }],\n 'mask-image-b-to-pos': [{\n 'mask-b-to': scaleMaskImagePosition()\n }],\n 'mask-image-b-from-color': [{\n 'mask-b-from': scaleColor()\n }],\n 'mask-image-b-to-color': [{\n 'mask-b-to': scaleColor()\n }],\n 'mask-image-l-from-pos': [{\n 'mask-l-from': scaleMaskImagePosition()\n }],\n 'mask-image-l-to-pos': [{\n 'mask-l-to': scaleMaskImagePosition()\n }],\n 'mask-image-l-from-color': [{\n 'mask-l-from': scaleColor()\n }],\n 'mask-image-l-to-color': [{\n 'mask-l-to': scaleColor()\n }],\n 'mask-image-x-from-pos': [{\n 'mask-x-from': scaleMaskImagePosition()\n }],\n 'mask-image-x-to-pos': [{\n 'mask-x-to': scaleMaskImagePosition()\n }],\n 'mask-image-x-from-color': [{\n 'mask-x-from': scaleColor()\n }],\n 'mask-image-x-to-color': [{\n 'mask-x-to': scaleColor()\n }],\n 'mask-image-y-from-pos': [{\n 'mask-y-from': scaleMaskImagePosition()\n }],\n 'mask-image-y-to-pos': [{\n 'mask-y-to': scaleMaskImagePosition()\n }],\n 'mask-image-y-from-color': [{\n 'mask-y-from': scaleColor()\n }],\n 'mask-image-y-to-color': [{\n 'mask-y-to': scaleColor()\n }],\n 'mask-image-radial': [{\n 'mask-radial': [isArbitraryVariable, isArbitraryValue]\n }],\n 'mask-image-radial-from-pos': [{\n 'mask-radial-from': scaleMaskImagePosition()\n }],\n 'mask-image-radial-to-pos': [{\n 'mask-radial-to': scaleMaskImagePosition()\n }],\n 'mask-image-radial-from-color': [{\n 'mask-radial-from': scaleColor()\n }],\n 'mask-image-radial-to-color': [{\n 'mask-radial-to': scaleColor()\n }],\n 'mask-image-radial-shape': [{\n 'mask-radial': ['circle', 'ellipse']\n }],\n 'mask-image-radial-size': [{\n 'mask-radial': [{\n closest: ['side', 'corner'],\n farthest: ['side', 'corner']\n }]\n }],\n 'mask-image-radial-pos': [{\n 'mask-radial-at': scalePosition()\n }],\n 'mask-image-conic-pos': [{\n 'mask-conic': [isNumber]\n }],\n 'mask-image-conic-from-pos': [{\n 'mask-conic-from': scaleMaskImagePosition()\n }],\n 'mask-image-conic-to-pos': [{\n 'mask-conic-to': scaleMaskImagePosition()\n }],\n 'mask-image-conic-from-color': [{\n 'mask-conic-from': scaleColor()\n }],\n 'mask-image-conic-to-color': [{\n 'mask-conic-to': scaleColor()\n }],\n /**\n * Mask Mode\n * @see https://tailwindcss.com/docs/mask-mode\n */\n 'mask-mode': [{\n mask: ['alpha', 'luminance', 'match']\n }],\n /**\n * Mask Origin\n * @see https://tailwindcss.com/docs/mask-origin\n */\n 'mask-origin': [{\n 'mask-origin': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }],\n /**\n * Mask Position\n * @see https://tailwindcss.com/docs/mask-position\n */\n 'mask-position': [{\n mask: scaleBgPosition()\n }],\n /**\n * Mask Repeat\n * @see https://tailwindcss.com/docs/mask-repeat\n */\n 'mask-repeat': [{\n mask: scaleBgRepeat()\n }],\n /**\n * Mask Size\n * @see https://tailwindcss.com/docs/mask-size\n */\n 'mask-size': [{\n mask: scaleBgSize()\n }],\n /**\n * Mask Type\n * @see https://tailwindcss.com/docs/mask-type\n */\n 'mask-type': [{\n 'mask-type': ['alpha', 'luminance']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image': [{\n mask: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // ---------------\n // --- Filters ---\n // ---------------\n /**\n * Filter\n * @see https://tailwindcss.com/docs/filter\n */\n filter: [{\n filter: [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Blur\n * @see https://tailwindcss.com/docs/blur\n */\n blur: [{\n blur: scaleBlur()\n }],\n /**\n * Brightness\n * @see https://tailwindcss.com/docs/brightness\n */\n brightness: [{\n brightness: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Contrast\n * @see https://tailwindcss.com/docs/contrast\n */\n contrast: [{\n contrast: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Drop Shadow\n * @see https://tailwindcss.com/docs/drop-shadow\n */\n 'drop-shadow': [{\n 'drop-shadow': [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeDropShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Drop Shadow Color\n * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color\n */\n 'drop-shadow-color': [{\n 'drop-shadow': scaleColor()\n }],\n /**\n * Grayscale\n * @see https://tailwindcss.com/docs/grayscale\n */\n grayscale: [{\n grayscale: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Hue Rotate\n * @see https://tailwindcss.com/docs/hue-rotate\n */\n 'hue-rotate': [{\n 'hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Invert\n * @see https://tailwindcss.com/docs/invert\n */\n invert: [{\n invert: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Saturate\n * @see https://tailwindcss.com/docs/saturate\n */\n saturate: [{\n saturate: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Sepia\n * @see https://tailwindcss.com/docs/sepia\n */\n sepia: [{\n sepia: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Filter\n * @see https://tailwindcss.com/docs/backdrop-filter\n */\n 'backdrop-filter': [{\n 'backdrop-filter': [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Blur\n * @see https://tailwindcss.com/docs/backdrop-blur\n */\n 'backdrop-blur': [{\n 'backdrop-blur': scaleBlur()\n }],\n /**\n * Backdrop Brightness\n * @see https://tailwindcss.com/docs/backdrop-brightness\n */\n 'backdrop-brightness': [{\n 'backdrop-brightness': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Contrast\n * @see https://tailwindcss.com/docs/backdrop-contrast\n */\n 'backdrop-contrast': [{\n 'backdrop-contrast': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Grayscale\n * @see https://tailwindcss.com/docs/backdrop-grayscale\n */\n 'backdrop-grayscale': [{\n 'backdrop-grayscale': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Hue Rotate\n * @see https://tailwindcss.com/docs/backdrop-hue-rotate\n */\n 'backdrop-hue-rotate': [{\n 'backdrop-hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Invert\n * @see https://tailwindcss.com/docs/backdrop-invert\n */\n 'backdrop-invert': [{\n 'backdrop-invert': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Opacity\n * @see https://tailwindcss.com/docs/backdrop-opacity\n */\n 'backdrop-opacity': [{\n 'backdrop-opacity': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Saturate\n * @see https://tailwindcss.com/docs/backdrop-saturate\n */\n 'backdrop-saturate': [{\n 'backdrop-saturate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Sepia\n * @see https://tailwindcss.com/docs/backdrop-sepia\n */\n 'backdrop-sepia': [{\n 'backdrop-sepia': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n // --------------\n // --- Tables ---\n // --------------\n /**\n * Border Collapse\n * @see https://tailwindcss.com/docs/border-collapse\n */\n 'border-collapse': [{\n border: ['collapse', 'separate']\n }],\n /**\n * Border Spacing\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing': [{\n 'border-spacing': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing X\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-x': [{\n 'border-spacing-x': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing Y\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-y': [{\n 'border-spacing-y': scaleUnambiguousSpacing()\n }],\n /**\n * Table Layout\n * @see https://tailwindcss.com/docs/table-layout\n */\n 'table-layout': [{\n table: ['auto', 'fixed']\n }],\n /**\n * Caption Side\n * @see https://tailwindcss.com/docs/caption-side\n */\n caption: [{\n caption: ['top', 'bottom']\n }],\n // ---------------------------------\n // --- Transitions and Animation ---\n // ---------------------------------\n /**\n * Transition Property\n * @see https://tailwindcss.com/docs/transition-property\n */\n transition: [{\n transition: ['', 'all', 'colors', 'opacity', 'shadow', 'transform', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Behavior\n * @see https://tailwindcss.com/docs/transition-behavior\n */\n 'transition-behavior': [{\n transition: ['normal', 'discrete']\n }],\n /**\n * Transition Duration\n * @see https://tailwindcss.com/docs/transition-duration\n */\n duration: [{\n duration: [isNumber, 'initial', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Timing Function\n * @see https://tailwindcss.com/docs/transition-timing-function\n */\n ease: [{\n ease: ['linear', 'initial', themeEase, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Delay\n * @see https://tailwindcss.com/docs/transition-delay\n */\n delay: [{\n delay: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Animation\n * @see https://tailwindcss.com/docs/animation\n */\n animate: [{\n animate: ['none', themeAnimate, isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------\n // --- Transforms ---\n // ------------------\n /**\n * Backface Visibility\n * @see https://tailwindcss.com/docs/backface-visibility\n */\n backface: [{\n backface: ['hidden', 'visible']\n }],\n /**\n * Perspective\n * @see https://tailwindcss.com/docs/perspective\n */\n perspective: [{\n perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Perspective Origin\n * @see https://tailwindcss.com/docs/perspective-origin\n */\n 'perspective-origin': [{\n 'perspective-origin': scalePositionWithArbitrary()\n }],\n /**\n * Rotate\n * @see https://tailwindcss.com/docs/rotate\n */\n rotate: [{\n rotate: scaleRotate()\n }],\n /**\n * Rotate X\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-x': [{\n 'rotate-x': scaleRotate()\n }],\n /**\n * Rotate Y\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-y': [{\n 'rotate-y': scaleRotate()\n }],\n /**\n * Rotate Z\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-z': [{\n 'rotate-z': scaleRotate()\n }],\n /**\n * Scale\n * @see https://tailwindcss.com/docs/scale\n */\n scale: [{\n scale: scaleScale()\n }],\n /**\n * Scale X\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-x': [{\n 'scale-x': scaleScale()\n }],\n /**\n * Scale Y\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-y': [{\n 'scale-y': scaleScale()\n }],\n /**\n * Scale Z\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-z': [{\n 'scale-z': scaleScale()\n }],\n /**\n * Scale 3D\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-3d': ['scale-3d'],\n /**\n * Skew\n * @see https://tailwindcss.com/docs/skew\n */\n skew: [{\n skew: scaleSkew()\n }],\n /**\n * Skew X\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-x': [{\n 'skew-x': scaleSkew()\n }],\n /**\n * Skew Y\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-y': [{\n 'skew-y': scaleSkew()\n }],\n /**\n * Transform\n * @see https://tailwindcss.com/docs/transform\n */\n transform: [{\n transform: [isArbitraryVariable, isArbitraryValue, '', 'none', 'gpu', 'cpu']\n }],\n /**\n * Transform Origin\n * @see https://tailwindcss.com/docs/transform-origin\n */\n 'transform-origin': [{\n origin: scalePositionWithArbitrary()\n }],\n /**\n * Transform Style\n * @see https://tailwindcss.com/docs/transform-style\n */\n 'transform-style': [{\n transform: ['3d', 'flat']\n }],\n /**\n * Translate\n * @see https://tailwindcss.com/docs/translate\n */\n translate: [{\n translate: scaleTranslate()\n }],\n /**\n * Translate X\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-x': [{\n 'translate-x': scaleTranslate()\n }],\n /**\n * Translate Y\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-y': [{\n 'translate-y': scaleTranslate()\n }],\n /**\n * Translate Z\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-z': [{\n 'translate-z': scaleTranslate()\n }],\n /**\n * Translate None\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-none': ['translate-none'],\n // ---------------------\n // --- Interactivity ---\n // ---------------------\n /**\n * Accent Color\n * @see https://tailwindcss.com/docs/accent-color\n */\n accent: [{\n accent: scaleColor()\n }],\n /**\n * Appearance\n * @see https://tailwindcss.com/docs/appearance\n */\n appearance: [{\n appearance: ['none', 'auto']\n }],\n /**\n * Caret Color\n * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities\n */\n 'caret-color': [{\n caret: scaleColor()\n }],\n /**\n * Color Scheme\n * @see https://tailwindcss.com/docs/color-scheme\n */\n 'color-scheme': [{\n scheme: ['normal', 'dark', 'light', 'light-dark', 'only-dark', 'only-light']\n }],\n /**\n * Cursor\n * @see https://tailwindcss.com/docs/cursor\n */\n cursor: [{\n cursor: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'help', 'not-allowed', 'none', 'context-menu', 'progress', 'cell', 'crosshair', 'vertical-text', 'alias', 'copy', 'no-drop', 'grab', 'grabbing', 'all-scroll', 'col-resize', 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Field Sizing\n * @see https://tailwindcss.com/docs/field-sizing\n */\n 'field-sizing': [{\n 'field-sizing': ['fixed', 'content']\n }],\n /**\n * Pointer Events\n * @see https://tailwindcss.com/docs/pointer-events\n */\n 'pointer-events': [{\n 'pointer-events': ['auto', 'none']\n }],\n /**\n * Resize\n * @see https://tailwindcss.com/docs/resize\n */\n resize: [{\n resize: ['none', '', 'y', 'x']\n }],\n /**\n * Scroll Behavior\n * @see https://tailwindcss.com/docs/scroll-behavior\n */\n 'scroll-behavior': [{\n scroll: ['auto', 'smooth']\n }],\n /**\n * Scroll Margin\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-m': [{\n 'scroll-m': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin X\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mx': [{\n 'scroll-mx': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Y\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-my': [{\n 'scroll-my': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Start\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ms': [{\n 'scroll-ms': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin End\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-me': [{\n 'scroll-me': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Top\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mt': [{\n 'scroll-mt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Right\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mr': [{\n 'scroll-mr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Bottom\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mb': [{\n 'scroll-mb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Left\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ml': [{\n 'scroll-ml': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-p': [{\n 'scroll-p': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding X\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-px': [{\n 'scroll-px': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Y\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-py': [{\n 'scroll-py': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Start\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-ps': [{\n 'scroll-ps': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding End\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pe': [{\n 'scroll-pe': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Top\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pt': [{\n 'scroll-pt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Right\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pr': [{\n 'scroll-pr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Bottom\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pb': [{\n 'scroll-pb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Left\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pl': [{\n 'scroll-pl': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Snap Align\n * @see https://tailwindcss.com/docs/scroll-snap-align\n */\n 'snap-align': [{\n snap: ['start', 'end', 'center', 'align-none']\n }],\n /**\n * Scroll Snap Stop\n * @see https://tailwindcss.com/docs/scroll-snap-stop\n */\n 'snap-stop': [{\n snap: ['normal', 'always']\n }],\n /**\n * Scroll Snap Type\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-type': [{\n snap: ['none', 'x', 'y', 'both']\n }],\n /**\n * Scroll Snap Type Strictness\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-strictness': [{\n snap: ['mandatory', 'proximity']\n }],\n /**\n * Touch Action\n * @see https://tailwindcss.com/docs/touch-action\n */\n touch: [{\n touch: ['auto', 'none', 'manipulation']\n }],\n /**\n * Touch Action X\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-x': [{\n 'touch-pan': ['x', 'left', 'right']\n }],\n /**\n * Touch Action Y\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-y': [{\n 'touch-pan': ['y', 'up', 'down']\n }],\n /**\n * Touch Action Pinch Zoom\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-pz': ['touch-pinch-zoom'],\n /**\n * User Select\n * @see https://tailwindcss.com/docs/user-select\n */\n select: [{\n select: ['none', 'text', 'all', 'auto']\n }],\n /**\n * Will Change\n * @see https://tailwindcss.com/docs/will-change\n */\n 'will-change': [{\n 'will-change': ['auto', 'scroll', 'contents', 'transform', isArbitraryVariable, isArbitraryValue]\n }],\n // -----------\n // --- SVG ---\n // -----------\n /**\n * Fill\n * @see https://tailwindcss.com/docs/fill\n */\n fill: [{\n fill: ['none', ...scaleColor()]\n }],\n /**\n * Stroke Width\n * @see https://tailwindcss.com/docs/stroke-width\n */\n 'stroke-w': [{\n stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]\n }],\n /**\n * Stroke\n * @see https://tailwindcss.com/docs/stroke\n */\n stroke: [{\n stroke: ['none', ...scaleColor()]\n }],\n // ---------------------\n // --- Accessibility ---\n // ---------------------\n /**\n * Forced Color Adjust\n * @see https://tailwindcss.com/docs/forced-color-adjust\n */\n 'forced-color-adjust': [{\n 'forced-color-adjust': ['auto', 'none']\n }]\n },\n conflictingClassGroups: {\n overflow: ['overflow-x', 'overflow-y'],\n overscroll: ['overscroll-x', 'overscroll-y'],\n inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],\n 'inset-x': ['right', 'left'],\n 'inset-y': ['top', 'bottom'],\n flex: ['basis', 'grow', 'shrink'],\n gap: ['gap-x', 'gap-y'],\n p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],\n px: ['pr', 'pl'],\n py: ['pt', 'pb'],\n m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],\n mx: ['mr', 'ml'],\n my: ['mt', 'mb'],\n size: ['w', 'h'],\n 'font-size': ['leading'],\n 'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],\n 'fvn-ordinal': ['fvn-normal'],\n 'fvn-slashed-zero': ['fvn-normal'],\n 'fvn-figure': ['fvn-normal'],\n 'fvn-spacing': ['fvn-normal'],\n 'fvn-fraction': ['fvn-normal'],\n 'line-clamp': ['display', 'overflow'],\n rounded: ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],\n 'rounded-s': ['rounded-ss', 'rounded-es'],\n 'rounded-e': ['rounded-se', 'rounded-ee'],\n 'rounded-t': ['rounded-tl', 'rounded-tr'],\n 'rounded-r': ['rounded-tr', 'rounded-br'],\n 'rounded-b': ['rounded-br', 'rounded-bl'],\n 'rounded-l': ['rounded-tl', 'rounded-bl'],\n 'border-spacing': ['border-spacing-x', 'border-spacing-y'],\n 'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],\n 'border-w-x': ['border-w-r', 'border-w-l'],\n 'border-w-y': ['border-w-t', 'border-w-b'],\n 'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],\n 'border-color-x': ['border-color-r', 'border-color-l'],\n 'border-color-y': ['border-color-t', 'border-color-b'],\n translate: ['translate-x', 'translate-y', 'translate-none'],\n 'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],\n 'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],\n 'scroll-mx': ['scroll-mr', 'scroll-ml'],\n 'scroll-my': ['scroll-mt', 'scroll-mb'],\n 'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],\n 'scroll-px': ['scroll-pr', 'scroll-pl'],\n 'scroll-py': ['scroll-pt', 'scroll-pb'],\n touch: ['touch-x', 'touch-y', 'touch-pz'],\n 'touch-x': ['touch'],\n 'touch-y': ['touch'],\n 'touch-pz': ['touch']\n },\n conflictingClassGroupModifiers: {\n 'font-size': ['leading']\n },\n orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']\n };\n};\n\n/**\n * @param baseConfig Config where other config will be merged into. This object will be mutated.\n * @param configExtension Partial config to merge into the `baseConfig`.\n */\nconst mergeConfigs = (baseConfig, {\n cacheSize,\n prefix,\n experimentalParseClassName,\n extend = {},\n override = {}\n}) => {\n overrideProperty(baseConfig, 'cacheSize', cacheSize);\n overrideProperty(baseConfig, 'prefix', prefix);\n overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName);\n overrideConfigProperties(baseConfig.theme, override.theme);\n overrideConfigProperties(baseConfig.classGroups, override.classGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroupModifiers, override.conflictingClassGroupModifiers);\n overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers);\n mergeConfigProperties(baseConfig.theme, extend.theme);\n mergeConfigProperties(baseConfig.classGroups, extend.classGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroupModifiers, extend.conflictingClassGroupModifiers);\n mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers');\n return baseConfig;\n};\nconst overrideProperty = (baseObject, overrideKey, overrideValue) => {\n if (overrideValue !== undefined) {\n baseObject[overrideKey] = overrideValue;\n }\n};\nconst overrideConfigProperties = (baseObject, overrideObject) => {\n if (overrideObject) {\n for (const key in overrideObject) {\n overrideProperty(baseObject, key, overrideObject[key]);\n }\n }\n};\nconst mergeConfigProperties = (baseObject, mergeObject) => {\n if (mergeObject) {\n for (const key in mergeObject) {\n mergeArrayProperties(baseObject, mergeObject, key);\n }\n }\n};\nconst mergeArrayProperties = (baseObject, mergeObject, key) => {\n const mergeValue = mergeObject[key];\n if (mergeValue !== undefined) {\n baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue;\n }\n};\nconst extendTailwindMerge = (configExtension, ...createConfig) => typeof configExtension === 'function' ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig) : createTailwindMerge(() => mergeConfigs(getDefaultConfig(), configExtension), ...createConfig);\nconst twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);\nexport { createTailwindMerge, extendTailwindMerge, fromTheme, getDefaultConfig, mergeConfigs, twJoin, twMerge, validators };\n//# sourceMappingURL=bundle-mjs.mjs.map\n","import { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nexport const cn = (...inputs) => twMerge(clsx(inputs));\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","/**\n * @license React\n * scheduler.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nfunction push(heap, node) {\n var index = heap.length;\n heap.push(node);\n a: for (; 0 < index; ) {\n var parentIndex = (index - 1) >>> 1,\n parent = heap[parentIndex];\n if (0 < compare(parent, node))\n (heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);\n else break a;\n }\n}\nfunction peek(heap) {\n return 0 === heap.length ? null : heap[0];\n}\nfunction pop(heap) {\n if (0 === heap.length) return null;\n var first = heap[0],\n last = heap.pop();\n if (last !== first) {\n heap[0] = last;\n a: for (\n var index = 0, length = heap.length, halfLength = length >>> 1;\n index < halfLength;\n\n ) {\n var leftIndex = 2 * (index + 1) - 1,\n left = heap[leftIndex],\n rightIndex = leftIndex + 1,\n right = heap[rightIndex];\n if (0 > compare(left, last))\n rightIndex < length && 0 > compare(right, left)\n ? ((heap[index] = right),\n (heap[rightIndex] = last),\n (index = rightIndex))\n : ((heap[index] = left),\n (heap[leftIndex] = last),\n (index = leftIndex));\n else if (rightIndex < length && 0 > compare(right, last))\n (heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);\n else break a;\n }\n }\n return first;\n}\nfunction compare(a, b) {\n var diff = a.sortIndex - b.sortIndex;\n return 0 !== diff ? diff : a.id - b.id;\n}\nexports.unstable_now = void 0;\nif (\"object\" === typeof performance && \"function\" === typeof performance.now) {\n var localPerformance = performance;\n exports.unstable_now = function () {\n return localPerformance.now();\n };\n} else {\n var localDate = Date,\n initialTime = localDate.now();\n exports.unstable_now = function () {\n return localDate.now() - initialTime;\n };\n}\nvar taskQueue = [],\n timerQueue = [],\n taskIdCounter = 1,\n currentTask = null,\n currentPriorityLevel = 3,\n isPerformingWork = !1,\n isHostCallbackScheduled = !1,\n isHostTimeoutScheduled = !1,\n needsPaint = !1,\n localSetTimeout = \"function\" === typeof setTimeout ? setTimeout : null,\n localClearTimeout = \"function\" === typeof clearTimeout ? clearTimeout : null,\n localSetImmediate = \"undefined\" !== typeof setImmediate ? setImmediate : null;\nfunction advanceTimers(currentTime) {\n for (var timer = peek(timerQueue); null !== timer; ) {\n if (null === timer.callback) pop(timerQueue);\n else if (timer.startTime <= currentTime)\n pop(timerQueue),\n (timer.sortIndex = timer.expirationTime),\n push(taskQueue, timer);\n else break;\n timer = peek(timerQueue);\n }\n}\nfunction handleTimeout(currentTime) {\n isHostTimeoutScheduled = !1;\n advanceTimers(currentTime);\n if (!isHostCallbackScheduled)\n if (null !== peek(taskQueue))\n (isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);\n }\n}\nvar isMessageLoopRunning = !1,\n taskTimeoutID = -1,\n frameInterval = 5,\n startTime = -1;\nfunction shouldYieldToHost() {\n return needsPaint\n ? !0\n : exports.unstable_now() - startTime < frameInterval\n ? !1\n : !0;\n}\nfunction performWorkUntilDeadline() {\n needsPaint = !1;\n if (isMessageLoopRunning) {\n var currentTime = exports.unstable_now();\n startTime = currentTime;\n var hasMoreWork = !0;\n try {\n a: {\n isHostCallbackScheduled = !1;\n isHostTimeoutScheduled &&\n ((isHostTimeoutScheduled = !1),\n localClearTimeout(taskTimeoutID),\n (taskTimeoutID = -1));\n isPerformingWork = !0;\n var previousPriorityLevel = currentPriorityLevel;\n try {\n b: {\n advanceTimers(currentTime);\n for (\n currentTask = peek(taskQueue);\n null !== currentTask &&\n !(\n currentTask.expirationTime > currentTime && shouldYieldToHost()\n );\n\n ) {\n var callback = currentTask.callback;\n if (\"function\" === typeof callback) {\n currentTask.callback = null;\n currentPriorityLevel = currentTask.priorityLevel;\n var continuationCallback = callback(\n currentTask.expirationTime <= currentTime\n );\n currentTime = exports.unstable_now();\n if (\"function\" === typeof continuationCallback) {\n currentTask.callback = continuationCallback;\n advanceTimers(currentTime);\n hasMoreWork = !0;\n break b;\n }\n currentTask === peek(taskQueue) && pop(taskQueue);\n advanceTimers(currentTime);\n } else pop(taskQueue);\n currentTask = peek(taskQueue);\n }\n if (null !== currentTask) hasMoreWork = !0;\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(\n handleTimeout,\n firstTimer.startTime - currentTime\n );\n hasMoreWork = !1;\n }\n }\n break a;\n } finally {\n (currentTask = null),\n (currentPriorityLevel = previousPriorityLevel),\n (isPerformingWork = !1);\n }\n hasMoreWork = void 0;\n }\n } finally {\n hasMoreWork\n ? schedulePerformWorkUntilDeadline()\n : (isMessageLoopRunning = !1);\n }\n }\n}\nvar schedulePerformWorkUntilDeadline;\nif (\"function\" === typeof localSetImmediate)\n schedulePerformWorkUntilDeadline = function () {\n localSetImmediate(performWorkUntilDeadline);\n };\nelse if (\"undefined\" !== typeof MessageChannel) {\n var channel = new MessageChannel(),\n port = channel.port2;\n channel.port1.onmessage = performWorkUntilDeadline;\n schedulePerformWorkUntilDeadline = function () {\n port.postMessage(null);\n };\n} else\n schedulePerformWorkUntilDeadline = function () {\n localSetTimeout(performWorkUntilDeadline, 0);\n };\nfunction requestHostTimeout(callback, ms) {\n taskTimeoutID = localSetTimeout(function () {\n callback(exports.unstable_now());\n }, ms);\n}\nexports.unstable_IdlePriority = 5;\nexports.unstable_ImmediatePriority = 1;\nexports.unstable_LowPriority = 4;\nexports.unstable_NormalPriority = 3;\nexports.unstable_Profiling = null;\nexports.unstable_UserBlockingPriority = 2;\nexports.unstable_cancelCallback = function (task) {\n task.callback = null;\n};\nexports.unstable_forceFrameRate = function (fps) {\n 0 > fps || 125 < fps\n ? console.error(\n \"forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported\"\n )\n : (frameInterval = 0 < fps ? Math.floor(1e3 / fps) : 5);\n};\nexports.unstable_getCurrentPriorityLevel = function () {\n return currentPriorityLevel;\n};\nexports.unstable_next = function (eventHandler) {\n switch (currentPriorityLevel) {\n case 1:\n case 2:\n case 3:\n var priorityLevel = 3;\n break;\n default:\n priorityLevel = currentPriorityLevel;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_requestPaint = function () {\n needsPaint = !0;\n};\nexports.unstable_runWithPriority = function (priorityLevel, eventHandler) {\n switch (priorityLevel) {\n case 1:\n case 2:\n case 3:\n case 4:\n case 5:\n break;\n default:\n priorityLevel = 3;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_scheduleCallback = function (\n priorityLevel,\n callback,\n options\n) {\n var currentTime = exports.unstable_now();\n \"object\" === typeof options && null !== options\n ? ((options = options.delay),\n (options =\n \"number\" === typeof options && 0 < options\n ? currentTime + options\n : currentTime))\n : (options = currentTime);\n switch (priorityLevel) {\n case 1:\n var timeout = -1;\n break;\n case 2:\n timeout = 250;\n break;\n case 5:\n timeout = 1073741823;\n break;\n case 4:\n timeout = 1e4;\n break;\n default:\n timeout = 5e3;\n }\n timeout = options + timeout;\n priorityLevel = {\n id: taskIdCounter++,\n callback: callback,\n priorityLevel: priorityLevel,\n startTime: options,\n expirationTime: timeout,\n sortIndex: -1\n };\n options > currentTime\n ? ((priorityLevel.sortIndex = options),\n push(timerQueue, priorityLevel),\n null === peek(taskQueue) &&\n priorityLevel === peek(timerQueue) &&\n (isHostTimeoutScheduled\n ? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))\n : (isHostTimeoutScheduled = !0),\n requestHostTimeout(handleTimeout, options - currentTime)))\n : ((priorityLevel.sortIndex = timeout),\n push(taskQueue, priorityLevel),\n isHostCallbackScheduled ||\n isPerformingWork ||\n ((isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));\n return priorityLevel;\n};\nexports.unstable_shouldYield = shouldYieldToHost;\nexports.unstable_wrapCallback = function (callback) {\n var parentPriorityLevel = currentPriorityLevel;\n return function () {\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = parentPriorityLevel;\n try {\n return callback.apply(this, arguments);\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n };\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction noop() {}\nvar Internals = {\n d: {\n f: noop,\n r: function () {\n throw Error(formatProdErrorMessage(522));\n },\n D: noop,\n C: noop,\n L: noop,\n m: noop,\n X: noop,\n S: noop,\n M: noop\n },\n p: 0,\n findDOMNode: null\n },\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\");\nfunction createPortal$1(children, containerInfo, implementation) {\n var key =\n 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;\n return {\n $$typeof: REACT_PORTAL_TYPE,\n key: null == key ? null : \"\" + key,\n children: children,\n containerInfo: containerInfo,\n implementation: implementation\n };\n}\nvar ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\nfunction getCrossOriginStringAs(as, input) {\n if (\"font\" === as) return \"\";\n if (\"string\" === typeof input)\n return \"use-credentials\" === input ? input : \"\";\n}\nexports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n Internals;\nexports.createPortal = function (children, container) {\n var key =\n 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;\n if (\n !container ||\n (1 !== container.nodeType &&\n 9 !== container.nodeType &&\n 11 !== container.nodeType)\n )\n throw Error(formatProdErrorMessage(299));\n return createPortal$1(children, container, null, key);\n};\nexports.flushSync = function (fn) {\n var previousTransition = ReactSharedInternals.T,\n previousUpdatePriority = Internals.p;\n try {\n if (((ReactSharedInternals.T = null), (Internals.p = 2), fn)) return fn();\n } finally {\n (ReactSharedInternals.T = previousTransition),\n (Internals.p = previousUpdatePriority),\n Internals.d.f();\n }\n};\nexports.preconnect = function (href, options) {\n \"string\" === typeof href &&\n (options\n ? ((options = options.crossOrigin),\n (options =\n \"string\" === typeof options\n ? \"use-credentials\" === options\n ? options\n : \"\"\n : void 0))\n : (options = null),\n Internals.d.C(href, options));\n};\nexports.prefetchDNS = function (href) {\n \"string\" === typeof href && Internals.d.D(href);\n};\nexports.preinit = function (href, options) {\n if (\"string\" === typeof href && options && \"string\" === typeof options.as) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin),\n integrity =\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n fetchPriority =\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0;\n \"style\" === as\n ? Internals.d.S(\n href,\n \"string\" === typeof options.precedence ? options.precedence : void 0,\n {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority\n }\n )\n : \"script\" === as &&\n Internals.d.X(href, {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n};\nexports.preinitModule = function (href, options) {\n if (\"string\" === typeof href)\n if (\"object\" === typeof options && null !== options) {\n if (null == options.as || \"script\" === options.as) {\n var crossOrigin = getCrossOriginStringAs(\n options.as,\n options.crossOrigin\n );\n Internals.d.M(href, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n } else null == options && Internals.d.M(href);\n};\nexports.preload = function (href, options) {\n if (\n \"string\" === typeof href &&\n \"object\" === typeof options &&\n null !== options &&\n \"string\" === typeof options.as\n ) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);\n Internals.d.L(href, as, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0,\n type: \"string\" === typeof options.type ? options.type : void 0,\n fetchPriority:\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0,\n referrerPolicy:\n \"string\" === typeof options.referrerPolicy\n ? options.referrerPolicy\n : void 0,\n imageSrcSet:\n \"string\" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,\n imageSizes:\n \"string\" === typeof options.imageSizes ? options.imageSizes : void 0,\n media: \"string\" === typeof options.media ? options.media : void 0\n });\n }\n};\nexports.preloadModule = function (href, options) {\n if (\"string\" === typeof href)\n if (options) {\n var crossOrigin = getCrossOriginStringAs(options.as, options.crossOrigin);\n Internals.d.m(href, {\n as:\n \"string\" === typeof options.as && \"script\" !== options.as\n ? options.as\n : void 0,\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0\n });\n } else Internals.d.m(href);\n};\nexports.requestFormReset = function (form) {\n Internals.d.r(form);\n};\nexports.unstable_batchedUpdates = function (fn, a) {\n return fn(a);\n};\nexports.useFormState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useFormState(action, initialState, permalink);\n};\nexports.useFormStatus = function () {\n return ReactSharedInternals.H.useHostTransitionStatus();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nfunction checkDCE() {\n /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\n if (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' ||\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function'\n ) {\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // This branch is unreachable because this function is only called\n // in production, but the condition is true only in development.\n // Therefore if the branch is still here, dead code elimination wasn't\n // properly applied.\n // Don't change the message. React DevTools relies on it. Also make sure\n // this message doesn't occur elsewhere in this function, or it will cause\n // a false positive.\n throw new Error('^_^');\n }\n try {\n // Verify that the code above has been dead code eliminated (DCE'd).\n __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);\n } catch (err) {\n // DevTools shouldn't crash React, no matter what.\n // We should still report in case we break this code.\n console.error(err);\n }\n}\n\nif (process.env.NODE_ENV === 'production') {\n // DCE check should happen before ReactDOM bundle executes so that\n // DevTools can report bad minification during injection.\n checkDCE();\n module.exports = require('./cjs/react-dom.production.js');\n} else {\n module.exports = require('./cjs/react-dom.development.js');\n}\n","/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n\"use strict\";\nvar Scheduler = require(\"scheduler\"),\n React = require(\"react\"),\n ReactDOM = require(\"react-dom\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction isValidContainer(node) {\n return !(\n !node ||\n (1 !== node.nodeType && 9 !== node.nodeType && 11 !== node.nodeType)\n );\n}\nfunction getNearestMountedFiber(fiber) {\n var node = fiber,\n nearestMounted = fiber;\n if (fiber.alternate) for (; node.return; ) node = node.return;\n else {\n fiber = node;\n do\n (node = fiber),\n 0 !== (node.flags & 4098) && (nearestMounted = node.return),\n (fiber = node.return);\n while (fiber);\n }\n return 3 === node.tag ? nearestMounted : null;\n}\nfunction getSuspenseInstanceFromFiber(fiber) {\n if (13 === fiber.tag) {\n var suspenseState = fiber.memoizedState;\n null === suspenseState &&\n ((fiber = fiber.alternate),\n null !== fiber && (suspenseState = fiber.memoizedState));\n if (null !== suspenseState) return suspenseState.dehydrated;\n }\n return null;\n}\nfunction getActivityInstanceFromFiber(fiber) {\n if (31 === fiber.tag) {\n var activityState = fiber.memoizedState;\n null === activityState &&\n ((fiber = fiber.alternate),\n null !== fiber && (activityState = fiber.memoizedState));\n if (null !== activityState) return activityState.dehydrated;\n }\n return null;\n}\nfunction assertIsMounted(fiber) {\n if (getNearestMountedFiber(fiber) !== fiber)\n throw Error(formatProdErrorMessage(188));\n}\nfunction findCurrentFiberUsingSlowPath(fiber) {\n var alternate = fiber.alternate;\n if (!alternate) {\n alternate = getNearestMountedFiber(fiber);\n if (null === alternate) throw Error(formatProdErrorMessage(188));\n return alternate !== fiber ? null : fiber;\n }\n for (var a = fiber, b = alternate; ; ) {\n var parentA = a.return;\n if (null === parentA) break;\n var parentB = parentA.alternate;\n if (null === parentB) {\n b = parentA.return;\n if (null !== b) {\n a = b;\n continue;\n }\n break;\n }\n if (parentA.child === parentB.child) {\n for (parentB = parentA.child; parentB; ) {\n if (parentB === a) return assertIsMounted(parentA), fiber;\n if (parentB === b) return assertIsMounted(parentA), alternate;\n parentB = parentB.sibling;\n }\n throw Error(formatProdErrorMessage(188));\n }\n if (a.return !== b.return) (a = parentA), (b = parentB);\n else {\n for (var didFindChild = !1, child$0 = parentA.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentA;\n b = parentB;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentA;\n a = parentB;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) {\n for (child$0 = parentB.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentB;\n b = parentA;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentB;\n a = parentA;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) throw Error(formatProdErrorMessage(189));\n }\n }\n if (a.alternate !== b) throw Error(formatProdErrorMessage(190));\n }\n if (3 !== a.tag) throw Error(formatProdErrorMessage(188));\n return a.stateNode.current === a ? fiber : alternate;\n}\nfunction findCurrentHostFiberImpl(node) {\n var tag = node.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return node;\n for (node = node.child; null !== node; ) {\n tag = findCurrentHostFiberImpl(node);\n if (null !== tag) return tag;\n node = node.sibling;\n }\n return null;\n}\nvar assign = Object.assign,\n REACT_LEGACY_ELEMENT_TYPE = Symbol.for(\"react.element\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\nSymbol.for(\"react.scope\");\nvar REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\");\nSymbol.for(\"react.legacy_hidden\");\nSymbol.for(\"react.tracing_marker\");\nvar REACT_MEMO_CACHE_SENTINEL = Symbol.for(\"react.memo_cache_sentinel\");\nSymbol.for(\"react.view_transition\");\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\");\nfunction getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (type.$$typeof) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n}\nvar isArrayImpl = Array.isArray,\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n ReactDOMSharedInternals =\n ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n sharedNotPendingObject = {\n pending: !1,\n data: null,\n method: null,\n action: null\n },\n valueStack = [],\n index = -1;\nfunction createCursor(defaultValue) {\n return { current: defaultValue };\n}\nfunction pop(cursor) {\n 0 > index ||\n ((cursor.current = valueStack[index]), (valueStack[index] = null), index--);\n}\nfunction push(cursor, value) {\n index++;\n valueStack[index] = cursor.current;\n cursor.current = value;\n}\nvar contextStackCursor = createCursor(null),\n contextFiberStackCursor = createCursor(null),\n rootInstanceStackCursor = createCursor(null),\n hostTransitionProviderCursor = createCursor(null);\nfunction pushHostContainer(fiber, nextRootInstance) {\n push(rootInstanceStackCursor, nextRootInstance);\n push(contextFiberStackCursor, fiber);\n push(contextStackCursor, null);\n switch (nextRootInstance.nodeType) {\n case 9:\n case 11:\n fiber = (fiber = nextRootInstance.documentElement)\n ? (fiber = fiber.namespaceURI)\n ? getOwnHostContext(fiber)\n : 0\n : 0;\n break;\n default:\n if (\n ((fiber = nextRootInstance.tagName),\n (nextRootInstance = nextRootInstance.namespaceURI))\n )\n (nextRootInstance = getOwnHostContext(nextRootInstance)),\n (fiber = getChildHostContextProd(nextRootInstance, fiber));\n else\n switch (fiber) {\n case \"svg\":\n fiber = 1;\n break;\n case \"math\":\n fiber = 2;\n break;\n default:\n fiber = 0;\n }\n }\n pop(contextStackCursor);\n push(contextStackCursor, fiber);\n}\nfunction popHostContainer() {\n pop(contextStackCursor);\n pop(contextFiberStackCursor);\n pop(rootInstanceStackCursor);\n}\nfunction pushHostContext(fiber) {\n null !== fiber.memoizedState && push(hostTransitionProviderCursor, fiber);\n var context = contextStackCursor.current;\n var JSCompiler_inline_result = getChildHostContextProd(context, fiber.type);\n context !== JSCompiler_inline_result &&\n (push(contextFiberStackCursor, fiber),\n push(contextStackCursor, JSCompiler_inline_result));\n}\nfunction popHostContext(fiber) {\n contextFiberStackCursor.current === fiber &&\n (pop(contextStackCursor), pop(contextFiberStackCursor));\n hostTransitionProviderCursor.current === fiber &&\n (pop(hostTransitionProviderCursor),\n (HostTransitionContext._currentValue = sharedNotPendingObject));\n}\nvar prefix, suffix;\nfunction describeBuiltInComponentFrame(name) {\n if (void 0 === prefix)\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = (match && match[1]) || \"\";\n suffix =\n -1 < x.stack.indexOf(\"\\n at\")\n ? \" ()\"\n : -1 < x.stack.indexOf(\"@\")\n ? \"@unknown:0:0\"\n : \"\";\n }\n return \"\\n\" + prefix + name + suffix;\n}\nvar reentry = !1;\nfunction describeNativeComponentFrame(fn, construct) {\n if (!fn || reentry) return \"\";\n reentry = !0;\n var previousPrepareStackTrace = Error.prepareStackTrace;\n Error.prepareStackTrace = void 0;\n try {\n var RunInRootFrame = {\n DetermineComponentFrameRoot: function () {\n try {\n if (construct) {\n var Fake = function () {\n throw Error();\n };\n Object.defineProperty(Fake.prototype, \"props\", {\n set: function () {\n throw Error();\n }\n });\n if (\"object\" === typeof Reflect && Reflect.construct) {\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n var control = x;\n }\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x$1) {\n control = x$1;\n }\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x$2) {\n control = x$2;\n }\n (Fake = fn()) &&\n \"function\" === typeof Fake.catch &&\n Fake.catch(function () {});\n }\n } catch (sample) {\n if (sample && control && \"string\" === typeof sample.stack)\n return [sample.stack, control.stack];\n }\n return [null, null];\n }\n };\n RunInRootFrame.DetermineComponentFrameRoot.displayName =\n \"DetermineComponentFrameRoot\";\n var namePropDescriptor = Object.getOwnPropertyDescriptor(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\"\n );\n namePropDescriptor &&\n namePropDescriptor.configurable &&\n Object.defineProperty(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\",\n { value: \"DetermineComponentFrameRoot\" }\n );\n var _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(),\n sampleStack = _RunInRootFrame$Deter[0],\n controlStack = _RunInRootFrame$Deter[1];\n if (sampleStack && controlStack) {\n var sampleLines = sampleStack.split(\"\\n\"),\n controlLines = controlStack.split(\"\\n\");\n for (\n namePropDescriptor = RunInRootFrame = 0;\n RunInRootFrame < sampleLines.length &&\n !sampleLines[RunInRootFrame].includes(\"DetermineComponentFrameRoot\");\n\n )\n RunInRootFrame++;\n for (\n ;\n namePropDescriptor < controlLines.length &&\n !controlLines[namePropDescriptor].includes(\n \"DetermineComponentFrameRoot\"\n );\n\n )\n namePropDescriptor++;\n if (\n RunInRootFrame === sampleLines.length ||\n namePropDescriptor === controlLines.length\n )\n for (\n RunInRootFrame = sampleLines.length - 1,\n namePropDescriptor = controlLines.length - 1;\n 1 <= RunInRootFrame &&\n 0 <= namePropDescriptor &&\n sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor];\n\n )\n namePropDescriptor--;\n for (\n ;\n 1 <= RunInRootFrame && 0 <= namePropDescriptor;\n RunInRootFrame--, namePropDescriptor--\n )\n if (sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {\n if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {\n do\n if (\n (RunInRootFrame--,\n namePropDescriptor--,\n 0 > namePropDescriptor ||\n sampleLines[RunInRootFrame] !==\n controlLines[namePropDescriptor])\n ) {\n var frame =\n \"\\n\" +\n sampleLines[RunInRootFrame].replace(\" at new \", \" at \");\n fn.displayName &&\n frame.includes(\"\") &&\n (frame = frame.replace(\"\", fn.displayName));\n return frame;\n }\n while (1 <= RunInRootFrame && 0 <= namePropDescriptor);\n }\n break;\n }\n }\n } finally {\n (reentry = !1), (Error.prepareStackTrace = previousPrepareStackTrace);\n }\n return (previousPrepareStackTrace = fn ? fn.displayName || fn.name : \"\")\n ? describeBuiltInComponentFrame(previousPrepareStackTrace)\n : \"\";\n}\nfunction describeFiber(fiber, childFiber) {\n switch (fiber.tag) {\n case 26:\n case 27:\n case 5:\n return describeBuiltInComponentFrame(fiber.type);\n case 16:\n return describeBuiltInComponentFrame(\"Lazy\");\n case 13:\n return fiber.child !== childFiber && null !== childFiber\n ? describeBuiltInComponentFrame(\"Suspense Fallback\")\n : describeBuiltInComponentFrame(\"Suspense\");\n case 19:\n return describeBuiltInComponentFrame(\"SuspenseList\");\n case 0:\n case 15:\n return describeNativeComponentFrame(fiber.type, !1);\n case 11:\n return describeNativeComponentFrame(fiber.type.render, !1);\n case 1:\n return describeNativeComponentFrame(fiber.type, !0);\n case 31:\n return describeBuiltInComponentFrame(\"Activity\");\n default:\n return \"\";\n }\n}\nfunction getStackByFiberInDevAndProd(workInProgress) {\n try {\n var info = \"\",\n previous = null;\n do\n (info += describeFiber(workInProgress, previous)),\n (previous = workInProgress),\n (workInProgress = workInProgress.return);\n while (workInProgress);\n return info;\n } catch (x) {\n return \"\\nError generating stack: \" + x.message + \"\\n\" + x.stack;\n }\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty,\n scheduleCallback$3 = Scheduler.unstable_scheduleCallback,\n cancelCallback$1 = Scheduler.unstable_cancelCallback,\n shouldYield = Scheduler.unstable_shouldYield,\n requestPaint = Scheduler.unstable_requestPaint,\n now = Scheduler.unstable_now,\n getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel,\n ImmediatePriority = Scheduler.unstable_ImmediatePriority,\n UserBlockingPriority = Scheduler.unstable_UserBlockingPriority,\n NormalPriority$1 = Scheduler.unstable_NormalPriority,\n LowPriority = Scheduler.unstable_LowPriority,\n IdlePriority = Scheduler.unstable_IdlePriority,\n log$1 = Scheduler.log,\n unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue,\n rendererID = null,\n injectedHook = null;\nfunction setIsStrictModeForDevtools(newIsStrictMode) {\n \"function\" === typeof log$1 && unstable_setDisableYieldValue(newIsStrictMode);\n if (injectedHook && \"function\" === typeof injectedHook.setStrictMode)\n try {\n injectedHook.setStrictMode(rendererID, newIsStrictMode);\n } catch (err) {}\n}\nvar clz32 = Math.clz32 ? Math.clz32 : clz32Fallback,\n log = Math.log,\n LN2 = Math.LN2;\nfunction clz32Fallback(x) {\n x >>>= 0;\n return 0 === x ? 32 : (31 - ((log(x) / LN2) | 0)) | 0;\n}\nvar nextTransitionUpdateLane = 256,\n nextTransitionDeferredLane = 262144,\n nextRetryLane = 4194304;\nfunction getHighestPriorityLanes(lanes) {\n var pendingSyncLanes = lanes & 42;\n if (0 !== pendingSyncLanes) return pendingSyncLanes;\n switch (lanes & -lanes) {\n case 1:\n return 1;\n case 2:\n return 2;\n case 4:\n return 4;\n case 8:\n return 8;\n case 16:\n return 16;\n case 32:\n return 32;\n case 64:\n return 64;\n case 128:\n return 128;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n return lanes & 261888;\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return lanes & 3932160;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return lanes & 62914560;\n case 67108864:\n return 67108864;\n case 134217728:\n return 134217728;\n case 268435456:\n return 268435456;\n case 536870912:\n return 536870912;\n case 1073741824:\n return 0;\n default:\n return lanes;\n }\n}\nfunction getNextLanes(root, wipLanes, rootHasPendingCommit) {\n var pendingLanes = root.pendingLanes;\n if (0 === pendingLanes) return 0;\n var nextLanes = 0,\n suspendedLanes = root.suspendedLanes,\n pingedLanes = root.pingedLanes;\n root = root.warmLanes;\n var nonIdlePendingLanes = pendingLanes & 134217727;\n 0 !== nonIdlePendingLanes\n ? ((pendingLanes = nonIdlePendingLanes & ~suspendedLanes),\n 0 !== pendingLanes\n ? (nextLanes = getHighestPriorityLanes(pendingLanes))\n : ((pingedLanes &= nonIdlePendingLanes),\n 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = nonIdlePendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit)))))\n : ((nonIdlePendingLanes = pendingLanes & ~suspendedLanes),\n 0 !== nonIdlePendingLanes\n ? (nextLanes = getHighestPriorityLanes(nonIdlePendingLanes))\n : 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = pendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit))));\n return 0 === nextLanes\n ? 0\n : 0 !== wipLanes &&\n wipLanes !== nextLanes &&\n 0 === (wipLanes & suspendedLanes) &&\n ((suspendedLanes = nextLanes & -nextLanes),\n (rootHasPendingCommit = wipLanes & -wipLanes),\n suspendedLanes >= rootHasPendingCommit ||\n (32 === suspendedLanes && 0 !== (rootHasPendingCommit & 4194048)))\n ? wipLanes\n : nextLanes;\n}\nfunction checkIfRootIsPrerendering(root, renderLanes) {\n return (\n 0 ===\n (root.pendingLanes &\n ~(root.suspendedLanes & ~root.pingedLanes) &\n renderLanes)\n );\n}\nfunction computeExpirationTime(lane, currentTime) {\n switch (lane) {\n case 1:\n case 2:\n case 4:\n case 8:\n case 64:\n return currentTime + 250;\n case 16:\n case 32:\n case 128:\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return currentTime + 5e3;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return -1;\n case 67108864:\n case 134217728:\n case 268435456:\n case 536870912:\n case 1073741824:\n return -1;\n default:\n return -1;\n }\n}\nfunction claimNextRetryLane() {\n var lane = nextRetryLane;\n nextRetryLane <<= 1;\n 0 === (nextRetryLane & 62914560) && (nextRetryLane = 4194304);\n return lane;\n}\nfunction createLaneMap(initial) {\n for (var laneMap = [], i = 0; 31 > i; i++) laneMap.push(initial);\n return laneMap;\n}\nfunction markRootUpdated$1(root, updateLane) {\n root.pendingLanes |= updateLane;\n 268435456 !== updateLane &&\n ((root.suspendedLanes = 0), (root.pingedLanes = 0), (root.warmLanes = 0));\n}\nfunction markRootFinished(\n root,\n finishedLanes,\n remainingLanes,\n spawnedLane,\n updatedLanes,\n suspendedRetryLanes\n) {\n var previouslyPendingLanes = root.pendingLanes;\n root.pendingLanes = remainingLanes;\n root.suspendedLanes = 0;\n root.pingedLanes = 0;\n root.warmLanes = 0;\n root.expiredLanes &= remainingLanes;\n root.entangledLanes &= remainingLanes;\n root.errorRecoveryDisabledLanes &= remainingLanes;\n root.shellSuspendCounter = 0;\n var entanglements = root.entanglements,\n expirationTimes = root.expirationTimes,\n hiddenUpdates = root.hiddenUpdates;\n for (\n remainingLanes = previouslyPendingLanes & ~remainingLanes;\n 0 < remainingLanes;\n\n ) {\n var index$7 = 31 - clz32(remainingLanes),\n lane = 1 << index$7;\n entanglements[index$7] = 0;\n expirationTimes[index$7] = -1;\n var hiddenUpdatesForLane = hiddenUpdates[index$7];\n if (null !== hiddenUpdatesForLane)\n for (\n hiddenUpdates[index$7] = null, index$7 = 0;\n index$7 < hiddenUpdatesForLane.length;\n index$7++\n ) {\n var update = hiddenUpdatesForLane[index$7];\n null !== update && (update.lane &= -536870913);\n }\n remainingLanes &= ~lane;\n }\n 0 !== spawnedLane && markSpawnedDeferredLane(root, spawnedLane, 0);\n 0 !== suspendedRetryLanes &&\n 0 === updatedLanes &&\n 0 !== root.tag &&\n (root.suspendedLanes |=\n suspendedRetryLanes & ~(previouslyPendingLanes & ~finishedLanes));\n}\nfunction markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {\n root.pendingLanes |= spawnedLane;\n root.suspendedLanes &= ~spawnedLane;\n var spawnedLaneIndex = 31 - clz32(spawnedLane);\n root.entangledLanes |= spawnedLane;\n root.entanglements[spawnedLaneIndex] =\n root.entanglements[spawnedLaneIndex] |\n 1073741824 |\n (entangledLanes & 261930);\n}\nfunction markRootEntangled(root, entangledLanes) {\n var rootEntangledLanes = (root.entangledLanes |= entangledLanes);\n for (root = root.entanglements; rootEntangledLanes; ) {\n var index$8 = 31 - clz32(rootEntangledLanes),\n lane = 1 << index$8;\n (lane & entangledLanes) | (root[index$8] & entangledLanes) &&\n (root[index$8] |= entangledLanes);\n rootEntangledLanes &= ~lane;\n }\n}\nfunction getBumpedLaneForHydration(root, renderLanes) {\n var renderLane = renderLanes & -renderLanes;\n renderLane =\n 0 !== (renderLane & 42) ? 1 : getBumpedLaneForHydrationByLane(renderLane);\n return 0 !== (renderLane & (root.suspendedLanes | renderLanes))\n ? 0\n : renderLane;\n}\nfunction getBumpedLaneForHydrationByLane(lane) {\n switch (lane) {\n case 2:\n lane = 1;\n break;\n case 8:\n lane = 4;\n break;\n case 32:\n lane = 16;\n break;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n lane = 128;\n break;\n case 268435456:\n lane = 134217728;\n break;\n default:\n lane = 0;\n }\n return lane;\n}\nfunction lanesToEventPriority(lanes) {\n lanes &= -lanes;\n return 2 < lanes\n ? 8 < lanes\n ? 0 !== (lanes & 134217727)\n ? 32\n : 268435456\n : 8\n : 2;\n}\nfunction resolveUpdatePriority() {\n var updatePriority = ReactDOMSharedInternals.p;\n if (0 !== updatePriority) return updatePriority;\n updatePriority = window.event;\n return void 0 === updatePriority ? 32 : getEventPriority(updatePriority.type);\n}\nfunction runWithPriority(priority, fn) {\n var previousPriority = ReactDOMSharedInternals.p;\n try {\n return (ReactDOMSharedInternals.p = priority), fn();\n } finally {\n ReactDOMSharedInternals.p = previousPriority;\n }\n}\nvar randomKey = Math.random().toString(36).slice(2),\n internalInstanceKey = \"__reactFiber$\" + randomKey,\n internalPropsKey = \"__reactProps$\" + randomKey,\n internalContainerInstanceKey = \"__reactContainer$\" + randomKey,\n internalEventHandlersKey = \"__reactEvents$\" + randomKey,\n internalEventHandlerListenersKey = \"__reactListeners$\" + randomKey,\n internalEventHandlesSetKey = \"__reactHandles$\" + randomKey,\n internalRootNodeResourcesKey = \"__reactResources$\" + randomKey,\n internalHoistableMarker = \"__reactMarker$\" + randomKey;\nfunction detachDeletedInstance(node) {\n delete node[internalInstanceKey];\n delete node[internalPropsKey];\n delete node[internalEventHandlersKey];\n delete node[internalEventHandlerListenersKey];\n delete node[internalEventHandlesSetKey];\n}\nfunction getClosestInstanceFromNode(targetNode) {\n var targetInst = targetNode[internalInstanceKey];\n if (targetInst) return targetInst;\n for (var parentNode = targetNode.parentNode; parentNode; ) {\n if (\n (targetInst =\n parentNode[internalContainerInstanceKey] ||\n parentNode[internalInstanceKey])\n ) {\n parentNode = targetInst.alternate;\n if (\n null !== targetInst.child ||\n (null !== parentNode && null !== parentNode.child)\n )\n for (\n targetNode = getParentHydrationBoundary(targetNode);\n null !== targetNode;\n\n ) {\n if ((parentNode = targetNode[internalInstanceKey])) return parentNode;\n targetNode = getParentHydrationBoundary(targetNode);\n }\n return targetInst;\n }\n targetNode = parentNode;\n parentNode = targetNode.parentNode;\n }\n return null;\n}\nfunction getInstanceFromNode(node) {\n if (\n (node = node[internalInstanceKey] || node[internalContainerInstanceKey])\n ) {\n var tag = node.tag;\n if (\n 5 === tag ||\n 6 === tag ||\n 13 === tag ||\n 31 === tag ||\n 26 === tag ||\n 27 === tag ||\n 3 === tag\n )\n return node;\n }\n return null;\n}\nfunction getNodeFromInstance(inst) {\n var tag = inst.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return inst.stateNode;\n throw Error(formatProdErrorMessage(33));\n}\nfunction getResourcesFromRoot(root) {\n var resources = root[internalRootNodeResourcesKey];\n resources ||\n (resources = root[internalRootNodeResourcesKey] =\n { hoistableStyles: new Map(), hoistableScripts: new Map() });\n return resources;\n}\nfunction markNodeAsHoistable(node) {\n node[internalHoistableMarker] = !0;\n}\nvar allNativeEvents = new Set(),\n registrationNameDependencies = {};\nfunction registerTwoPhaseEvent(registrationName, dependencies) {\n registerDirectEvent(registrationName, dependencies);\n registerDirectEvent(registrationName + \"Capture\", dependencies);\n}\nfunction registerDirectEvent(registrationName, dependencies) {\n registrationNameDependencies[registrationName] = dependencies;\n for (\n registrationName = 0;\n registrationName < dependencies.length;\n registrationName++\n )\n allNativeEvents.add(dependencies[registrationName]);\n}\nvar VALID_ATTRIBUTE_NAME_REGEX = RegExp(\n \"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"\n ),\n illegalAttributeNameCache = {},\n validatedAttributeNameCache = {};\nfunction isAttributeNameSafe(attributeName) {\n if (hasOwnProperty.call(validatedAttributeNameCache, attributeName))\n return !0;\n if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) return !1;\n if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName))\n return (validatedAttributeNameCache[attributeName] = !0);\n illegalAttributeNameCache[attributeName] = !0;\n return !1;\n}\nfunction setValueForAttribute(node, name, value) {\n if (isAttributeNameSafe(name))\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n node.removeAttribute(name);\n return;\n case \"boolean\":\n var prefix$10 = name.toLowerCase().slice(0, 5);\n if (\"data-\" !== prefix$10 && \"aria-\" !== prefix$10) {\n node.removeAttribute(name);\n return;\n }\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForKnownAttribute(node, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForNamespacedAttribute(node, namespace, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttributeNS(namespace, name, \"\" + value);\n }\n}\nfunction getToStringValue(value) {\n switch (typeof value) {\n case \"bigint\":\n case \"boolean\":\n case \"number\":\n case \"string\":\n case \"undefined\":\n return value;\n case \"object\":\n return value;\n default:\n return \"\";\n }\n}\nfunction isCheckable(elem) {\n var type = elem.type;\n return (\n (elem = elem.nodeName) &&\n \"input\" === elem.toLowerCase() &&\n (\"checkbox\" === type || \"radio\" === type)\n );\n}\nfunction trackValueOnNode(node, valueField, currentValue) {\n var descriptor = Object.getOwnPropertyDescriptor(\n node.constructor.prototype,\n valueField\n );\n if (\n !node.hasOwnProperty(valueField) &&\n \"undefined\" !== typeof descriptor &&\n \"function\" === typeof descriptor.get &&\n \"function\" === typeof descriptor.set\n ) {\n var get = descriptor.get,\n set = descriptor.set;\n Object.defineProperty(node, valueField, {\n configurable: !0,\n get: function () {\n return get.call(this);\n },\n set: function (value) {\n currentValue = \"\" + value;\n set.call(this, value);\n }\n });\n Object.defineProperty(node, valueField, {\n enumerable: descriptor.enumerable\n });\n return {\n getValue: function () {\n return currentValue;\n },\n setValue: function (value) {\n currentValue = \"\" + value;\n },\n stopTracking: function () {\n node._valueTracker = null;\n delete node[valueField];\n }\n };\n }\n}\nfunction track(node) {\n if (!node._valueTracker) {\n var valueField = isCheckable(node) ? \"checked\" : \"value\";\n node._valueTracker = trackValueOnNode(\n node,\n valueField,\n \"\" + node[valueField]\n );\n }\n}\nfunction updateValueIfChanged(node) {\n if (!node) return !1;\n var tracker = node._valueTracker;\n if (!tracker) return !0;\n var lastValue = tracker.getValue();\n var value = \"\";\n node &&\n (value = isCheckable(node)\n ? node.checked\n ? \"true\"\n : \"false\"\n : node.value);\n node = value;\n return node !== lastValue ? (tracker.setValue(node), !0) : !1;\n}\nfunction getActiveElement(doc) {\n doc = doc || (\"undefined\" !== typeof document ? document : void 0);\n if (\"undefined\" === typeof doc) return null;\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\nvar escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\\n\"\\\\]/g;\nfunction escapeSelectorAttributeValueInsideDoubleQuotes(value) {\n return value.replace(\n escapeSelectorAttributeValueInsideDoubleQuotesRegex,\n function (ch) {\n return \"\\\\\" + ch.charCodeAt(0).toString(16) + \" \";\n }\n );\n}\nfunction updateInput(\n element,\n value,\n defaultValue,\n lastDefaultValue,\n checked,\n defaultChecked,\n type,\n name\n) {\n element.name = \"\";\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type\n ? (element.type = type)\n : element.removeAttribute(\"type\");\n if (null != value)\n if (\"number\" === type) {\n if ((0 === value && \"\" === element.value) || element.value != value)\n element.value = \"\" + getToStringValue(value);\n } else\n element.value !== \"\" + getToStringValue(value) &&\n (element.value = \"\" + getToStringValue(value));\n else\n (\"submit\" !== type && \"reset\" !== type) || element.removeAttribute(\"value\");\n null != value\n ? setDefaultValue(element, type, getToStringValue(value))\n : null != defaultValue\n ? setDefaultValue(element, type, getToStringValue(defaultValue))\n : null != lastDefaultValue && element.removeAttribute(\"value\");\n null == checked &&\n null != defaultChecked &&\n (element.defaultChecked = !!defaultChecked);\n null != checked &&\n (element.checked =\n checked && \"function\" !== typeof checked && \"symbol\" !== typeof checked);\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name\n ? (element.name = \"\" + getToStringValue(name))\n : element.removeAttribute(\"name\");\n}\nfunction initInput(\n element,\n value,\n defaultValue,\n checked,\n defaultChecked,\n type,\n name,\n isHydrating\n) {\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type &&\n (element.type = type);\n if (null != value || null != defaultValue) {\n if (\n !(\n (\"submit\" !== type && \"reset\" !== type) ||\n (void 0 !== value && null !== value)\n )\n ) {\n track(element);\n return;\n }\n defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n value = null != value ? \"\" + getToStringValue(value) : defaultValue;\n isHydrating || value === element.value || (element.value = value);\n element.defaultValue = value;\n }\n checked = null != checked ? checked : defaultChecked;\n checked =\n \"function\" !== typeof checked && \"symbol\" !== typeof checked && !!checked;\n element.checked = isHydrating ? element.checked : !!checked;\n element.defaultChecked = !!checked;\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name &&\n (element.name = name);\n track(element);\n}\nfunction setDefaultValue(node, type, value) {\n (\"number\" === type && getActiveElement(node.ownerDocument) === node) ||\n node.defaultValue === \"\" + value ||\n (node.defaultValue = \"\" + value);\n}\nfunction updateOptions(node, multiple, propValue, setDefaultSelected) {\n node = node.options;\n if (multiple) {\n multiple = {};\n for (var i = 0; i < propValue.length; i++)\n multiple[\"$\" + propValue[i]] = !0;\n for (propValue = 0; propValue < node.length; propValue++)\n (i = multiple.hasOwnProperty(\"$\" + node[propValue].value)),\n node[propValue].selected !== i && (node[propValue].selected = i),\n i && setDefaultSelected && (node[propValue].defaultSelected = !0);\n } else {\n propValue = \"\" + getToStringValue(propValue);\n multiple = null;\n for (i = 0; i < node.length; i++) {\n if (node[i].value === propValue) {\n node[i].selected = !0;\n setDefaultSelected && (node[i].defaultSelected = !0);\n return;\n }\n null !== multiple || node[i].disabled || (multiple = node[i]);\n }\n null !== multiple && (multiple.selected = !0);\n }\n}\nfunction updateTextarea(element, value, defaultValue) {\n if (\n null != value &&\n ((value = \"\" + getToStringValue(value)),\n value !== element.value && (element.value = value),\n null == defaultValue)\n ) {\n element.defaultValue !== value && (element.defaultValue = value);\n return;\n }\n element.defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n}\nfunction initTextarea(element, value, defaultValue, children) {\n if (null == value) {\n if (null != children) {\n if (null != defaultValue) throw Error(formatProdErrorMessage(92));\n if (isArrayImpl(children)) {\n if (1 < children.length) throw Error(formatProdErrorMessage(93));\n children = children[0];\n }\n defaultValue = children;\n }\n null == defaultValue && (defaultValue = \"\");\n value = defaultValue;\n }\n defaultValue = getToStringValue(value);\n element.defaultValue = defaultValue;\n children = element.textContent;\n children === defaultValue &&\n \"\" !== children &&\n null !== children &&\n (element.value = children);\n track(element);\n}\nfunction setTextContent(node, text) {\n if (text) {\n var firstChild = node.firstChild;\n if (\n firstChild &&\n firstChild === node.lastChild &&\n 3 === firstChild.nodeType\n ) {\n firstChild.nodeValue = text;\n return;\n }\n }\n node.textContent = text;\n}\nvar unitlessNumbers = new Set(\n \"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\n \" \"\n )\n);\nfunction setValueForStyle(style, styleName, value) {\n var isCustomProperty = 0 === styleName.indexOf(\"--\");\n null == value || \"boolean\" === typeof value || \"\" === value\n ? isCustomProperty\n ? style.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (style.cssFloat = \"\")\n : (style[styleName] = \"\")\n : isCustomProperty\n ? style.setProperty(styleName, value)\n : \"number\" !== typeof value ||\n 0 === value ||\n unitlessNumbers.has(styleName)\n ? \"float\" === styleName\n ? (style.cssFloat = value)\n : (style[styleName] = (\"\" + value).trim())\n : (style[styleName] = value + \"px\");\n}\nfunction setValueForStyles(node, styles, prevStyles) {\n if (null != styles && \"object\" !== typeof styles)\n throw Error(formatProdErrorMessage(62));\n node = node.style;\n if (null != prevStyles) {\n for (var styleName in prevStyles)\n !prevStyles.hasOwnProperty(styleName) ||\n (null != styles && styles.hasOwnProperty(styleName)) ||\n (0 === styleName.indexOf(\"--\")\n ? node.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (node.cssFloat = \"\")\n : (node[styleName] = \"\"));\n for (var styleName$16 in styles)\n (styleName = styles[styleName$16]),\n styles.hasOwnProperty(styleName$16) &&\n prevStyles[styleName$16] !== styleName &&\n setValueForStyle(node, styleName$16, styleName);\n } else\n for (var styleName$17 in styles)\n styles.hasOwnProperty(styleName$17) &&\n setValueForStyle(node, styleName$17, styles[styleName$17]);\n}\nfunction isCustomElement(tagName) {\n if (-1 === tagName.indexOf(\"-\")) return !1;\n switch (tagName) {\n case \"annotation-xml\":\n case \"color-profile\":\n case \"font-face\":\n case \"font-face-src\":\n case \"font-face-uri\":\n case \"font-face-format\":\n case \"font-face-name\":\n case \"missing-glyph\":\n return !1;\n default:\n return !0;\n }\n}\nvar aliases = new Map([\n [\"acceptCharset\", \"accept-charset\"],\n [\"htmlFor\", \"for\"],\n [\"httpEquiv\", \"http-equiv\"],\n [\"crossOrigin\", \"crossorigin\"],\n [\"accentHeight\", \"accent-height\"],\n [\"alignmentBaseline\", \"alignment-baseline\"],\n [\"arabicForm\", \"arabic-form\"],\n [\"baselineShift\", \"baseline-shift\"],\n [\"capHeight\", \"cap-height\"],\n [\"clipPath\", \"clip-path\"],\n [\"clipRule\", \"clip-rule\"],\n [\"colorInterpolation\", \"color-interpolation\"],\n [\"colorInterpolationFilters\", \"color-interpolation-filters\"],\n [\"colorProfile\", \"color-profile\"],\n [\"colorRendering\", \"color-rendering\"],\n [\"dominantBaseline\", \"dominant-baseline\"],\n [\"enableBackground\", \"enable-background\"],\n [\"fillOpacity\", \"fill-opacity\"],\n [\"fillRule\", \"fill-rule\"],\n [\"floodColor\", \"flood-color\"],\n [\"floodOpacity\", \"flood-opacity\"],\n [\"fontFamily\", \"font-family\"],\n [\"fontSize\", \"font-size\"],\n [\"fontSizeAdjust\", \"font-size-adjust\"],\n [\"fontStretch\", \"font-stretch\"],\n [\"fontStyle\", \"font-style\"],\n [\"fontVariant\", \"font-variant\"],\n [\"fontWeight\", \"font-weight\"],\n [\"glyphName\", \"glyph-name\"],\n [\"glyphOrientationHorizontal\", \"glyph-orientation-horizontal\"],\n [\"glyphOrientationVertical\", \"glyph-orientation-vertical\"],\n [\"horizAdvX\", \"horiz-adv-x\"],\n [\"horizOriginX\", \"horiz-origin-x\"],\n [\"imageRendering\", \"image-rendering\"],\n [\"letterSpacing\", \"letter-spacing\"],\n [\"lightingColor\", \"lighting-color\"],\n [\"markerEnd\", \"marker-end\"],\n [\"markerMid\", \"marker-mid\"],\n [\"markerStart\", \"marker-start\"],\n [\"overlinePosition\", \"overline-position\"],\n [\"overlineThickness\", \"overline-thickness\"],\n [\"paintOrder\", \"paint-order\"],\n [\"panose-1\", \"panose-1\"],\n [\"pointerEvents\", \"pointer-events\"],\n [\"renderingIntent\", \"rendering-intent\"],\n [\"shapeRendering\", \"shape-rendering\"],\n [\"stopColor\", \"stop-color\"],\n [\"stopOpacity\", \"stop-opacity\"],\n [\"strikethroughPosition\", \"strikethrough-position\"],\n [\"strikethroughThickness\", \"strikethrough-thickness\"],\n [\"strokeDasharray\", \"stroke-dasharray\"],\n [\"strokeDashoffset\", \"stroke-dashoffset\"],\n [\"strokeLinecap\", \"stroke-linecap\"],\n [\"strokeLinejoin\", \"stroke-linejoin\"],\n [\"strokeMiterlimit\", \"stroke-miterlimit\"],\n [\"strokeOpacity\", \"stroke-opacity\"],\n [\"strokeWidth\", \"stroke-width\"],\n [\"textAnchor\", \"text-anchor\"],\n [\"textDecoration\", \"text-decoration\"],\n [\"textRendering\", \"text-rendering\"],\n [\"transformOrigin\", \"transform-origin\"],\n [\"underlinePosition\", \"underline-position\"],\n [\"underlineThickness\", \"underline-thickness\"],\n [\"unicodeBidi\", \"unicode-bidi\"],\n [\"unicodeRange\", \"unicode-range\"],\n [\"unitsPerEm\", \"units-per-em\"],\n [\"vAlphabetic\", \"v-alphabetic\"],\n [\"vHanging\", \"v-hanging\"],\n [\"vIdeographic\", \"v-ideographic\"],\n [\"vMathematical\", \"v-mathematical\"],\n [\"vectorEffect\", \"vector-effect\"],\n [\"vertAdvY\", \"vert-adv-y\"],\n [\"vertOriginX\", \"vert-origin-x\"],\n [\"vertOriginY\", \"vert-origin-y\"],\n [\"wordSpacing\", \"word-spacing\"],\n [\"writingMode\", \"writing-mode\"],\n [\"xmlnsXlink\", \"xmlns:xlink\"],\n [\"xHeight\", \"x-height\"]\n ]),\n isJavaScriptProtocol =\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;\nfunction sanitizeURL(url) {\n return isJavaScriptProtocol.test(\"\" + url)\n ? \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\"\n : url;\n}\nfunction noop$1() {}\nvar currentReplayingEvent = null;\nfunction getEventTarget(nativeEvent) {\n nativeEvent = nativeEvent.target || nativeEvent.srcElement || window;\n nativeEvent.correspondingUseElement &&\n (nativeEvent = nativeEvent.correspondingUseElement);\n return 3 === nativeEvent.nodeType ? nativeEvent.parentNode : nativeEvent;\n}\nvar restoreTarget = null,\n restoreQueue = null;\nfunction restoreStateOfTarget(target) {\n var internalInstance = getInstanceFromNode(target);\n if (internalInstance && (target = internalInstance.stateNode)) {\n var props = target[internalPropsKey] || null;\n a: switch (((target = internalInstance.stateNode), internalInstance.type)) {\n case \"input\":\n updateInput(\n target,\n props.value,\n props.defaultValue,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name\n );\n internalInstance = props.name;\n if (\"radio\" === props.type && null != internalInstance) {\n for (props = target; props.parentNode; ) props = props.parentNode;\n props = props.querySelectorAll(\n 'input[name=\"' +\n escapeSelectorAttributeValueInsideDoubleQuotes(\n \"\" + internalInstance\n ) +\n '\"][type=\"radio\"]'\n );\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n ) {\n var otherNode = props[internalInstance];\n if (otherNode !== target && otherNode.form === target.form) {\n var otherProps = otherNode[internalPropsKey] || null;\n if (!otherProps) throw Error(formatProdErrorMessage(90));\n updateInput(\n otherNode,\n otherProps.value,\n otherProps.defaultValue,\n otherProps.defaultValue,\n otherProps.checked,\n otherProps.defaultChecked,\n otherProps.type,\n otherProps.name\n );\n }\n }\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n )\n (otherNode = props[internalInstance]),\n otherNode.form === target.form && updateValueIfChanged(otherNode);\n }\n break a;\n case \"textarea\":\n updateTextarea(target, props.value, props.defaultValue);\n break a;\n case \"select\":\n (internalInstance = props.value),\n null != internalInstance &&\n updateOptions(target, !!props.multiple, internalInstance, !1);\n }\n }\n}\nvar isInsideEventHandler = !1;\nfunction batchedUpdates$1(fn, a, b) {\n if (isInsideEventHandler) return fn(a, b);\n isInsideEventHandler = !0;\n try {\n var JSCompiler_inline_result = fn(a);\n return JSCompiler_inline_result;\n } finally {\n if (\n ((isInsideEventHandler = !1),\n null !== restoreTarget || null !== restoreQueue)\n )\n if (\n (flushSyncWork$1(),\n restoreTarget &&\n ((a = restoreTarget),\n (fn = restoreQueue),\n (restoreQueue = restoreTarget = null),\n restoreStateOfTarget(a),\n fn))\n )\n for (a = 0; a < fn.length; a++) restoreStateOfTarget(fn[a]);\n }\n}\nfunction getListener(inst, registrationName) {\n var stateNode = inst.stateNode;\n if (null === stateNode) return null;\n var props = stateNode[internalPropsKey] || null;\n if (null === props) return null;\n stateNode = props[registrationName];\n a: switch (registrationName) {\n case \"onClick\":\n case \"onClickCapture\":\n case \"onDoubleClick\":\n case \"onDoubleClickCapture\":\n case \"onMouseDown\":\n case \"onMouseDownCapture\":\n case \"onMouseMove\":\n case \"onMouseMoveCapture\":\n case \"onMouseUp\":\n case \"onMouseUpCapture\":\n case \"onMouseEnter\":\n (props = !props.disabled) ||\n ((inst = inst.type),\n (props = !(\n \"button\" === inst ||\n \"input\" === inst ||\n \"select\" === inst ||\n \"textarea\" === inst\n )));\n inst = !props;\n break a;\n default:\n inst = !1;\n }\n if (inst) return null;\n if (stateNode && \"function\" !== typeof stateNode)\n throw Error(\n formatProdErrorMessage(231, registrationName, typeof stateNode)\n );\n return stateNode;\n}\nvar canUseDOM = !(\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ),\n passiveBrowserEventsSupported = !1;\nif (canUseDOM)\n try {\n var options = {};\n Object.defineProperty(options, \"passive\", {\n get: function () {\n passiveBrowserEventsSupported = !0;\n }\n });\n window.addEventListener(\"test\", options, options);\n window.removeEventListener(\"test\", options, options);\n } catch (e) {\n passiveBrowserEventsSupported = !1;\n }\nvar root = null,\n startText = null,\n fallbackText = null;\nfunction getData() {\n if (fallbackText) return fallbackText;\n var start,\n startValue = startText,\n startLength = startValue.length,\n end,\n endValue = \"value\" in root ? root.value : root.textContent,\n endLength = endValue.length;\n for (\n start = 0;\n start < startLength && startValue[start] === endValue[start];\n start++\n );\n var minEnd = startLength - start;\n for (\n end = 1;\n end <= minEnd &&\n startValue[startLength - end] === endValue[endLength - end];\n end++\n );\n return (fallbackText = endValue.slice(start, 1 < end ? 1 - end : void 0));\n}\nfunction getEventCharCode(nativeEvent) {\n var keyCode = nativeEvent.keyCode;\n \"charCode\" in nativeEvent\n ? ((nativeEvent = nativeEvent.charCode),\n 0 === nativeEvent && 13 === keyCode && (nativeEvent = 13))\n : (nativeEvent = keyCode);\n 10 === nativeEvent && (nativeEvent = 13);\n return 32 <= nativeEvent || 13 === nativeEvent ? nativeEvent : 0;\n}\nfunction functionThatReturnsTrue() {\n return !0;\n}\nfunction functionThatReturnsFalse() {\n return !1;\n}\nfunction createSyntheticEvent(Interface) {\n function SyntheticBaseEvent(\n reactName,\n reactEventType,\n targetInst,\n nativeEvent,\n nativeEventTarget\n ) {\n this._reactName = reactName;\n this._targetInst = targetInst;\n this.type = reactEventType;\n this.nativeEvent = nativeEvent;\n this.target = nativeEventTarget;\n this.currentTarget = null;\n for (var propName in Interface)\n Interface.hasOwnProperty(propName) &&\n ((reactName = Interface[propName]),\n (this[propName] = reactName\n ? reactName(nativeEvent)\n : nativeEvent[propName]));\n this.isDefaultPrevented = (\n null != nativeEvent.defaultPrevented\n ? nativeEvent.defaultPrevented\n : !1 === nativeEvent.returnValue\n )\n ? functionThatReturnsTrue\n : functionThatReturnsFalse;\n this.isPropagationStopped = functionThatReturnsFalse;\n return this;\n }\n assign(SyntheticBaseEvent.prototype, {\n preventDefault: function () {\n this.defaultPrevented = !0;\n var event = this.nativeEvent;\n event &&\n (event.preventDefault\n ? event.preventDefault()\n : \"unknown\" !== typeof event.returnValue && (event.returnValue = !1),\n (this.isDefaultPrevented = functionThatReturnsTrue));\n },\n stopPropagation: function () {\n var event = this.nativeEvent;\n event &&\n (event.stopPropagation\n ? event.stopPropagation()\n : \"unknown\" !== typeof event.cancelBubble &&\n (event.cancelBubble = !0),\n (this.isPropagationStopped = functionThatReturnsTrue));\n },\n persist: function () {},\n isPersistent: functionThatReturnsTrue\n });\n return SyntheticBaseEvent;\n}\nvar EventInterface = {\n eventPhase: 0,\n bubbles: 0,\n cancelable: 0,\n timeStamp: function (event) {\n return event.timeStamp || Date.now();\n },\n defaultPrevented: 0,\n isTrusted: 0\n },\n SyntheticEvent = createSyntheticEvent(EventInterface),\n UIEventInterface = assign({}, EventInterface, { view: 0, detail: 0 }),\n SyntheticUIEvent = createSyntheticEvent(UIEventInterface),\n lastMovementX,\n lastMovementY,\n lastMouseEvent,\n MouseEventInterface = assign({}, UIEventInterface, {\n screenX: 0,\n screenY: 0,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n getModifierState: getEventModifierState,\n button: 0,\n buttons: 0,\n relatedTarget: function (event) {\n return void 0 === event.relatedTarget\n ? event.fromElement === event.srcElement\n ? event.toElement\n : event.fromElement\n : event.relatedTarget;\n },\n movementX: function (event) {\n if (\"movementX\" in event) return event.movementX;\n event !== lastMouseEvent &&\n (lastMouseEvent && \"mousemove\" === event.type\n ? ((lastMovementX = event.screenX - lastMouseEvent.screenX),\n (lastMovementY = event.screenY - lastMouseEvent.screenY))\n : (lastMovementY = lastMovementX = 0),\n (lastMouseEvent = event));\n return lastMovementX;\n },\n movementY: function (event) {\n return \"movementY\" in event ? event.movementY : lastMovementY;\n }\n }),\n SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface),\n DragEventInterface = assign({}, MouseEventInterface, { dataTransfer: 0 }),\n SyntheticDragEvent = createSyntheticEvent(DragEventInterface),\n FocusEventInterface = assign({}, UIEventInterface, { relatedTarget: 0 }),\n SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface),\n AnimationEventInterface = assign({}, EventInterface, {\n animationName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface),\n ClipboardEventInterface = assign({}, EventInterface, {\n clipboardData: function (event) {\n return \"clipboardData\" in event\n ? event.clipboardData\n : window.clipboardData;\n }\n }),\n SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface),\n CompositionEventInterface = assign({}, EventInterface, { data: 0 }),\n SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface),\n normalizeKey = {\n Esc: \"Escape\",\n Spacebar: \" \",\n Left: \"ArrowLeft\",\n Up: \"ArrowUp\",\n Right: \"ArrowRight\",\n Down: \"ArrowDown\",\n Del: \"Delete\",\n Win: \"OS\",\n Menu: \"ContextMenu\",\n Apps: \"ContextMenu\",\n Scroll: \"ScrollLock\",\n MozPrintableKey: \"Unidentified\"\n },\n translateToKey = {\n 8: \"Backspace\",\n 9: \"Tab\",\n 12: \"Clear\",\n 13: \"Enter\",\n 16: \"Shift\",\n 17: \"Control\",\n 18: \"Alt\",\n 19: \"Pause\",\n 20: \"CapsLock\",\n 27: \"Escape\",\n 32: \" \",\n 33: \"PageUp\",\n 34: \"PageDown\",\n 35: \"End\",\n 36: \"Home\",\n 37: \"ArrowLeft\",\n 38: \"ArrowUp\",\n 39: \"ArrowRight\",\n 40: \"ArrowDown\",\n 45: \"Insert\",\n 46: \"Delete\",\n 112: \"F1\",\n 113: \"F2\",\n 114: \"F3\",\n 115: \"F4\",\n 116: \"F5\",\n 117: \"F6\",\n 118: \"F7\",\n 119: \"F8\",\n 120: \"F9\",\n 121: \"F10\",\n 122: \"F11\",\n 123: \"F12\",\n 144: \"NumLock\",\n 145: \"ScrollLock\",\n 224: \"Meta\"\n },\n modifierKeyToProp = {\n Alt: \"altKey\",\n Control: \"ctrlKey\",\n Meta: \"metaKey\",\n Shift: \"shiftKey\"\n };\nfunction modifierStateGetter(keyArg) {\n var nativeEvent = this.nativeEvent;\n return nativeEvent.getModifierState\n ? nativeEvent.getModifierState(keyArg)\n : (keyArg = modifierKeyToProp[keyArg])\n ? !!nativeEvent[keyArg]\n : !1;\n}\nfunction getEventModifierState() {\n return modifierStateGetter;\n}\nvar KeyboardEventInterface = assign({}, UIEventInterface, {\n key: function (nativeEvent) {\n if (nativeEvent.key) {\n var key = normalizeKey[nativeEvent.key] || nativeEvent.key;\n if (\"Unidentified\" !== key) return key;\n }\n return \"keypress\" === nativeEvent.type\n ? ((nativeEvent = getEventCharCode(nativeEvent)),\n 13 === nativeEvent ? \"Enter\" : String.fromCharCode(nativeEvent))\n : \"keydown\" === nativeEvent.type || \"keyup\" === nativeEvent.type\n ? translateToKey[nativeEvent.keyCode] || \"Unidentified\"\n : \"\";\n },\n code: 0,\n location: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n repeat: 0,\n locale: 0,\n getModifierState: getEventModifierState,\n charCode: function (event) {\n return \"keypress\" === event.type ? getEventCharCode(event) : 0;\n },\n keyCode: function (event) {\n return \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n },\n which: function (event) {\n return \"keypress\" === event.type\n ? getEventCharCode(event)\n : \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n }\n }),\n SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface),\n PointerEventInterface = assign({}, MouseEventInterface, {\n pointerId: 0,\n width: 0,\n height: 0,\n pressure: 0,\n tangentialPressure: 0,\n tiltX: 0,\n tiltY: 0,\n twist: 0,\n pointerType: 0,\n isPrimary: 0\n }),\n SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface),\n TouchEventInterface = assign({}, UIEventInterface, {\n touches: 0,\n targetTouches: 0,\n changedTouches: 0,\n altKey: 0,\n metaKey: 0,\n ctrlKey: 0,\n shiftKey: 0,\n getModifierState: getEventModifierState\n }),\n SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface),\n TransitionEventInterface = assign({}, EventInterface, {\n propertyName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface),\n WheelEventInterface = assign({}, MouseEventInterface, {\n deltaX: function (event) {\n return \"deltaX\" in event\n ? event.deltaX\n : \"wheelDeltaX\" in event\n ? -event.wheelDeltaX\n : 0;\n },\n deltaY: function (event) {\n return \"deltaY\" in event\n ? event.deltaY\n : \"wheelDeltaY\" in event\n ? -event.wheelDeltaY\n : \"wheelDelta\" in event\n ? -event.wheelDelta\n : 0;\n },\n deltaZ: 0,\n deltaMode: 0\n }),\n SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface),\n ToggleEventInterface = assign({}, EventInterface, {\n newState: 0,\n oldState: 0\n }),\n SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface),\n END_KEYCODES = [9, 13, 27, 32],\n canUseCompositionEvent = canUseDOM && \"CompositionEvent\" in window,\n documentMode = null;\ncanUseDOM &&\n \"documentMode\" in document &&\n (documentMode = document.documentMode);\nvar canUseTextInputEvent = canUseDOM && \"TextEvent\" in window && !documentMode,\n useFallbackCompositionData =\n canUseDOM &&\n (!canUseCompositionEvent ||\n (documentMode && 8 < documentMode && 11 >= documentMode)),\n SPACEBAR_CHAR = String.fromCharCode(32),\n hasSpaceKeypress = !1;\nfunction isFallbackCompositionEnd(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"keyup\":\n return -1 !== END_KEYCODES.indexOf(nativeEvent.keyCode);\n case \"keydown\":\n return 229 !== nativeEvent.keyCode;\n case \"keypress\":\n case \"mousedown\":\n case \"focusout\":\n return !0;\n default:\n return !1;\n }\n}\nfunction getDataFromCustomEvent(nativeEvent) {\n nativeEvent = nativeEvent.detail;\n return \"object\" === typeof nativeEvent && \"data\" in nativeEvent\n ? nativeEvent.data\n : null;\n}\nvar isComposing = !1;\nfunction getNativeBeforeInputChars(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"compositionend\":\n return getDataFromCustomEvent(nativeEvent);\n case \"keypress\":\n if (32 !== nativeEvent.which) return null;\n hasSpaceKeypress = !0;\n return SPACEBAR_CHAR;\n case \"textInput\":\n return (\n (domEventName = nativeEvent.data),\n domEventName === SPACEBAR_CHAR && hasSpaceKeypress ? null : domEventName\n );\n default:\n return null;\n }\n}\nfunction getFallbackBeforeInputChars(domEventName, nativeEvent) {\n if (isComposing)\n return \"compositionend\" === domEventName ||\n (!canUseCompositionEvent &&\n isFallbackCompositionEnd(domEventName, nativeEvent))\n ? ((domEventName = getData()),\n (fallbackText = startText = root = null),\n (isComposing = !1),\n domEventName)\n : null;\n switch (domEventName) {\n case \"paste\":\n return null;\n case \"keypress\":\n if (\n !(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) ||\n (nativeEvent.ctrlKey && nativeEvent.altKey)\n ) {\n if (nativeEvent.char && 1 < nativeEvent.char.length)\n return nativeEvent.char;\n if (nativeEvent.which) return String.fromCharCode(nativeEvent.which);\n }\n return null;\n case \"compositionend\":\n return useFallbackCompositionData && \"ko\" !== nativeEvent.locale\n ? null\n : nativeEvent.data;\n default:\n return null;\n }\n}\nvar supportedInputTypes = {\n color: !0,\n date: !0,\n datetime: !0,\n \"datetime-local\": !0,\n email: !0,\n month: !0,\n number: !0,\n password: !0,\n range: !0,\n search: !0,\n tel: !0,\n text: !0,\n time: !0,\n url: !0,\n week: !0\n};\nfunction isTextInputElement(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return \"input\" === nodeName\n ? !!supportedInputTypes[elem.type]\n : \"textarea\" === nodeName\n ? !0\n : !1;\n}\nfunction createAndAccumulateChangeEvent(\n dispatchQueue,\n inst,\n nativeEvent,\n target\n) {\n restoreTarget\n ? restoreQueue\n ? restoreQueue.push(target)\n : (restoreQueue = [target])\n : (restoreTarget = target);\n inst = accumulateTwoPhaseListeners(inst, \"onChange\");\n 0 < inst.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onChange\",\n \"change\",\n null,\n nativeEvent,\n target\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: inst }));\n}\nvar activeElement$1 = null,\n activeElementInst$1 = null;\nfunction runEventInBatch(dispatchQueue) {\n processDispatchQueue(dispatchQueue, 0);\n}\nfunction getInstIfValueChanged(targetInst) {\n var targetNode = getNodeFromInstance(targetInst);\n if (updateValueIfChanged(targetNode)) return targetInst;\n}\nfunction getTargetInstForChangeEvent(domEventName, targetInst) {\n if (\"change\" === domEventName) return targetInst;\n}\nvar isInputEventSupported = !1;\nif (canUseDOM) {\n var JSCompiler_inline_result$jscomp$286;\n if (canUseDOM) {\n var isSupported$jscomp$inline_427 = \"oninput\" in document;\n if (!isSupported$jscomp$inline_427) {\n var element$jscomp$inline_428 = document.createElement(\"div\");\n element$jscomp$inline_428.setAttribute(\"oninput\", \"return;\");\n isSupported$jscomp$inline_427 =\n \"function\" === typeof element$jscomp$inline_428.oninput;\n }\n JSCompiler_inline_result$jscomp$286 = isSupported$jscomp$inline_427;\n } else JSCompiler_inline_result$jscomp$286 = !1;\n isInputEventSupported =\n JSCompiler_inline_result$jscomp$286 &&\n (!document.documentMode || 9 < document.documentMode);\n}\nfunction stopWatchingForValueChange() {\n activeElement$1 &&\n (activeElement$1.detachEvent(\"onpropertychange\", handlePropertyChange),\n (activeElementInst$1 = activeElement$1 = null));\n}\nfunction handlePropertyChange(nativeEvent) {\n if (\n \"value\" === nativeEvent.propertyName &&\n getInstIfValueChanged(activeElementInst$1)\n ) {\n var dispatchQueue = [];\n createAndAccumulateChangeEvent(\n dispatchQueue,\n activeElementInst$1,\n nativeEvent,\n getEventTarget(nativeEvent)\n );\n batchedUpdates$1(runEventInBatch, dispatchQueue);\n }\n}\nfunction handleEventsForInputEventPolyfill(domEventName, target, targetInst) {\n \"focusin\" === domEventName\n ? (stopWatchingForValueChange(),\n (activeElement$1 = target),\n (activeElementInst$1 = targetInst),\n activeElement$1.attachEvent(\"onpropertychange\", handlePropertyChange))\n : \"focusout\" === domEventName && stopWatchingForValueChange();\n}\nfunction getTargetInstForInputEventPolyfill(domEventName) {\n if (\n \"selectionchange\" === domEventName ||\n \"keyup\" === domEventName ||\n \"keydown\" === domEventName\n )\n return getInstIfValueChanged(activeElementInst$1);\n}\nfunction getTargetInstForClickEvent(domEventName, targetInst) {\n if (\"click\" === domEventName) return getInstIfValueChanged(targetInst);\n}\nfunction getTargetInstForInputOrChangeEvent(domEventName, targetInst) {\n if (\"input\" === domEventName || \"change\" === domEventName)\n return getInstIfValueChanged(targetInst);\n}\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is;\nfunction shallowEqual(objA, objB) {\n if (objectIs(objA, objB)) return !0;\n if (\n \"object\" !== typeof objA ||\n null === objA ||\n \"object\" !== typeof objB ||\n null === objB\n )\n return !1;\n var keysA = Object.keys(objA),\n keysB = Object.keys(objB);\n if (keysA.length !== keysB.length) return !1;\n for (keysB = 0; keysB < keysA.length; keysB++) {\n var currentKey = keysA[keysB];\n if (\n !hasOwnProperty.call(objB, currentKey) ||\n !objectIs(objA[currentKey], objB[currentKey])\n )\n return !1;\n }\n return !0;\n}\nfunction getLeafNode(node) {\n for (; node && node.firstChild; ) node = node.firstChild;\n return node;\n}\nfunction getNodeForCharacterOffset(root, offset) {\n var node = getLeafNode(root);\n root = 0;\n for (var nodeEnd; node; ) {\n if (3 === node.nodeType) {\n nodeEnd = root + node.textContent.length;\n if (root <= offset && nodeEnd >= offset)\n return { node: node, offset: offset - root };\n root = nodeEnd;\n }\n a: {\n for (; node; ) {\n if (node.nextSibling) {\n node = node.nextSibling;\n break a;\n }\n node = node.parentNode;\n }\n node = void 0;\n }\n node = getLeafNode(node);\n }\n}\nfunction containsNode(outerNode, innerNode) {\n return outerNode && innerNode\n ? outerNode === innerNode\n ? !0\n : outerNode && 3 === outerNode.nodeType\n ? !1\n : innerNode && 3 === innerNode.nodeType\n ? containsNode(outerNode, innerNode.parentNode)\n : \"contains\" in outerNode\n ? outerNode.contains(innerNode)\n : outerNode.compareDocumentPosition\n ? !!(outerNode.compareDocumentPosition(innerNode) & 16)\n : !1\n : !1;\n}\nfunction getActiveElementDeep(containerInfo) {\n containerInfo =\n null != containerInfo &&\n null != containerInfo.ownerDocument &&\n null != containerInfo.ownerDocument.defaultView\n ? containerInfo.ownerDocument.defaultView\n : window;\n for (\n var element = getActiveElement(containerInfo.document);\n element instanceof containerInfo.HTMLIFrameElement;\n\n ) {\n try {\n var JSCompiler_inline_result =\n \"string\" === typeof element.contentWindow.location.href;\n } catch (err) {\n JSCompiler_inline_result = !1;\n }\n if (JSCompiler_inline_result) containerInfo = element.contentWindow;\n else break;\n element = getActiveElement(containerInfo.document);\n }\n return element;\n}\nfunction hasSelectionCapabilities(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return (\n nodeName &&\n ((\"input\" === nodeName &&\n (\"text\" === elem.type ||\n \"search\" === elem.type ||\n \"tel\" === elem.type ||\n \"url\" === elem.type ||\n \"password\" === elem.type)) ||\n \"textarea\" === nodeName ||\n \"true\" === elem.contentEditable)\n );\n}\nvar skipSelectionChangeEvent =\n canUseDOM && \"documentMode\" in document && 11 >= document.documentMode,\n activeElement = null,\n activeElementInst = null,\n lastSelection = null,\n mouseDown = !1;\nfunction constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {\n var doc =\n nativeEventTarget.window === nativeEventTarget\n ? nativeEventTarget.document\n : 9 === nativeEventTarget.nodeType\n ? nativeEventTarget\n : nativeEventTarget.ownerDocument;\n mouseDown ||\n null == activeElement ||\n activeElement !== getActiveElement(doc) ||\n ((doc = activeElement),\n \"selectionStart\" in doc && hasSelectionCapabilities(doc)\n ? (doc = { start: doc.selectionStart, end: doc.selectionEnd })\n : ((doc = (\n (doc.ownerDocument && doc.ownerDocument.defaultView) ||\n window\n ).getSelection()),\n (doc = {\n anchorNode: doc.anchorNode,\n anchorOffset: doc.anchorOffset,\n focusNode: doc.focusNode,\n focusOffset: doc.focusOffset\n })),\n (lastSelection && shallowEqual(lastSelection, doc)) ||\n ((lastSelection = doc),\n (doc = accumulateTwoPhaseListeners(activeElementInst, \"onSelect\")),\n 0 < doc.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onSelect\",\n \"select\",\n null,\n nativeEvent,\n nativeEventTarget\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: doc }),\n (nativeEvent.target = activeElement))));\n}\nfunction makePrefixMap(styleProp, eventName) {\n var prefixes = {};\n prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();\n prefixes[\"Webkit\" + styleProp] = \"webkit\" + eventName;\n prefixes[\"Moz\" + styleProp] = \"moz\" + eventName;\n return prefixes;\n}\nvar vendorPrefixes = {\n animationend: makePrefixMap(\"Animation\", \"AnimationEnd\"),\n animationiteration: makePrefixMap(\"Animation\", \"AnimationIteration\"),\n animationstart: makePrefixMap(\"Animation\", \"AnimationStart\"),\n transitionrun: makePrefixMap(\"Transition\", \"TransitionRun\"),\n transitionstart: makePrefixMap(\"Transition\", \"TransitionStart\"),\n transitioncancel: makePrefixMap(\"Transition\", \"TransitionCancel\"),\n transitionend: makePrefixMap(\"Transition\", \"TransitionEnd\")\n },\n prefixedEventNames = {},\n style = {};\ncanUseDOM &&\n ((style = document.createElement(\"div\").style),\n \"AnimationEvent\" in window ||\n (delete vendorPrefixes.animationend.animation,\n delete vendorPrefixes.animationiteration.animation,\n delete vendorPrefixes.animationstart.animation),\n \"TransitionEvent\" in window ||\n delete vendorPrefixes.transitionend.transition);\nfunction getVendorPrefixedEventName(eventName) {\n if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];\n if (!vendorPrefixes[eventName]) return eventName;\n var prefixMap = vendorPrefixes[eventName],\n styleProp;\n for (styleProp in prefixMap)\n if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)\n return (prefixedEventNames[eventName] = prefixMap[styleProp]);\n return eventName;\n}\nvar ANIMATION_END = getVendorPrefixedEventName(\"animationend\"),\n ANIMATION_ITERATION = getVendorPrefixedEventName(\"animationiteration\"),\n ANIMATION_START = getVendorPrefixedEventName(\"animationstart\"),\n TRANSITION_RUN = getVendorPrefixedEventName(\"transitionrun\"),\n TRANSITION_START = getVendorPrefixedEventName(\"transitionstart\"),\n TRANSITION_CANCEL = getVendorPrefixedEventName(\"transitioncancel\"),\n TRANSITION_END = getVendorPrefixedEventName(\"transitionend\"),\n topLevelEventsToReactNames = new Map(),\n simpleEventPluginEvents =\n \"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\n \" \"\n );\nsimpleEventPluginEvents.push(\"scrollEnd\");\nfunction registerSimpleEvent(domEventName, reactName) {\n topLevelEventsToReactNames.set(domEventName, reactName);\n registerTwoPhaseEvent(reactName, [domEventName]);\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n concurrentQueues = [],\n concurrentQueuesIndex = 0,\n concurrentlyUpdatedLanes = 0;\nfunction finishQueueingConcurrentUpdates() {\n for (\n var endIndex = concurrentQueuesIndex,\n i = (concurrentlyUpdatedLanes = concurrentQueuesIndex = 0);\n i < endIndex;\n\n ) {\n var fiber = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var queue = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var update = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var lane = concurrentQueues[i];\n concurrentQueues[i++] = null;\n if (null !== queue && null !== update) {\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n }\n 0 !== lane && markUpdateLaneFromFiberToRoot(fiber, update, lane);\n }\n}\nfunction enqueueUpdate$1(fiber, queue, update, lane) {\n concurrentQueues[concurrentQueuesIndex++] = fiber;\n concurrentQueues[concurrentQueuesIndex++] = queue;\n concurrentQueues[concurrentQueuesIndex++] = update;\n concurrentQueues[concurrentQueuesIndex++] = lane;\n concurrentlyUpdatedLanes |= lane;\n fiber.lanes |= lane;\n fiber = fiber.alternate;\n null !== fiber && (fiber.lanes |= lane);\n}\nfunction enqueueConcurrentHookUpdate(fiber, queue, update, lane) {\n enqueueUpdate$1(fiber, queue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction enqueueConcurrentRenderForLane(fiber, lane) {\n enqueueUpdate$1(fiber, null, null, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {\n sourceFiber.lanes |= lane;\n var alternate = sourceFiber.alternate;\n null !== alternate && (alternate.lanes |= lane);\n for (var isHidden = !1, parent = sourceFiber.return; null !== parent; )\n (parent.childLanes |= lane),\n (alternate = parent.alternate),\n null !== alternate && (alternate.childLanes |= lane),\n 22 === parent.tag &&\n ((sourceFiber = parent.stateNode),\n null === sourceFiber || sourceFiber._visibility & 1 || (isHidden = !0)),\n (sourceFiber = parent),\n (parent = parent.return);\n return 3 === sourceFiber.tag\n ? ((parent = sourceFiber.stateNode),\n isHidden &&\n null !== update &&\n ((isHidden = 31 - clz32(lane)),\n (sourceFiber = parent.hiddenUpdates),\n (alternate = sourceFiber[isHidden]),\n null === alternate\n ? (sourceFiber[isHidden] = [update])\n : alternate.push(update),\n (update.lane = lane | 536870912)),\n parent)\n : null;\n}\nfunction getRootForUpdatedFiber(sourceFiber) {\n if (50 < nestedUpdateCount)\n throw (\n ((nestedUpdateCount = 0),\n (rootWithNestedUpdates = null),\n Error(formatProdErrorMessage(185)))\n );\n for (var parent = sourceFiber.return; null !== parent; )\n (sourceFiber = parent), (parent = sourceFiber.return);\n return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;\n}\nvar emptyContextObject = {};\nfunction FiberNode(tag, pendingProps, key, mode) {\n this.tag = tag;\n this.key = key;\n this.sibling =\n this.child =\n this.return =\n this.stateNode =\n this.type =\n this.elementType =\n null;\n this.index = 0;\n this.refCleanup = this.ref = null;\n this.pendingProps = pendingProps;\n this.dependencies =\n this.memoizedState =\n this.updateQueue =\n this.memoizedProps =\n null;\n this.mode = mode;\n this.subtreeFlags = this.flags = 0;\n this.deletions = null;\n this.childLanes = this.lanes = 0;\n this.alternate = null;\n}\nfunction createFiberImplClass(tag, pendingProps, key, mode) {\n return new FiberNode(tag, pendingProps, key, mode);\n}\nfunction shouldConstruct(Component) {\n Component = Component.prototype;\n return !(!Component || !Component.isReactComponent);\n}\nfunction createWorkInProgress(current, pendingProps) {\n var workInProgress = current.alternate;\n null === workInProgress\n ? ((workInProgress = createFiberImplClass(\n current.tag,\n pendingProps,\n current.key,\n current.mode\n )),\n (workInProgress.elementType = current.elementType),\n (workInProgress.type = current.type),\n (workInProgress.stateNode = current.stateNode),\n (workInProgress.alternate = current),\n (current.alternate = workInProgress))\n : ((workInProgress.pendingProps = pendingProps),\n (workInProgress.type = current.type),\n (workInProgress.flags = 0),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null));\n workInProgress.flags = current.flags & 65011712;\n workInProgress.childLanes = current.childLanes;\n workInProgress.lanes = current.lanes;\n workInProgress.child = current.child;\n workInProgress.memoizedProps = current.memoizedProps;\n workInProgress.memoizedState = current.memoizedState;\n workInProgress.updateQueue = current.updateQueue;\n pendingProps = current.dependencies;\n workInProgress.dependencies =\n null === pendingProps\n ? null\n : { lanes: pendingProps.lanes, firstContext: pendingProps.firstContext };\n workInProgress.sibling = current.sibling;\n workInProgress.index = current.index;\n workInProgress.ref = current.ref;\n workInProgress.refCleanup = current.refCleanup;\n return workInProgress;\n}\nfunction resetWorkInProgress(workInProgress, renderLanes) {\n workInProgress.flags &= 65011714;\n var current = workInProgress.alternate;\n null === current\n ? ((workInProgress.childLanes = 0),\n (workInProgress.lanes = renderLanes),\n (workInProgress.child = null),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.memoizedProps = null),\n (workInProgress.memoizedState = null),\n (workInProgress.updateQueue = null),\n (workInProgress.dependencies = null),\n (workInProgress.stateNode = null))\n : ((workInProgress.childLanes = current.childLanes),\n (workInProgress.lanes = current.lanes),\n (workInProgress.child = current.child),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null),\n (workInProgress.memoizedProps = current.memoizedProps),\n (workInProgress.memoizedState = current.memoizedState),\n (workInProgress.updateQueue = current.updateQueue),\n (workInProgress.type = current.type),\n (renderLanes = current.dependencies),\n (workInProgress.dependencies =\n null === renderLanes\n ? null\n : {\n lanes: renderLanes.lanes,\n firstContext: renderLanes.firstContext\n }));\n return workInProgress;\n}\nfunction createFiberFromTypeAndProps(\n type,\n key,\n pendingProps,\n owner,\n mode,\n lanes\n) {\n var fiberTag = 0;\n owner = type;\n if (\"function\" === typeof type) shouldConstruct(type) && (fiberTag = 1);\n else if (\"string\" === typeof type)\n fiberTag = isHostHoistableType(\n type,\n pendingProps,\n contextStackCursor.current\n )\n ? 26\n : \"html\" === type || \"head\" === type || \"body\" === type\n ? 27\n : 5;\n else\n a: switch (type) {\n case REACT_ACTIVITY_TYPE:\n return (\n (type = createFiberImplClass(31, pendingProps, key, mode)),\n (type.elementType = REACT_ACTIVITY_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_FRAGMENT_TYPE:\n return createFiberFromFragment(pendingProps.children, mode, lanes, key);\n case REACT_STRICT_MODE_TYPE:\n fiberTag = 8;\n mode |= 24;\n break;\n case REACT_PROFILER_TYPE:\n return (\n (type = createFiberImplClass(12, pendingProps, key, mode | 2)),\n (type.elementType = REACT_PROFILER_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_TYPE:\n return (\n (type = createFiberImplClass(13, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_LIST_TYPE:\n return (\n (type = createFiberImplClass(19, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_LIST_TYPE),\n (type.lanes = lanes),\n type\n );\n default:\n if (\"object\" === typeof type && null !== type)\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n fiberTag = 10;\n break a;\n case REACT_CONSUMER_TYPE:\n fiberTag = 9;\n break a;\n case REACT_FORWARD_REF_TYPE:\n fiberTag = 11;\n break a;\n case REACT_MEMO_TYPE:\n fiberTag = 14;\n break a;\n case REACT_LAZY_TYPE:\n fiberTag = 16;\n owner = null;\n break a;\n }\n fiberTag = 29;\n pendingProps = Error(\n formatProdErrorMessage(130, null === type ? \"null\" : typeof type, \"\")\n );\n owner = null;\n }\n key = createFiberImplClass(fiberTag, pendingProps, key, mode);\n key.elementType = type;\n key.type = owner;\n key.lanes = lanes;\n return key;\n}\nfunction createFiberFromFragment(elements, mode, lanes, key) {\n elements = createFiberImplClass(7, elements, key, mode);\n elements.lanes = lanes;\n return elements;\n}\nfunction createFiberFromText(content, mode, lanes) {\n content = createFiberImplClass(6, content, null, mode);\n content.lanes = lanes;\n return content;\n}\nfunction createFiberFromDehydratedFragment(dehydratedNode) {\n var fiber = createFiberImplClass(18, null, null, 0);\n fiber.stateNode = dehydratedNode;\n return fiber;\n}\nfunction createFiberFromPortal(portal, mode, lanes) {\n mode = createFiberImplClass(\n 4,\n null !== portal.children ? portal.children : [],\n portal.key,\n mode\n );\n mode.lanes = lanes;\n mode.stateNode = {\n containerInfo: portal.containerInfo,\n pendingChildren: null,\n implementation: portal.implementation\n };\n return mode;\n}\nvar CapturedStacks = new WeakMap();\nfunction createCapturedValueAtFiber(value, source) {\n if (\"object\" === typeof value && null !== value) {\n var existing = CapturedStacks.get(value);\n if (void 0 !== existing) return existing;\n source = {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n CapturedStacks.set(value, source);\n return source;\n }\n return {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n}\nvar forkStack = [],\n forkStackIndex = 0,\n treeForkProvider = null,\n treeForkCount = 0,\n idStack = [],\n idStackIndex = 0,\n treeContextProvider = null,\n treeContextId = 1,\n treeContextOverflow = \"\";\nfunction pushTreeFork(workInProgress, totalChildren) {\n forkStack[forkStackIndex++] = treeForkCount;\n forkStack[forkStackIndex++] = treeForkProvider;\n treeForkProvider = workInProgress;\n treeForkCount = totalChildren;\n}\nfunction pushTreeId(workInProgress, totalChildren, index) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextProvider = workInProgress;\n var baseIdWithLeadingBit = treeContextId;\n workInProgress = treeContextOverflow;\n var baseLength = 32 - clz32(baseIdWithLeadingBit) - 1;\n baseIdWithLeadingBit &= ~(1 << baseLength);\n index += 1;\n var length = 32 - clz32(totalChildren) + baseLength;\n if (30 < length) {\n var numberOfOverflowBits = baseLength - (baseLength % 5);\n length = (\n baseIdWithLeadingBit &\n ((1 << numberOfOverflowBits) - 1)\n ).toString(32);\n baseIdWithLeadingBit >>= numberOfOverflowBits;\n baseLength -= numberOfOverflowBits;\n treeContextId =\n (1 << (32 - clz32(totalChildren) + baseLength)) |\n (index << baseLength) |\n baseIdWithLeadingBit;\n treeContextOverflow = length + workInProgress;\n } else\n (treeContextId =\n (1 << length) | (index << baseLength) | baseIdWithLeadingBit),\n (treeContextOverflow = workInProgress);\n}\nfunction pushMaterializedTreeId(workInProgress) {\n null !== workInProgress.return &&\n (pushTreeFork(workInProgress, 1), pushTreeId(workInProgress, 1, 0));\n}\nfunction popTreeContext(workInProgress) {\n for (; workInProgress === treeForkProvider; )\n (treeForkProvider = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null),\n (treeForkCount = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null);\n for (; workInProgress === treeContextProvider; )\n (treeContextProvider = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextOverflow = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextId = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null);\n}\nfunction restoreSuspendedTreeContext(workInProgress, suspendedContext) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextId = suspendedContext.id;\n treeContextOverflow = suspendedContext.overflow;\n treeContextProvider = workInProgress;\n}\nvar hydrationParentFiber = null,\n nextHydratableInstance = null,\n isHydrating = !1,\n hydrationErrors = null,\n rootOrSingletonContext = !1,\n HydrationMismatchException = Error(formatProdErrorMessage(519));\nfunction throwOnHydrationMismatch(fiber) {\n var error = Error(\n formatProdErrorMessage(\n 418,\n 1 < arguments.length && void 0 !== arguments[1] && arguments[1]\n ? \"text\"\n : \"HTML\",\n \"\"\n )\n );\n queueHydrationError(createCapturedValueAtFiber(error, fiber));\n throw HydrationMismatchException;\n}\nfunction prepareToHydrateHostInstance(fiber) {\n var instance = fiber.stateNode,\n type = fiber.type,\n props = fiber.memoizedProps;\n instance[internalInstanceKey] = fiber;\n instance[internalPropsKey] = props;\n switch (type) {\n case \"dialog\":\n listenToNonDelegatedEvent(\"cancel\", instance);\n listenToNonDelegatedEvent(\"close\", instance);\n break;\n case \"iframe\":\n case \"object\":\n case \"embed\":\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"video\":\n case \"audio\":\n for (type = 0; type < mediaEventTypes.length; type++)\n listenToNonDelegatedEvent(mediaEventTypes[type], instance);\n break;\n case \"source\":\n listenToNonDelegatedEvent(\"error\", instance);\n break;\n case \"img\":\n case \"image\":\n case \"link\":\n listenToNonDelegatedEvent(\"error\", instance);\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"details\":\n listenToNonDelegatedEvent(\"toggle\", instance);\n break;\n case \"input\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n initInput(\n instance,\n props.value,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name,\n !0\n );\n break;\n case \"select\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n break;\n case \"textarea\":\n listenToNonDelegatedEvent(\"invalid\", instance),\n initTextarea(instance, props.value, props.defaultValue, props.children);\n }\n type = props.children;\n (\"string\" !== typeof type &&\n \"number\" !== typeof type &&\n \"bigint\" !== typeof type) ||\n instance.textContent === \"\" + type ||\n !0 === props.suppressHydrationWarning ||\n checkForUnmatchedText(instance.textContent, type)\n ? (null != props.popover &&\n (listenToNonDelegatedEvent(\"beforetoggle\", instance),\n listenToNonDelegatedEvent(\"toggle\", instance)),\n null != props.onScroll && listenToNonDelegatedEvent(\"scroll\", instance),\n null != props.onScrollEnd &&\n listenToNonDelegatedEvent(\"scrollend\", instance),\n null != props.onClick && (instance.onclick = noop$1),\n (instance = !0))\n : (instance = !1);\n instance || throwOnHydrationMismatch(fiber, !0);\n}\nfunction popToNextHostParent(fiber) {\n for (hydrationParentFiber = fiber.return; hydrationParentFiber; )\n switch (hydrationParentFiber.tag) {\n case 5:\n case 31:\n case 13:\n rootOrSingletonContext = !1;\n return;\n case 27:\n case 3:\n rootOrSingletonContext = !0;\n return;\n default:\n hydrationParentFiber = hydrationParentFiber.return;\n }\n}\nfunction popHydrationState(fiber) {\n if (fiber !== hydrationParentFiber) return !1;\n if (!isHydrating) return popToNextHostParent(fiber), (isHydrating = !0), !1;\n var tag = fiber.tag,\n JSCompiler_temp;\n if ((JSCompiler_temp = 3 !== tag && 27 !== tag)) {\n if ((JSCompiler_temp = 5 === tag))\n (JSCompiler_temp = fiber.type),\n (JSCompiler_temp =\n !(\"form\" !== JSCompiler_temp && \"button\" !== JSCompiler_temp) ||\n shouldSetTextContent(fiber.type, fiber.memoizedProps));\n JSCompiler_temp = !JSCompiler_temp;\n }\n JSCompiler_temp && nextHydratableInstance && throwOnHydrationMismatch(fiber);\n popToNextHostParent(fiber);\n if (13 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else if (31 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else\n 27 === tag\n ? ((tag = nextHydratableInstance),\n isSingletonScope(fiber.type)\n ? ((fiber = previousHydratableOnEnteringScopedSingleton),\n (previousHydratableOnEnteringScopedSingleton = null),\n (nextHydratableInstance = fiber))\n : (nextHydratableInstance = tag))\n : (nextHydratableInstance = hydrationParentFiber\n ? getNextHydratable(fiber.stateNode.nextSibling)\n : null);\n return !0;\n}\nfunction resetHydrationState() {\n nextHydratableInstance = hydrationParentFiber = null;\n isHydrating = !1;\n}\nfunction upgradeHydrationErrorsToRecoverable() {\n var queuedErrors = hydrationErrors;\n null !== queuedErrors &&\n (null === workInProgressRootRecoverableErrors\n ? (workInProgressRootRecoverableErrors = queuedErrors)\n : workInProgressRootRecoverableErrors.push.apply(\n workInProgressRootRecoverableErrors,\n queuedErrors\n ),\n (hydrationErrors = null));\n return queuedErrors;\n}\nfunction queueHydrationError(error) {\n null === hydrationErrors\n ? (hydrationErrors = [error])\n : hydrationErrors.push(error);\n}\nvar valueCursor = createCursor(null),\n currentlyRenderingFiber$1 = null,\n lastContextDependency = null;\nfunction pushProvider(providerFiber, context, nextValue) {\n push(valueCursor, context._currentValue);\n context._currentValue = nextValue;\n}\nfunction popProvider(context) {\n context._currentValue = valueCursor.current;\n pop(valueCursor);\n}\nfunction scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {\n for (; null !== parent; ) {\n var alternate = parent.alternate;\n (parent.childLanes & renderLanes) !== renderLanes\n ? ((parent.childLanes |= renderLanes),\n null !== alternate && (alternate.childLanes |= renderLanes))\n : null !== alternate &&\n (alternate.childLanes & renderLanes) !== renderLanes &&\n (alternate.childLanes |= renderLanes);\n if (parent === propagationRoot) break;\n parent = parent.return;\n }\n}\nfunction propagateContextChanges(\n workInProgress,\n contexts,\n renderLanes,\n forcePropagateEntireTree\n) {\n var fiber = workInProgress.child;\n null !== fiber && (fiber.return = workInProgress);\n for (; null !== fiber; ) {\n var list = fiber.dependencies;\n if (null !== list) {\n var nextFiber = fiber.child;\n list = list.firstContext;\n a: for (; null !== list; ) {\n var dependency = list;\n list = fiber;\n for (var i = 0; i < contexts.length; i++)\n if (dependency.context === contexts[i]) {\n list.lanes |= renderLanes;\n dependency = list.alternate;\n null !== dependency && (dependency.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(\n list.return,\n renderLanes,\n workInProgress\n );\n forcePropagateEntireTree || (nextFiber = null);\n break a;\n }\n list = dependency.next;\n }\n } else if (18 === fiber.tag) {\n nextFiber = fiber.return;\n if (null === nextFiber) throw Error(formatProdErrorMessage(341));\n nextFiber.lanes |= renderLanes;\n list = nextFiber.alternate;\n null !== list && (list.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(nextFiber, renderLanes, workInProgress);\n nextFiber = null;\n } else nextFiber = fiber.child;\n if (null !== nextFiber) nextFiber.return = fiber;\n else\n for (nextFiber = fiber; null !== nextFiber; ) {\n if (nextFiber === workInProgress) {\n nextFiber = null;\n break;\n }\n fiber = nextFiber.sibling;\n if (null !== fiber) {\n fiber.return = nextFiber.return;\n nextFiber = fiber;\n break;\n }\n nextFiber = nextFiber.return;\n }\n fiber = nextFiber;\n }\n}\nfunction propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n forcePropagateEntireTree\n) {\n current = null;\n for (\n var parent = workInProgress, isInsidePropagationBailout = !1;\n null !== parent;\n\n ) {\n if (!isInsidePropagationBailout)\n if (0 !== (parent.flags & 524288)) isInsidePropagationBailout = !0;\n else if (0 !== (parent.flags & 262144)) break;\n if (10 === parent.tag) {\n var currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent = currentParent.memoizedProps;\n if (null !== currentParent) {\n var context = parent.type;\n objectIs(parent.pendingProps.value, currentParent.value) ||\n (null !== current ? current.push(context) : (current = [context]));\n }\n } else if (parent === hostTransitionProviderCursor.current) {\n currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent.memoizedState.memoizedState !==\n parent.memoizedState.memoizedState &&\n (null !== current\n ? current.push(HostTransitionContext)\n : (current = [HostTransitionContext]));\n }\n parent = parent.return;\n }\n null !== current &&\n propagateContextChanges(\n workInProgress,\n current,\n renderLanes,\n forcePropagateEntireTree\n );\n workInProgress.flags |= 262144;\n}\nfunction checkIfContextChanged(currentDependencies) {\n for (\n currentDependencies = currentDependencies.firstContext;\n null !== currentDependencies;\n\n ) {\n if (\n !objectIs(\n currentDependencies.context._currentValue,\n currentDependencies.memoizedValue\n )\n )\n return !0;\n currentDependencies = currentDependencies.next;\n }\n return !1;\n}\nfunction prepareToReadContext(workInProgress) {\n currentlyRenderingFiber$1 = workInProgress;\n lastContextDependency = null;\n workInProgress = workInProgress.dependencies;\n null !== workInProgress && (workInProgress.firstContext = null);\n}\nfunction readContext(context) {\n return readContextForConsumer(currentlyRenderingFiber$1, context);\n}\nfunction readContextDuringReconciliation(consumer, context) {\n null === currentlyRenderingFiber$1 && prepareToReadContext(consumer);\n return readContextForConsumer(consumer, context);\n}\nfunction readContextForConsumer(consumer, context) {\n var value = context._currentValue;\n context = { context: context, memoizedValue: value, next: null };\n if (null === lastContextDependency) {\n if (null === consumer) throw Error(formatProdErrorMessage(308));\n lastContextDependency = context;\n consumer.dependencies = { lanes: 0, firstContext: context };\n consumer.flags |= 524288;\n } else lastContextDependency = lastContextDependency.next = context;\n return value;\n}\nvar AbortControllerLocal =\n \"undefined\" !== typeof AbortController\n ? AbortController\n : function () {\n var listeners = [],\n signal = (this.signal = {\n aborted: !1,\n addEventListener: function (type, listener) {\n listeners.push(listener);\n }\n });\n this.abort = function () {\n signal.aborted = !0;\n listeners.forEach(function (listener) {\n return listener();\n });\n };\n },\n scheduleCallback$2 = Scheduler.unstable_scheduleCallback,\n NormalPriority = Scheduler.unstable_NormalPriority,\n CacheContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n Consumer: null,\n Provider: null,\n _currentValue: null,\n _currentValue2: null,\n _threadCount: 0\n };\nfunction createCache() {\n return {\n controller: new AbortControllerLocal(),\n data: new Map(),\n refCount: 0\n };\n}\nfunction releaseCache(cache) {\n cache.refCount--;\n 0 === cache.refCount &&\n scheduleCallback$2(NormalPriority, function () {\n cache.controller.abort();\n });\n}\nvar currentEntangledListeners = null,\n currentEntangledPendingCount = 0,\n currentEntangledLane = 0,\n currentEntangledActionThenable = null;\nfunction entangleAsyncAction(transition, thenable) {\n if (null === currentEntangledListeners) {\n var entangledListeners = (currentEntangledListeners = []);\n currentEntangledPendingCount = 0;\n currentEntangledLane = requestTransitionLane();\n currentEntangledActionThenable = {\n status: \"pending\",\n value: void 0,\n then: function (resolve) {\n entangledListeners.push(resolve);\n }\n };\n }\n currentEntangledPendingCount++;\n thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);\n return thenable;\n}\nfunction pingEngtangledActionScope() {\n if (\n 0 === --currentEntangledPendingCount &&\n null !== currentEntangledListeners\n ) {\n null !== currentEntangledActionThenable &&\n (currentEntangledActionThenable.status = \"fulfilled\");\n var listeners = currentEntangledListeners;\n currentEntangledListeners = null;\n currentEntangledLane = 0;\n currentEntangledActionThenable = null;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])();\n }\n}\nfunction chainThenableValue(thenable, result) {\n var listeners = [],\n thenableWithOverride = {\n status: \"pending\",\n value: null,\n reason: null,\n then: function (resolve) {\n listeners.push(resolve);\n }\n };\n thenable.then(\n function () {\n thenableWithOverride.status = \"fulfilled\";\n thenableWithOverride.value = result;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])(result);\n },\n function (error) {\n thenableWithOverride.status = \"rejected\";\n thenableWithOverride.reason = error;\n for (error = 0; error < listeners.length; error++)\n (0, listeners[error])(void 0);\n }\n );\n return thenableWithOverride;\n}\nvar prevOnStartTransitionFinish = ReactSharedInternals.S;\nReactSharedInternals.S = function (transition, returnValue) {\n globalMostRecentTransitionTime = now();\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n entangleAsyncAction(transition, returnValue);\n null !== prevOnStartTransitionFinish &&\n prevOnStartTransitionFinish(transition, returnValue);\n};\nvar resumedCache = createCursor(null);\nfunction peekCacheFromPool() {\n var cacheResumedFromPreviousRender = resumedCache.current;\n return null !== cacheResumedFromPreviousRender\n ? cacheResumedFromPreviousRender\n : workInProgressRoot.pooledCache;\n}\nfunction pushTransition(offscreenWorkInProgress, prevCachePool) {\n null === prevCachePool\n ? push(resumedCache, resumedCache.current)\n : push(resumedCache, prevCachePool.pool);\n}\nfunction getSuspendedCache() {\n var cacheFromPool = peekCacheFromPool();\n return null === cacheFromPool\n ? null\n : { parent: CacheContext._currentValue, pool: cacheFromPool };\n}\nvar SuspenseException = Error(formatProdErrorMessage(460)),\n SuspenseyCommitException = Error(formatProdErrorMessage(474)),\n SuspenseActionException = Error(formatProdErrorMessage(542)),\n noopSuspenseyCommitThenable = { then: function () {} };\nfunction isThenableResolved(thenable) {\n thenable = thenable.status;\n return \"fulfilled\" === thenable || \"rejected\" === thenable;\n}\nfunction trackUsedThenable(thenableState, thenable, index) {\n index = thenableState[index];\n void 0 === index\n ? thenableState.push(thenable)\n : index !== thenable && (thenable.then(noop$1, noop$1), (thenable = index));\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n default:\n if (\"string\" === typeof thenable.status) thenable.then(noop$1, noop$1);\n else {\n thenableState = workInProgressRoot;\n if (null !== thenableState && 100 < thenableState.shellSuspendCounter)\n throw Error(formatProdErrorMessage(482));\n thenableState = thenable;\n thenableState.status = \"pending\";\n thenableState.then(\n function (fulfilledValue) {\n if (\"pending\" === thenable.status) {\n var fulfilledThenable = thenable;\n fulfilledThenable.status = \"fulfilled\";\n fulfilledThenable.value = fulfilledValue;\n }\n },\n function (error) {\n if (\"pending\" === thenable.status) {\n var rejectedThenable = thenable;\n rejectedThenable.status = \"rejected\";\n rejectedThenable.reason = error;\n }\n }\n );\n }\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n }\n suspendedThenable = thenable;\n throw SuspenseException;\n }\n}\nfunction resolveLazy(lazyType) {\n try {\n var init = lazyType._init;\n return init(lazyType._payload);\n } catch (x) {\n if (null !== x && \"object\" === typeof x && \"function\" === typeof x.then)\n throw ((suspendedThenable = x), SuspenseException);\n throw x;\n }\n}\nvar suspendedThenable = null;\nfunction getSuspendedThenable() {\n if (null === suspendedThenable) throw Error(formatProdErrorMessage(459));\n var thenable = suspendedThenable;\n suspendedThenable = null;\n return thenable;\n}\nfunction checkIfUseWrappedInAsyncCatch(rejectedReason) {\n if (\n rejectedReason === SuspenseException ||\n rejectedReason === SuspenseActionException\n )\n throw Error(formatProdErrorMessage(483));\n}\nvar thenableState$1 = null,\n thenableIndexCounter$1 = 0;\nfunction unwrapThenable(thenable) {\n var index = thenableIndexCounter$1;\n thenableIndexCounter$1 += 1;\n null === thenableState$1 && (thenableState$1 = []);\n return trackUsedThenable(thenableState$1, thenable, index);\n}\nfunction coerceRef(workInProgress, element) {\n element = element.props.ref;\n workInProgress.ref = void 0 !== element ? element : null;\n}\nfunction throwOnInvalidObjectTypeImpl(returnFiber, newChild) {\n if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE)\n throw Error(formatProdErrorMessage(525));\n returnFiber = Object.prototype.toString.call(newChild);\n throw Error(\n formatProdErrorMessage(\n 31,\n \"[object Object]\" === returnFiber\n ? \"object with keys {\" + Object.keys(newChild).join(\", \") + \"}\"\n : returnFiber\n )\n );\n}\nfunction createChildReconciler(shouldTrackSideEffects) {\n function deleteChild(returnFiber, childToDelete) {\n if (shouldTrackSideEffects) {\n var deletions = returnFiber.deletions;\n null === deletions\n ? ((returnFiber.deletions = [childToDelete]), (returnFiber.flags |= 16))\n : deletions.push(childToDelete);\n }\n }\n function deleteRemainingChildren(returnFiber, currentFirstChild) {\n if (!shouldTrackSideEffects) return null;\n for (; null !== currentFirstChild; )\n deleteChild(returnFiber, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return null;\n }\n function mapRemainingChildren(currentFirstChild) {\n for (var existingChildren = new Map(); null !== currentFirstChild; )\n null !== currentFirstChild.key\n ? existingChildren.set(currentFirstChild.key, currentFirstChild)\n : existingChildren.set(currentFirstChild.index, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return existingChildren;\n }\n function useFiber(fiber, pendingProps) {\n fiber = createWorkInProgress(fiber, pendingProps);\n fiber.index = 0;\n fiber.sibling = null;\n return fiber;\n }\n function placeChild(newFiber, lastPlacedIndex, newIndex) {\n newFiber.index = newIndex;\n if (!shouldTrackSideEffects)\n return (newFiber.flags |= 1048576), lastPlacedIndex;\n newIndex = newFiber.alternate;\n if (null !== newIndex)\n return (\n (newIndex = newIndex.index),\n newIndex < lastPlacedIndex\n ? ((newFiber.flags |= 67108866), lastPlacedIndex)\n : newIndex\n );\n newFiber.flags |= 67108866;\n return lastPlacedIndex;\n }\n function placeSingleChild(newFiber) {\n shouldTrackSideEffects &&\n null === newFiber.alternate &&\n (newFiber.flags |= 67108866);\n return newFiber;\n }\n function updateTextNode(returnFiber, current, textContent, lanes) {\n if (null === current || 6 !== current.tag)\n return (\n (current = createFiberFromText(textContent, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, textContent);\n current.return = returnFiber;\n return current;\n }\n function updateElement(returnFiber, current, element, lanes) {\n var elementType = element.type;\n if (elementType === REACT_FRAGMENT_TYPE)\n return updateFragment(\n returnFiber,\n current,\n element.props.children,\n lanes,\n element.key\n );\n if (\n null !== current &&\n (current.elementType === elementType ||\n (\"object\" === typeof elementType &&\n null !== elementType &&\n elementType.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(elementType) === current.type))\n )\n return (\n (current = useFiber(current, element.props)),\n coerceRef(current, element),\n (current.return = returnFiber),\n current\n );\n current = createFiberFromTypeAndProps(\n element.type,\n element.key,\n element.props,\n null,\n returnFiber.mode,\n lanes\n );\n coerceRef(current, element);\n current.return = returnFiber;\n return current;\n }\n function updatePortal(returnFiber, current, portal, lanes) {\n if (\n null === current ||\n 4 !== current.tag ||\n current.stateNode.containerInfo !== portal.containerInfo ||\n current.stateNode.implementation !== portal.implementation\n )\n return (\n (current = createFiberFromPortal(portal, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, portal.children || []);\n current.return = returnFiber;\n return current;\n }\n function updateFragment(returnFiber, current, fragment, lanes, key) {\n if (null === current || 7 !== current.tag)\n return (\n (current = createFiberFromFragment(\n fragment,\n returnFiber.mode,\n lanes,\n key\n )),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, fragment);\n current.return = returnFiber;\n return current;\n }\n function createChild(returnFiber, newChild, lanes) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (newChild = createFiberFromText(\n \"\" + newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n lanes\n );\n case REACT_PORTAL_TYPE:\n return (\n (newChild = createFiberFromPortal(\n newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n createChild(returnFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (newChild = createFiberFromFragment(\n newChild,\n returnFiber.mode,\n lanes,\n null\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"function\" === typeof newChild.then)\n return createChild(returnFiber, unwrapThenable(newChild), lanes);\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return createChild(\n returnFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateSlot(returnFiber, oldFiber, newChild, lanes) {\n var key = null !== oldFiber ? oldFiber.key : null;\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return null !== key\n ? null\n : updateTextNode(returnFiber, oldFiber, \"\" + newChild, lanes);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return newChild.key === key\n ? updateElement(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_PORTAL_TYPE:\n return newChild.key === key\n ? updatePortal(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateSlot(returnFiber, oldFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return null !== key\n ? null\n : updateFragment(returnFiber, oldFiber, newChild, lanes, null);\n if (\"function\" === typeof newChild.then)\n return updateSlot(\n returnFiber,\n oldFiber,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateSlot(\n returnFiber,\n oldFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n ) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateTextNode(returnFiber, existingChildren, \"\" + newChild, lanes)\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updateElement(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_PORTAL_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updatePortal(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateFragment(returnFiber, existingChildren, newChild, lanes, null)\n );\n if (\"function\" === typeof newChild.then)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null;\n null !== oldFiber && newIdx < newChildren.length;\n newIdx++\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(\n returnFiber,\n oldFiber,\n newChildren[newIdx],\n lanes\n );\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (newIdx === newChildren.length)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; newIdx < newChildren.length; newIdx++)\n (oldFiber = createChild(returnFiber, newChildren[newIdx], lanes)),\n null !== oldFiber &&\n ((currentFirstChild = placeChild(\n oldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = oldFiber)\n : (previousNewFiber.sibling = oldFiber),\n (previousNewFiber = oldFiber));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n newIdx < newChildren.length;\n newIdx++\n )\n (nextOldFiber = updateFromMap(\n oldFiber,\n returnFiber,\n newIdx,\n newChildren[newIdx],\n lanes\n )),\n null !== nextOldFiber &&\n (shouldTrackSideEffects &&\n null !== nextOldFiber.alternate &&\n oldFiber.delete(\n null === nextOldFiber.key ? newIdx : nextOldFiber.key\n ),\n (currentFirstChild = placeChild(\n nextOldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = nextOldFiber)\n : (previousNewFiber.sibling = nextOldFiber),\n (previousNewFiber = nextOldFiber));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n if (null == newChildren) throw Error(formatProdErrorMessage(151));\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null,\n step = newChildren.next();\n null !== oldFiber && !step.done;\n newIdx++, step = newChildren.next()\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (step.done)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; !step.done; newIdx++, step = newChildren.next())\n (step = createChild(returnFiber, step.value, lanes)),\n null !== step &&\n ((currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n !step.done;\n newIdx++, step = newChildren.next()\n )\n (step = updateFromMap(oldFiber, returnFiber, newIdx, step.value, lanes)),\n null !== step &&\n (shouldTrackSideEffects &&\n null !== step.alternate &&\n oldFiber.delete(null === step.key ? newIdx : step.key),\n (currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n ) {\n \"object\" === typeof newChild &&\n null !== newChild &&\n newChild.type === REACT_FRAGMENT_TYPE &&\n null === newChild.key &&\n (newChild = newChild.props.children);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n a: {\n for (var key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key) {\n key = newChild.type;\n if (key === REACT_FRAGMENT_TYPE) {\n if (7 === currentFirstChild.tag) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(\n currentFirstChild,\n newChild.props.children\n );\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n } else if (\n currentFirstChild.elementType === key ||\n (\"object\" === typeof key &&\n null !== key &&\n key.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(key) === currentFirstChild.type)\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.props);\n coerceRef(lanes, newChild);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n } else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n newChild.type === REACT_FRAGMENT_TYPE\n ? ((lanes = createFiberFromFragment(\n newChild.props.children,\n returnFiber.mode,\n lanes,\n newChild.key\n )),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : ((lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n (returnFiber = lanes));\n }\n return placeSingleChild(returnFiber);\n case REACT_PORTAL_TYPE:\n a: {\n for (key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key)\n if (\n 4 === currentFirstChild.tag &&\n currentFirstChild.stateNode.containerInfo ===\n newChild.containerInfo &&\n currentFirstChild.stateNode.implementation ===\n newChild.implementation\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.children || []);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n } else {\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n }\n else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n lanes = createFiberFromPortal(newChild, returnFiber.mode, lanes);\n lanes.return = returnFiber;\n returnFiber = lanes;\n }\n return placeSingleChild(returnFiber);\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild))\n return reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n if (getIteratorFn(newChild)) {\n key = getIteratorFn(newChild);\n if (\"function\" !== typeof key) throw Error(formatProdErrorMessage(150));\n newChild = key.call(newChild);\n return reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n }\n if (\"function\" === typeof newChild.then)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n ? ((newChild = \"\" + newChild),\n null !== currentFirstChild && 6 === currentFirstChild.tag\n ? (deleteRemainingChildren(returnFiber, currentFirstChild.sibling),\n (lanes = useFiber(currentFirstChild, newChild)),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : (deleteRemainingChildren(returnFiber, currentFirstChild),\n (lanes = createFiberFromText(newChild, returnFiber.mode, lanes)),\n (lanes.return = returnFiber),\n (returnFiber = lanes)),\n placeSingleChild(returnFiber))\n : deleteRemainingChildren(returnFiber, currentFirstChild);\n }\n return function (returnFiber, currentFirstChild, newChild, lanes) {\n try {\n thenableIndexCounter$1 = 0;\n var firstChildFiber = reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n thenableState$1 = null;\n return firstChildFiber;\n } catch (x) {\n if (x === SuspenseException || x === SuspenseActionException) throw x;\n var fiber = createFiberImplClass(29, x, null, returnFiber.mode);\n fiber.lanes = lanes;\n fiber.return = returnFiber;\n return fiber;\n } finally {\n }\n };\n}\nvar reconcileChildFibers = createChildReconciler(!0),\n mountChildFibers = createChildReconciler(!1),\n hasForceUpdate = !1;\nfunction initializeUpdateQueue(fiber) {\n fiber.updateQueue = {\n baseState: fiber.memoizedState,\n firstBaseUpdate: null,\n lastBaseUpdate: null,\n shared: { pending: null, lanes: 0, hiddenCallbacks: null },\n callbacks: null\n };\n}\nfunction cloneUpdateQueue(current, workInProgress) {\n current = current.updateQueue;\n workInProgress.updateQueue === current &&\n (workInProgress.updateQueue = {\n baseState: current.baseState,\n firstBaseUpdate: current.firstBaseUpdate,\n lastBaseUpdate: current.lastBaseUpdate,\n shared: current.shared,\n callbacks: null\n });\n}\nfunction createUpdate(lane) {\n return { lane: lane, tag: 0, payload: null, callback: null, next: null };\n}\nfunction enqueueUpdate(fiber, update, lane) {\n var updateQueue = fiber.updateQueue;\n if (null === updateQueue) return null;\n updateQueue = updateQueue.shared;\n if (0 !== (executionContext & 2)) {\n var pending = updateQueue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n updateQueue.pending = update;\n update = getRootForUpdatedFiber(fiber);\n markUpdateLaneFromFiberToRoot(fiber, null, lane);\n return update;\n }\n enqueueUpdate$1(fiber, updateQueue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction entangleTransitions(root, fiber, lane) {\n fiber = fiber.updateQueue;\n if (null !== fiber && ((fiber = fiber.shared), 0 !== (lane & 4194048))) {\n var queueLanes = fiber.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n fiber.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nfunction enqueueCapturedUpdate(workInProgress, capturedUpdate) {\n var queue = workInProgress.updateQueue,\n current = workInProgress.alternate;\n if (\n null !== current &&\n ((current = current.updateQueue), queue === current)\n ) {\n var newFirst = null,\n newLast = null;\n queue = queue.firstBaseUpdate;\n if (null !== queue) {\n do {\n var clone = {\n lane: queue.lane,\n tag: queue.tag,\n payload: queue.payload,\n callback: null,\n next: null\n };\n null === newLast\n ? (newFirst = newLast = clone)\n : (newLast = newLast.next = clone);\n queue = queue.next;\n } while (null !== queue);\n null === newLast\n ? (newFirst = newLast = capturedUpdate)\n : (newLast = newLast.next = capturedUpdate);\n } else newFirst = newLast = capturedUpdate;\n queue = {\n baseState: current.baseState,\n firstBaseUpdate: newFirst,\n lastBaseUpdate: newLast,\n shared: current.shared,\n callbacks: current.callbacks\n };\n workInProgress.updateQueue = queue;\n return;\n }\n workInProgress = queue.lastBaseUpdate;\n null === workInProgress\n ? (queue.firstBaseUpdate = capturedUpdate)\n : (workInProgress.next = capturedUpdate);\n queue.lastBaseUpdate = capturedUpdate;\n}\nvar didReadFromEntangledAsyncAction = !1;\nfunction suspendIfUpdateReadFromEntangledAsyncAction() {\n if (didReadFromEntangledAsyncAction) {\n var entangledActionThenable = currentEntangledActionThenable;\n if (null !== entangledActionThenable) throw entangledActionThenable;\n }\n}\nfunction processUpdateQueue(\n workInProgress$jscomp$0,\n props,\n instance$jscomp$0,\n renderLanes\n) {\n didReadFromEntangledAsyncAction = !1;\n var queue = workInProgress$jscomp$0.updateQueue;\n hasForceUpdate = !1;\n var firstBaseUpdate = queue.firstBaseUpdate,\n lastBaseUpdate = queue.lastBaseUpdate,\n pendingQueue = queue.shared.pending;\n if (null !== pendingQueue) {\n queue.shared.pending = null;\n var lastPendingUpdate = pendingQueue,\n firstPendingUpdate = lastPendingUpdate.next;\n lastPendingUpdate.next = null;\n null === lastBaseUpdate\n ? (firstBaseUpdate = firstPendingUpdate)\n : (lastBaseUpdate.next = firstPendingUpdate);\n lastBaseUpdate = lastPendingUpdate;\n var current = workInProgress$jscomp$0.alternate;\n null !== current &&\n ((current = current.updateQueue),\n (pendingQueue = current.lastBaseUpdate),\n pendingQueue !== lastBaseUpdate &&\n (null === pendingQueue\n ? (current.firstBaseUpdate = firstPendingUpdate)\n : (pendingQueue.next = firstPendingUpdate),\n (current.lastBaseUpdate = lastPendingUpdate)));\n }\n if (null !== firstBaseUpdate) {\n var newState = queue.baseState;\n lastBaseUpdate = 0;\n current = firstPendingUpdate = lastPendingUpdate = null;\n pendingQueue = firstBaseUpdate;\n do {\n var updateLane = pendingQueue.lane & -536870913,\n isHiddenUpdate = updateLane !== pendingQueue.lane;\n if (\n isHiddenUpdate\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n 0 !== updateLane &&\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction = !0);\n null !== current &&\n (current = current.next =\n {\n lane: 0,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: null,\n next: null\n });\n a: {\n var workInProgress = workInProgress$jscomp$0,\n update = pendingQueue;\n updateLane = props;\n var instance = instance$jscomp$0;\n switch (update.tag) {\n case 1:\n workInProgress = update.payload;\n if (\"function\" === typeof workInProgress) {\n newState = workInProgress.call(instance, newState, updateLane);\n break a;\n }\n newState = workInProgress;\n break a;\n case 3:\n workInProgress.flags = (workInProgress.flags & -65537) | 128;\n case 0:\n workInProgress = update.payload;\n updateLane =\n \"function\" === typeof workInProgress\n ? workInProgress.call(instance, newState, updateLane)\n : workInProgress;\n if (null === updateLane || void 0 === updateLane) break a;\n newState = assign({}, newState, updateLane);\n break a;\n case 2:\n hasForceUpdate = !0;\n }\n }\n updateLane = pendingQueue.callback;\n null !== updateLane &&\n ((workInProgress$jscomp$0.flags |= 64),\n isHiddenUpdate && (workInProgress$jscomp$0.flags |= 8192),\n (isHiddenUpdate = queue.callbacks),\n null === isHiddenUpdate\n ? (queue.callbacks = [updateLane])\n : isHiddenUpdate.push(updateLane));\n } else\n (isHiddenUpdate = {\n lane: updateLane,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: pendingQueue.callback,\n next: null\n }),\n null === current\n ? ((firstPendingUpdate = current = isHiddenUpdate),\n (lastPendingUpdate = newState))\n : (current = current.next = isHiddenUpdate),\n (lastBaseUpdate |= updateLane);\n pendingQueue = pendingQueue.next;\n if (null === pendingQueue)\n if (((pendingQueue = queue.shared.pending), null === pendingQueue))\n break;\n else\n (isHiddenUpdate = pendingQueue),\n (pendingQueue = isHiddenUpdate.next),\n (isHiddenUpdate.next = null),\n (queue.lastBaseUpdate = isHiddenUpdate),\n (queue.shared.pending = null);\n } while (1);\n null === current && (lastPendingUpdate = newState);\n queue.baseState = lastPendingUpdate;\n queue.firstBaseUpdate = firstPendingUpdate;\n queue.lastBaseUpdate = current;\n null === firstBaseUpdate && (queue.shared.lanes = 0);\n workInProgressRootSkippedLanes |= lastBaseUpdate;\n workInProgress$jscomp$0.lanes = lastBaseUpdate;\n workInProgress$jscomp$0.memoizedState = newState;\n }\n}\nfunction callCallback(callback, context) {\n if (\"function\" !== typeof callback)\n throw Error(formatProdErrorMessage(191, callback));\n callback.call(context);\n}\nfunction commitCallbacks(updateQueue, context) {\n var callbacks = updateQueue.callbacks;\n if (null !== callbacks)\n for (\n updateQueue.callbacks = null, updateQueue = 0;\n updateQueue < callbacks.length;\n updateQueue++\n )\n callCallback(callbacks[updateQueue], context);\n}\nvar currentTreeHiddenStackCursor = createCursor(null),\n prevEntangledRenderLanesCursor = createCursor(0);\nfunction pushHiddenContext(fiber, context) {\n fiber = entangledRenderLanes;\n push(prevEntangledRenderLanesCursor, fiber);\n push(currentTreeHiddenStackCursor, context);\n entangledRenderLanes = fiber | context.baseLanes;\n}\nfunction reuseHiddenContextOnStack() {\n push(prevEntangledRenderLanesCursor, entangledRenderLanes);\n push(currentTreeHiddenStackCursor, currentTreeHiddenStackCursor.current);\n}\nfunction popHiddenContext() {\n entangledRenderLanes = prevEntangledRenderLanesCursor.current;\n pop(currentTreeHiddenStackCursor);\n pop(prevEntangledRenderLanesCursor);\n}\nvar suspenseHandlerStackCursor = createCursor(null),\n shellBoundary = null;\nfunction pushPrimaryTreeSuspenseHandler(handler) {\n var current = handler.alternate;\n push(suspenseStackCursor, suspenseStackCursor.current & 1);\n push(suspenseHandlerStackCursor, handler);\n null === shellBoundary &&\n (null === current || null !== currentTreeHiddenStackCursor.current\n ? (shellBoundary = handler)\n : null !== current.memoizedState && (shellBoundary = handler));\n}\nfunction pushDehydratedActivitySuspenseHandler(fiber) {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, fiber);\n null === shellBoundary && (shellBoundary = fiber);\n}\nfunction pushOffscreenSuspenseHandler(fiber) {\n 22 === fiber.tag\n ? (push(suspenseStackCursor, suspenseStackCursor.current),\n push(suspenseHandlerStackCursor, fiber),\n null === shellBoundary && (shellBoundary = fiber))\n : reuseSuspenseHandlerOnStack(fiber);\n}\nfunction reuseSuspenseHandlerOnStack() {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, suspenseHandlerStackCursor.current);\n}\nfunction popSuspenseHandler(fiber) {\n pop(suspenseHandlerStackCursor);\n shellBoundary === fiber && (shellBoundary = null);\n pop(suspenseStackCursor);\n}\nvar suspenseStackCursor = createCursor(0);\nfunction findFirstSuspended(row) {\n for (var node = row; null !== node; ) {\n if (13 === node.tag) {\n var state = node.memoizedState;\n if (\n null !== state &&\n ((state = state.dehydrated),\n null === state ||\n isSuspenseInstancePending(state) ||\n isSuspenseInstanceFallback(state))\n )\n return node;\n } else if (\n 19 === node.tag &&\n (\"forwards\" === node.memoizedProps.revealOrder ||\n \"backwards\" === node.memoizedProps.revealOrder ||\n \"unstable_legacy-backwards\" === node.memoizedProps.revealOrder ||\n \"together\" === node.memoizedProps.revealOrder)\n ) {\n if (0 !== (node.flags & 128)) return node;\n } else if (null !== node.child) {\n node.child.return = node;\n node = node.child;\n continue;\n }\n if (node === row) break;\n for (; null === node.sibling; ) {\n if (null === node.return || node.return === row) return null;\n node = node.return;\n }\n node.sibling.return = node.return;\n node = node.sibling;\n }\n return null;\n}\nvar renderLanes = 0,\n currentlyRenderingFiber = null,\n currentHook = null,\n workInProgressHook = null,\n didScheduleRenderPhaseUpdate = !1,\n didScheduleRenderPhaseUpdateDuringThisPass = !1,\n shouldDoubleInvokeUserFnsInHooksDEV = !1,\n localIdCounter = 0,\n thenableIndexCounter = 0,\n thenableState = null,\n globalClientIdCounter = 0;\nfunction throwInvalidHookError() {\n throw Error(formatProdErrorMessage(321));\n}\nfunction areHookInputsEqual(nextDeps, prevDeps) {\n if (null === prevDeps) return !1;\n for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)\n if (!objectIs(nextDeps[i], prevDeps[i])) return !1;\n return !0;\n}\nfunction renderWithHooks(\n current,\n workInProgress,\n Component,\n props,\n secondArg,\n nextRenderLanes\n) {\n renderLanes = nextRenderLanes;\n currentlyRenderingFiber = workInProgress;\n workInProgress.memoizedState = null;\n workInProgress.updateQueue = null;\n workInProgress.lanes = 0;\n ReactSharedInternals.H =\n null === current || null === current.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate;\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n nextRenderLanes = Component(props, secondArg);\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n didScheduleRenderPhaseUpdateDuringThisPass &&\n (nextRenderLanes = renderWithHooksAgain(\n workInProgress,\n Component,\n props,\n secondArg\n ));\n finishRenderingHooks(current);\n return nextRenderLanes;\n}\nfunction finishRenderingHooks(current) {\n ReactSharedInternals.H = ContextOnlyDispatcher;\n var didRenderTooFewHooks = null !== currentHook && null !== currentHook.next;\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdate = !1;\n thenableIndexCounter = 0;\n thenableState = null;\n if (didRenderTooFewHooks) throw Error(formatProdErrorMessage(300));\n null === current ||\n didReceiveUpdate ||\n ((current = current.dependencies),\n null !== current &&\n checkIfContextChanged(current) &&\n (didReceiveUpdate = !0));\n}\nfunction renderWithHooksAgain(workInProgress, Component, props, secondArg) {\n currentlyRenderingFiber = workInProgress;\n var numberOfReRenders = 0;\n do {\n didScheduleRenderPhaseUpdateDuringThisPass && (thenableState = null);\n thenableIndexCounter = 0;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n if (25 <= numberOfReRenders) throw Error(formatProdErrorMessage(301));\n numberOfReRenders += 1;\n workInProgressHook = currentHook = null;\n if (null != workInProgress.updateQueue) {\n var children = workInProgress.updateQueue;\n children.lastEffect = null;\n children.events = null;\n children.stores = null;\n null != children.memoCache && (children.memoCache.index = 0);\n }\n ReactSharedInternals.H = HooksDispatcherOnRerender;\n children = Component(props, secondArg);\n } while (didScheduleRenderPhaseUpdateDuringThisPass);\n return children;\n}\nfunction TransitionAwareHostComponent() {\n var dispatcher = ReactSharedInternals.H,\n maybeThenable = dispatcher.useState()[0];\n maybeThenable =\n \"function\" === typeof maybeThenable.then\n ? useThenable(maybeThenable)\n : maybeThenable;\n dispatcher = dispatcher.useState()[0];\n (null !== currentHook ? currentHook.memoizedState : null) !== dispatcher &&\n (currentlyRenderingFiber.flags |= 1024);\n return maybeThenable;\n}\nfunction checkDidRenderIdHook() {\n var didRenderIdHook = 0 !== localIdCounter;\n localIdCounter = 0;\n return didRenderIdHook;\n}\nfunction bailoutHooks(current, workInProgress, lanes) {\n workInProgress.updateQueue = current.updateQueue;\n workInProgress.flags &= -2053;\n current.lanes &= ~lanes;\n}\nfunction resetHooksOnUnwind(workInProgress) {\n if (didScheduleRenderPhaseUpdate) {\n for (\n workInProgress = workInProgress.memoizedState;\n null !== workInProgress;\n\n ) {\n var queue = workInProgress.queue;\n null !== queue && (queue.pending = null);\n workInProgress = workInProgress.next;\n }\n didScheduleRenderPhaseUpdate = !1;\n }\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n thenableIndexCounter = localIdCounter = 0;\n thenableState = null;\n}\nfunction mountWorkInProgressHook() {\n var hook = {\n memoizedState: null,\n baseState: null,\n baseQueue: null,\n queue: null,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook = hook)\n : (workInProgressHook = workInProgressHook.next = hook);\n return workInProgressHook;\n}\nfunction updateWorkInProgressHook() {\n if (null === currentHook) {\n var nextCurrentHook = currentlyRenderingFiber.alternate;\n nextCurrentHook =\n null !== nextCurrentHook ? nextCurrentHook.memoizedState : null;\n } else nextCurrentHook = currentHook.next;\n var nextWorkInProgressHook =\n null === workInProgressHook\n ? currentlyRenderingFiber.memoizedState\n : workInProgressHook.next;\n if (null !== nextWorkInProgressHook)\n (workInProgressHook = nextWorkInProgressHook),\n (currentHook = nextCurrentHook);\n else {\n if (null === nextCurrentHook) {\n if (null === currentlyRenderingFiber.alternate)\n throw Error(formatProdErrorMessage(467));\n throw Error(formatProdErrorMessage(310));\n }\n currentHook = nextCurrentHook;\n nextCurrentHook = {\n memoizedState: currentHook.memoizedState,\n baseState: currentHook.baseState,\n baseQueue: currentHook.baseQueue,\n queue: currentHook.queue,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook =\n nextCurrentHook)\n : (workInProgressHook = workInProgressHook.next = nextCurrentHook);\n }\n return workInProgressHook;\n}\nfunction createFunctionComponentUpdateQueue() {\n return { lastEffect: null, events: null, stores: null, memoCache: null };\n}\nfunction useThenable(thenable) {\n var index = thenableIndexCounter;\n thenableIndexCounter += 1;\n null === thenableState && (thenableState = []);\n thenable = trackUsedThenable(thenableState, thenable, index);\n index = currentlyRenderingFiber;\n null ===\n (null === workInProgressHook\n ? index.memoizedState\n : workInProgressHook.next) &&\n ((index = index.alternate),\n (ReactSharedInternals.H =\n null === index || null === index.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate));\n return thenable;\n}\nfunction use(usable) {\n if (null !== usable && \"object\" === typeof usable) {\n if (\"function\" === typeof usable.then) return useThenable(usable);\n if (usable.$$typeof === REACT_CONTEXT_TYPE) return readContext(usable);\n }\n throw Error(formatProdErrorMessage(438, String(usable)));\n}\nfunction useMemoCache(size) {\n var memoCache = null,\n updateQueue = currentlyRenderingFiber.updateQueue;\n null !== updateQueue && (memoCache = updateQueue.memoCache);\n if (null == memoCache) {\n var current = currentlyRenderingFiber.alternate;\n null !== current &&\n ((current = current.updateQueue),\n null !== current &&\n ((current = current.memoCache),\n null != current &&\n (memoCache = {\n data: current.data.map(function (array) {\n return array.slice();\n }),\n index: 0\n })));\n }\n null == memoCache && (memoCache = { data: [], index: 0 });\n null === updateQueue &&\n ((updateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = updateQueue));\n updateQueue.memoCache = memoCache;\n updateQueue = memoCache.data[memoCache.index];\n if (void 0 === updateQueue)\n for (\n updateQueue = memoCache.data[memoCache.index] = Array(size), current = 0;\n current < size;\n current++\n )\n updateQueue[current] = REACT_MEMO_CACHE_SENTINEL;\n memoCache.index++;\n return updateQueue;\n}\nfunction basicStateReducer(state, action) {\n return \"function\" === typeof action ? action(state) : action;\n}\nfunction updateReducer(reducer) {\n var hook = updateWorkInProgressHook();\n return updateReducerImpl(hook, currentHook, reducer);\n}\nfunction updateReducerImpl(hook, current, reducer) {\n var queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var baseQueue = hook.baseQueue,\n pendingQueue = queue.pending;\n if (null !== pendingQueue) {\n if (null !== baseQueue) {\n var baseFirst = baseQueue.next;\n baseQueue.next = pendingQueue.next;\n pendingQueue.next = baseFirst;\n }\n current.baseQueue = baseQueue = pendingQueue;\n queue.pending = null;\n }\n pendingQueue = hook.baseState;\n if (null === baseQueue) hook.memoizedState = pendingQueue;\n else {\n current = baseQueue.next;\n var newBaseQueueFirst = (baseFirst = null),\n newBaseQueueLast = null,\n update = current,\n didReadFromEntangledAsyncAction$60 = !1;\n do {\n var updateLane = update.lane & -536870913;\n if (\n updateLane !== update.lane\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n var revertLane = update.revertLane;\n if (0 === revertLane)\n null !== newBaseQueueLast &&\n (newBaseQueueLast = newBaseQueueLast.next =\n {\n lane: 0,\n revertLane: 0,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n else if ((renderLanes & revertLane) === revertLane) {\n update = update.next;\n revertLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n continue;\n } else\n (updateLane = {\n lane: 0,\n revertLane: update.revertLane,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = updateLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = updateLane),\n (currentlyRenderingFiber.lanes |= revertLane),\n (workInProgressRootSkippedLanes |= revertLane);\n updateLane = update.action;\n shouldDoubleInvokeUserFnsInHooksDEV &&\n reducer(pendingQueue, updateLane);\n pendingQueue = update.hasEagerState\n ? update.eagerState\n : reducer(pendingQueue, updateLane);\n } else\n (revertLane = {\n lane: updateLane,\n revertLane: update.revertLane,\n gesture: update.gesture,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = revertLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = revertLane),\n (currentlyRenderingFiber.lanes |= updateLane),\n (workInProgressRootSkippedLanes |= updateLane);\n update = update.next;\n } while (null !== update && update !== current);\n null === newBaseQueueLast\n ? (baseFirst = pendingQueue)\n : (newBaseQueueLast.next = newBaseQueueFirst);\n if (\n !objectIs(pendingQueue, hook.memoizedState) &&\n ((didReceiveUpdate = !0),\n didReadFromEntangledAsyncAction$60 &&\n ((reducer = currentEntangledActionThenable), null !== reducer))\n )\n throw reducer;\n hook.memoizedState = pendingQueue;\n hook.baseState = baseFirst;\n hook.baseQueue = newBaseQueueLast;\n queue.lastRenderedState = pendingQueue;\n }\n null === baseQueue && (queue.lanes = 0);\n return [hook.memoizedState, queue.dispatch];\n}\nfunction rerenderReducer(reducer) {\n var hook = updateWorkInProgressHook(),\n queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var dispatch = queue.dispatch,\n lastRenderPhaseUpdate = queue.pending,\n newState = hook.memoizedState;\n if (null !== lastRenderPhaseUpdate) {\n queue.pending = null;\n var update = (lastRenderPhaseUpdate = lastRenderPhaseUpdate.next);\n do (newState = reducer(newState, update.action)), (update = update.next);\n while (update !== lastRenderPhaseUpdate);\n objectIs(newState, hook.memoizedState) || (didReceiveUpdate = !0);\n hook.memoizedState = newState;\n null === hook.baseQueue && (hook.baseState = newState);\n queue.lastRenderedState = newState;\n }\n return [newState, dispatch];\n}\nfunction updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = updateWorkInProgressHook(),\n isHydrating$jscomp$0 = isHydrating;\n if (isHydrating$jscomp$0) {\n if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else getServerSnapshot = getSnapshot();\n var snapshotChanged = !objectIs(\n (currentHook || hook).memoizedState,\n getServerSnapshot\n );\n snapshotChanged &&\n ((hook.memoizedState = getServerSnapshot), (didReceiveUpdate = !0));\n hook = hook.queue;\n updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [\n subscribe\n ]);\n if (\n hook.getSnapshot !== getSnapshot ||\n snapshotChanged ||\n (null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1)\n ) {\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n hook,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));\n isHydrating$jscomp$0 ||\n 0 !== (renderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n return getServerSnapshot;\n}\nfunction pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {\n fiber.flags |= 16384;\n fiber = { getSnapshot: getSnapshot, value: renderedSnapshot };\n getSnapshot = currentlyRenderingFiber.updateQueue;\n null === getSnapshot\n ? ((getSnapshot = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = getSnapshot),\n (getSnapshot.stores = [fiber]))\n : ((renderedSnapshot = getSnapshot.stores),\n null === renderedSnapshot\n ? (getSnapshot.stores = [fiber])\n : renderedSnapshot.push(fiber));\n}\nfunction updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {\n inst.value = nextSnapshot;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n}\nfunction subscribeToStore(fiber, inst, subscribe) {\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n });\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction forceStoreRerender(fiber) {\n var root = enqueueConcurrentRenderForLane(fiber, 2);\n null !== root && scheduleUpdateOnFiber(root, fiber, 2);\n}\nfunction mountStateImpl(initialState) {\n var hook = mountWorkInProgressHook();\n if (\"function\" === typeof initialState) {\n var initialStateInitializer = initialState;\n initialState = initialStateInitializer();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n initialStateInitializer();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n }\n hook.memoizedState = hook.baseState = initialState;\n hook.queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState\n };\n return hook;\n}\nfunction updateOptimisticImpl(hook, current, passthrough, reducer) {\n hook.baseState = passthrough;\n return updateReducerImpl(\n hook,\n currentHook,\n \"function\" === typeof reducer ? reducer : basicStateReducer\n );\n}\nfunction dispatchActionState(\n fiber,\n actionQueue,\n setPendingState,\n setState,\n payload\n) {\n if (isRenderPhaseUpdate(fiber)) throw Error(formatProdErrorMessage(485));\n fiber = actionQueue.action;\n if (null !== fiber) {\n var actionNode = {\n payload: payload,\n action: fiber,\n next: null,\n isTransition: !0,\n status: \"pending\",\n value: null,\n reason: null,\n listeners: [],\n then: function (listener) {\n actionNode.listeners.push(listener);\n }\n };\n null !== ReactSharedInternals.T\n ? setPendingState(!0)\n : (actionNode.isTransition = !1);\n setState(actionNode);\n setPendingState = actionQueue.pending;\n null === setPendingState\n ? ((actionNode.next = actionQueue.pending = actionNode),\n runActionStateAction(actionQueue, actionNode))\n : ((actionNode.next = setPendingState.next),\n (actionQueue.pending = setPendingState.next = actionNode));\n }\n}\nfunction runActionStateAction(actionQueue, node) {\n var action = node.action,\n payload = node.payload,\n prevState = actionQueue.state;\n if (node.isTransition) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = action(prevState, payload),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n handleActionReturnValue(actionQueue, node, returnValue);\n } catch (error) {\n onActionError(actionQueue, node, error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n } else\n try {\n (prevTransition = action(prevState, payload)),\n handleActionReturnValue(actionQueue, node, prevTransition);\n } catch (error$66) {\n onActionError(actionQueue, node, error$66);\n }\n}\nfunction handleActionReturnValue(actionQueue, node, returnValue) {\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ? returnValue.then(\n function (nextState) {\n onActionSuccess(actionQueue, node, nextState);\n },\n function (error) {\n return onActionError(actionQueue, node, error);\n }\n )\n : onActionSuccess(actionQueue, node, returnValue);\n}\nfunction onActionSuccess(actionQueue, actionNode, nextState) {\n actionNode.status = \"fulfilled\";\n actionNode.value = nextState;\n notifyActionListeners(actionNode);\n actionQueue.state = nextState;\n actionNode = actionQueue.pending;\n null !== actionNode &&\n ((nextState = actionNode.next),\n nextState === actionNode\n ? (actionQueue.pending = null)\n : ((nextState = nextState.next),\n (actionNode.next = nextState),\n runActionStateAction(actionQueue, nextState)));\n}\nfunction onActionError(actionQueue, actionNode, error) {\n var last = actionQueue.pending;\n actionQueue.pending = null;\n if (null !== last) {\n last = last.next;\n do\n (actionNode.status = \"rejected\"),\n (actionNode.reason = error),\n notifyActionListeners(actionNode),\n (actionNode = actionNode.next);\n while (actionNode !== last);\n }\n actionQueue.action = null;\n}\nfunction notifyActionListeners(actionNode) {\n actionNode = actionNode.listeners;\n for (var i = 0; i < actionNode.length; i++) (0, actionNode[i])();\n}\nfunction actionStateReducer(oldState, newState) {\n return newState;\n}\nfunction mountActionState(action, initialStateProp) {\n if (isHydrating) {\n var ssrFormState = workInProgressRoot.formState;\n if (null !== ssrFormState) {\n a: {\n var JSCompiler_inline_result = currentlyRenderingFiber;\n if (isHydrating) {\n if (nextHydratableInstance) {\n b: {\n var JSCompiler_inline_result$jscomp$0 = nextHydratableInstance;\n for (\n var inRootOrSingleton = rootOrSingletonContext;\n 8 !== JSCompiler_inline_result$jscomp$0.nodeType;\n\n ) {\n if (!inRootOrSingleton) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n JSCompiler_inline_result$jscomp$0 = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n if (null === JSCompiler_inline_result$jscomp$0) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n }\n inRootOrSingleton = JSCompiler_inline_result$jscomp$0.data;\n JSCompiler_inline_result$jscomp$0 =\n \"F!\" === inRootOrSingleton || \"F\" === inRootOrSingleton\n ? JSCompiler_inline_result$jscomp$0\n : null;\n }\n if (JSCompiler_inline_result$jscomp$0) {\n nextHydratableInstance = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n JSCompiler_inline_result =\n \"F!\" === JSCompiler_inline_result$jscomp$0.data;\n break a;\n }\n }\n throwOnHydrationMismatch(JSCompiler_inline_result);\n }\n JSCompiler_inline_result = !1;\n }\n JSCompiler_inline_result && (initialStateProp = ssrFormState[0]);\n }\n }\n ssrFormState = mountWorkInProgressHook();\n ssrFormState.memoizedState = ssrFormState.baseState = initialStateProp;\n JSCompiler_inline_result = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: actionStateReducer,\n lastRenderedState: initialStateProp\n };\n ssrFormState.queue = JSCompiler_inline_result;\n ssrFormState = dispatchSetState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result\n );\n JSCompiler_inline_result.dispatch = ssrFormState;\n JSCompiler_inline_result = mountStateImpl(!1);\n inRootOrSingleton = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !1,\n JSCompiler_inline_result.queue\n );\n JSCompiler_inline_result = mountWorkInProgressHook();\n JSCompiler_inline_result$jscomp$0 = {\n state: initialStateProp,\n dispatch: null,\n action: action,\n pending: null\n };\n JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;\n ssrFormState = dispatchActionState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result$jscomp$0,\n inRootOrSingleton,\n ssrFormState\n );\n JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;\n JSCompiler_inline_result.memoizedState = action;\n return [initialStateProp, ssrFormState, !1];\n}\nfunction updateActionState(action) {\n var stateHook = updateWorkInProgressHook();\n return updateActionStateImpl(stateHook, currentHook, action);\n}\nfunction updateActionStateImpl(stateHook, currentStateHook, action) {\n currentStateHook = updateReducerImpl(\n stateHook,\n currentStateHook,\n actionStateReducer\n )[0];\n stateHook = updateReducer(basicStateReducer)[0];\n if (\n \"object\" === typeof currentStateHook &&\n null !== currentStateHook &&\n \"function\" === typeof currentStateHook.then\n )\n try {\n var state = useThenable(currentStateHook);\n } catch (x) {\n if (x === SuspenseException) throw SuspenseActionException;\n throw x;\n }\n else state = currentStateHook;\n currentStateHook = updateWorkInProgressHook();\n var actionQueue = currentStateHook.queue,\n dispatch = actionQueue.dispatch;\n action !== currentStateHook.memoizedState &&\n ((currentlyRenderingFiber.flags |= 2048),\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n actionStateActionEffect.bind(null, actionQueue, action),\n null\n ));\n return [state, dispatch, stateHook];\n}\nfunction actionStateActionEffect(actionQueue, action) {\n actionQueue.action = action;\n}\nfunction rerenderActionState(action) {\n var stateHook = updateWorkInProgressHook(),\n currentStateHook = currentHook;\n if (null !== currentStateHook)\n return updateActionStateImpl(stateHook, currentStateHook, action);\n updateWorkInProgressHook();\n stateHook = stateHook.memoizedState;\n currentStateHook = updateWorkInProgressHook();\n var dispatch = currentStateHook.queue.dispatch;\n currentStateHook.memoizedState = action;\n return [stateHook, dispatch, !1];\n}\nfunction pushSimpleEffect(tag, inst, create, deps) {\n tag = { tag: tag, create: create, deps: deps, inst: inst, next: null };\n inst = currentlyRenderingFiber.updateQueue;\n null === inst &&\n ((inst = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = inst));\n create = inst.lastEffect;\n null === create\n ? (inst.lastEffect = tag.next = tag)\n : ((deps = create.next),\n (create.next = tag),\n (tag.next = deps),\n (inst.lastEffect = tag));\n return tag;\n}\nfunction updateRef() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = mountWorkInProgressHook();\n currentlyRenderingFiber.flags |= fiberFlags;\n hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n { destroy: void 0 },\n create,\n void 0 === deps ? null : deps\n );\n}\nfunction updateEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var inst = hook.memoizedState.inst;\n null !== currentHook &&\n null !== deps &&\n areHookInputsEqual(deps, currentHook.memoizedState.deps)\n ? (hook.memoizedState = pushSimpleEffect(hookFlags, inst, create, deps))\n : ((currentlyRenderingFiber.flags |= fiberFlags),\n (hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n inst,\n create,\n deps\n )));\n}\nfunction mountEffect(create, deps) {\n mountEffectImpl(8390656, 8, create, deps);\n}\nfunction updateEffect(create, deps) {\n updateEffectImpl(2048, 8, create, deps);\n}\nfunction useEffectEventImpl(payload) {\n currentlyRenderingFiber.flags |= 4;\n var componentUpdateQueue = currentlyRenderingFiber.updateQueue;\n if (null === componentUpdateQueue)\n (componentUpdateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = componentUpdateQueue),\n (componentUpdateQueue.events = [payload]);\n else {\n var events = componentUpdateQueue.events;\n null === events\n ? (componentUpdateQueue.events = [payload])\n : events.push(payload);\n }\n}\nfunction updateEvent(callback) {\n var ref = updateWorkInProgressHook().memoizedState;\n useEffectEventImpl({ ref: ref, nextImpl: callback });\n return function () {\n if (0 !== (executionContext & 2)) throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n}\nfunction updateInsertionEffect(create, deps) {\n return updateEffectImpl(4, 2, create, deps);\n}\nfunction updateLayoutEffect(create, deps) {\n return updateEffectImpl(4, 4, create, deps);\n}\nfunction imperativeHandleEffect(create, ref) {\n if (\"function\" === typeof ref) {\n create = create();\n var refCleanup = ref(create);\n return function () {\n \"function\" === typeof refCleanup ? refCleanup() : ref(null);\n };\n }\n if (null !== ref && void 0 !== ref)\n return (\n (create = create()),\n (ref.current = create),\n function () {\n ref.current = null;\n }\n );\n}\nfunction updateImperativeHandle(ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n updateEffectImpl(4, 4, imperativeHandleEffect.bind(null, create, ref), deps);\n}\nfunction mountDebugValue() {}\nfunction updateCallback(callback, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n hook.memoizedState = [callback, deps];\n return callback;\n}\nfunction updateMemo(nextCreate, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n prevState = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [prevState, deps];\n return prevState;\n}\nfunction mountDeferredValueImpl(hook, value, initialValue) {\n if (\n void 0 === initialValue ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (hook.memoizedState = value);\n hook.memoizedState = initialValue;\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return initialValue;\n}\nfunction updateDeferredValueImpl(hook, prevValue, value, initialValue) {\n if (objectIs(value, prevValue)) return value;\n if (null !== currentTreeHiddenStackCursor.current)\n return (\n (hook = mountDeferredValueImpl(hook, value, initialValue)),\n objectIs(hook, prevValue) || (didReceiveUpdate = !0),\n hook\n );\n if (\n 0 === (renderLanes & 42) ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (didReceiveUpdate = !0), (hook.memoizedState = value);\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return prevValue;\n}\nfunction startTransition(fiber, queue, pendingState, finishedState, callback) {\n var previousPriority = ReactDOMSharedInternals.p;\n ReactDOMSharedInternals.p =\n 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8;\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n dispatchOptimisticSetState(fiber, !1, queue, pendingState);\n try {\n var returnValue = callback(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n if (\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ) {\n var thenableForFinishedState = chainThenableValue(\n returnValue,\n finishedState\n );\n dispatchSetStateInternal(\n fiber,\n queue,\n thenableForFinishedState,\n requestUpdateLane(fiber)\n );\n } else\n dispatchSetStateInternal(\n fiber,\n queue,\n finishedState,\n requestUpdateLane(fiber)\n );\n } catch (error) {\n dispatchSetStateInternal(\n fiber,\n queue,\n { then: function () {}, status: \"rejected\", reason: error },\n requestUpdateLane()\n );\n } finally {\n (ReactDOMSharedInternals.p = previousPriority),\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n}\nfunction noop() {}\nfunction startHostTransition(formFiber, pendingState, action, formData) {\n if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));\n var queue = ensureFormComponentIsStateful(formFiber).queue;\n startTransition(\n formFiber,\n queue,\n pendingState,\n sharedNotPendingObject,\n null === action\n ? noop\n : function () {\n requestFormReset$1(formFiber);\n return action(formData);\n }\n );\n}\nfunction ensureFormComponentIsStateful(formFiber) {\n var existingStateHook = formFiber.memoizedState;\n if (null !== existingStateHook) return existingStateHook;\n existingStateHook = {\n memoizedState: sharedNotPendingObject,\n baseState: sharedNotPendingObject,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: sharedNotPendingObject\n },\n next: null\n };\n var initialResetState = {};\n existingStateHook.next = {\n memoizedState: initialResetState,\n baseState: initialResetState,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialResetState\n },\n next: null\n };\n formFiber.memoizedState = existingStateHook;\n formFiber = formFiber.alternate;\n null !== formFiber && (formFiber.memoizedState = existingStateHook);\n return existingStateHook;\n}\nfunction requestFormReset$1(formFiber) {\n var stateHook = ensureFormComponentIsStateful(formFiber);\n null === stateHook.next && (stateHook = formFiber.alternate.memoizedState);\n dispatchSetStateInternal(\n formFiber,\n stateHook.next.queue,\n {},\n requestUpdateLane()\n );\n}\nfunction useHostTransitionStatus() {\n return readContext(HostTransitionContext);\n}\nfunction updateId() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction updateRefresh() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction refreshCache(fiber) {\n for (var provider = fiber.return; null !== provider; ) {\n switch (provider.tag) {\n case 24:\n case 3:\n var lane = requestUpdateLane();\n fiber = createUpdate(lane);\n var root$69 = enqueueUpdate(provider, fiber, lane);\n null !== root$69 &&\n (scheduleUpdateOnFiber(root$69, provider, lane),\n entangleTransitions(root$69, provider, lane));\n provider = { cache: createCache() };\n fiber.payload = provider;\n return;\n }\n provider = provider.return;\n }\n}\nfunction dispatchReducerAction(fiber, queue, action) {\n var lane = requestUpdateLane();\n action = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n isRenderPhaseUpdate(fiber)\n ? enqueueRenderPhaseUpdate(queue, action)\n : ((action = enqueueConcurrentHookUpdate(fiber, queue, action, lane)),\n null !== action &&\n (scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane)));\n}\nfunction dispatchSetState(fiber, queue, action) {\n var lane = requestUpdateLane();\n dispatchSetStateInternal(fiber, queue, action, lane);\n}\nfunction dispatchSetStateInternal(fiber, queue, action, lane) {\n var update = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) enqueueRenderPhaseUpdate(queue, update);\n else {\n var alternate = fiber.alternate;\n if (\n 0 === fiber.lanes &&\n (null === alternate || 0 === alternate.lanes) &&\n ((alternate = queue.lastRenderedReducer), null !== alternate)\n )\n try {\n var currentState = queue.lastRenderedState,\n eagerState = alternate(currentState, action);\n update.hasEagerState = !0;\n update.eagerState = eagerState;\n if (objectIs(eagerState, currentState))\n return (\n enqueueUpdate$1(fiber, queue, update, 0),\n null === workInProgressRoot && finishQueueingConcurrentUpdates(),\n !1\n );\n } catch (error) {\n } finally {\n }\n action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n if (null !== action)\n return (\n scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane),\n !0\n );\n }\n return !1;\n}\nfunction dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {\n action = {\n lane: 2,\n revertLane: requestTransitionLane(),\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) {\n if (throwIfDuringRender) throw Error(formatProdErrorMessage(479));\n } else\n (throwIfDuringRender = enqueueConcurrentHookUpdate(\n fiber,\n queue,\n action,\n 2\n )),\n null !== throwIfDuringRender &&\n scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);\n}\nfunction isRenderPhaseUpdate(fiber) {\n var alternate = fiber.alternate;\n return (\n fiber === currentlyRenderingFiber ||\n (null !== alternate && alternate === currentlyRenderingFiber)\n );\n}\nfunction enqueueRenderPhaseUpdate(queue, update) {\n didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =\n !0;\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n}\nfunction entangleTransitionUpdate(root, queue, lane) {\n if (0 !== (lane & 4194048)) {\n var queueLanes = queue.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n queue.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nvar ContextOnlyDispatcher = {\n readContext: readContext,\n use: use,\n useCallback: throwInvalidHookError,\n useContext: throwInvalidHookError,\n useEffect: throwInvalidHookError,\n useImperativeHandle: throwInvalidHookError,\n useLayoutEffect: throwInvalidHookError,\n useInsertionEffect: throwInvalidHookError,\n useMemo: throwInvalidHookError,\n useReducer: throwInvalidHookError,\n useRef: throwInvalidHookError,\n useState: throwInvalidHookError,\n useDebugValue: throwInvalidHookError,\n useDeferredValue: throwInvalidHookError,\n useTransition: throwInvalidHookError,\n useSyncExternalStore: throwInvalidHookError,\n useId: throwInvalidHookError,\n useHostTransitionStatus: throwInvalidHookError,\n useFormState: throwInvalidHookError,\n useActionState: throwInvalidHookError,\n useOptimistic: throwInvalidHookError,\n useMemoCache: throwInvalidHookError,\n useCacheRefresh: throwInvalidHookError\n};\nContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;\nvar HooksDispatcherOnMount = {\n readContext: readContext,\n use: use,\n useCallback: function (callback, deps) {\n mountWorkInProgressHook().memoizedState = [\n callback,\n void 0 === deps ? null : deps\n ];\n return callback;\n },\n useContext: readContext,\n useEffect: mountEffect,\n useImperativeHandle: function (ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n mountEffectImpl(\n 4194308,\n 4,\n imperativeHandleEffect.bind(null, create, ref),\n deps\n );\n },\n useLayoutEffect: function (create, deps) {\n return mountEffectImpl(4194308, 4, create, deps);\n },\n useInsertionEffect: function (create, deps) {\n mountEffectImpl(4, 2, create, deps);\n },\n useMemo: function (nextCreate, deps) {\n var hook = mountWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var nextValue = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [nextValue, deps];\n return nextValue;\n },\n useReducer: function (reducer, initialArg, init) {\n var hook = mountWorkInProgressHook();\n if (void 0 !== init) {\n var initialState = init(initialArg);\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n init(initialArg);\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n } else initialState = initialArg;\n hook.memoizedState = hook.baseState = initialState;\n reducer = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: reducer,\n lastRenderedState: initialState\n };\n hook.queue = reducer;\n reducer = reducer.dispatch = dispatchReducerAction.bind(\n null,\n currentlyRenderingFiber,\n reducer\n );\n return [hook.memoizedState, reducer];\n },\n useRef: function (initialValue) {\n var hook = mountWorkInProgressHook();\n initialValue = { current: initialValue };\n return (hook.memoizedState = initialValue);\n },\n useState: function (initialState) {\n initialState = mountStateImpl(initialState);\n var queue = initialState.queue,\n dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);\n queue.dispatch = dispatch;\n return [initialState.memoizedState, dispatch];\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = mountWorkInProgressHook();\n return mountDeferredValueImpl(hook, value, initialValue);\n },\n useTransition: function () {\n var stateHook = mountStateImpl(!1);\n stateHook = startTransition.bind(\n null,\n currentlyRenderingFiber,\n stateHook.queue,\n !0,\n !1\n );\n mountWorkInProgressHook().memoizedState = stateHook;\n return [!1, stateHook];\n },\n useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = mountWorkInProgressHook();\n if (isHydrating) {\n if (void 0 === getServerSnapshot)\n throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else {\n getServerSnapshot = getSnapshot();\n if (null === workInProgressRoot)\n throw Error(formatProdErrorMessage(349));\n 0 !== (workInProgressRootRenderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n hook.memoizedState = getServerSnapshot;\n var inst = { value: getServerSnapshot, getSnapshot: getSnapshot };\n hook.queue = inst;\n mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [\n subscribe\n ]);\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n inst,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n return getServerSnapshot;\n },\n useId: function () {\n var hook = mountWorkInProgressHook(),\n identifierPrefix = workInProgressRoot.identifierPrefix;\n if (isHydrating) {\n var JSCompiler_inline_result = treeContextOverflow;\n var idWithLeadingBit = treeContextId;\n JSCompiler_inline_result =\n (\n idWithLeadingBit & ~(1 << (32 - clz32(idWithLeadingBit) - 1))\n ).toString(32) + JSCompiler_inline_result;\n identifierPrefix =\n \"_\" + identifierPrefix + \"R_\" + JSCompiler_inline_result;\n JSCompiler_inline_result = localIdCounter++;\n 0 < JSCompiler_inline_result &&\n (identifierPrefix += \"H\" + JSCompiler_inline_result.toString(32));\n identifierPrefix += \"_\";\n } else\n (JSCompiler_inline_result = globalClientIdCounter++),\n (identifierPrefix =\n \"_\" +\n identifierPrefix +\n \"r_\" +\n JSCompiler_inline_result.toString(32) +\n \"_\");\n return (hook.memoizedState = identifierPrefix);\n },\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: mountActionState,\n useActionState: mountActionState,\n useOptimistic: function (passthrough) {\n var hook = mountWorkInProgressHook();\n hook.memoizedState = hook.baseState = passthrough;\n var queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: null,\n lastRenderedState: null\n };\n hook.queue = queue;\n hook = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !0,\n queue\n );\n queue.dispatch = hook;\n return [passthrough, hook];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: function () {\n return (mountWorkInProgressHook().memoizedState = refreshCache.bind(\n null,\n currentlyRenderingFiber\n ));\n },\n useEffectEvent: function (callback) {\n var hook = mountWorkInProgressHook(),\n ref = { impl: callback };\n hook.memoizedState = ref;\n return function () {\n if (0 !== (executionContext & 2))\n throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n }\n },\n HooksDispatcherOnUpdate = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: updateReducer,\n useRef: updateRef,\n useState: function () {\n return updateReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = updateReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: updateActionState,\n useActionState: updateActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n };\nHooksDispatcherOnUpdate.useEffectEvent = updateEvent;\nvar HooksDispatcherOnRerender = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: rerenderReducer,\n useRef: updateRef,\n useState: function () {\n return rerenderReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return null === currentHook\n ? mountDeferredValueImpl(hook, value, initialValue)\n : updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = rerenderReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: rerenderActionState,\n useActionState: rerenderActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n if (null !== currentHook)\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n hook.baseState = passthrough;\n return [passthrough, hook.queue.dispatch];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n};\nHooksDispatcherOnRerender.useEffectEvent = updateEvent;\nfunction applyDerivedStateFromProps(\n workInProgress,\n ctor,\n getDerivedStateFromProps,\n nextProps\n) {\n ctor = workInProgress.memoizedState;\n getDerivedStateFromProps = getDerivedStateFromProps(nextProps, ctor);\n getDerivedStateFromProps =\n null === getDerivedStateFromProps || void 0 === getDerivedStateFromProps\n ? ctor\n : assign({}, ctor, getDerivedStateFromProps);\n workInProgress.memoizedState = getDerivedStateFromProps;\n 0 === workInProgress.lanes &&\n (workInProgress.updateQueue.baseState = getDerivedStateFromProps);\n}\nvar classComponentUpdater = {\n enqueueSetState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueReplaceState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 1;\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueForceUpdate: function (inst, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 2;\n void 0 !== callback && null !== callback && (update.callback = callback);\n callback = enqueueUpdate(inst, update, lane);\n null !== callback &&\n (scheduleUpdateOnFiber(callback, inst, lane),\n entangleTransitions(callback, inst, lane));\n }\n};\nfunction checkShouldComponentUpdate(\n workInProgress,\n ctor,\n oldProps,\n newProps,\n oldState,\n newState,\n nextContext\n) {\n workInProgress = workInProgress.stateNode;\n return \"function\" === typeof workInProgress.shouldComponentUpdate\n ? workInProgress.shouldComponentUpdate(newProps, newState, nextContext)\n : ctor.prototype && ctor.prototype.isPureReactComponent\n ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)\n : !0;\n}\nfunction callComponentWillReceiveProps(\n workInProgress,\n instance,\n newProps,\n nextContext\n) {\n workInProgress = instance.state;\n \"function\" === typeof instance.componentWillReceiveProps &&\n instance.componentWillReceiveProps(newProps, nextContext);\n \"function\" === typeof instance.UNSAFE_componentWillReceiveProps &&\n instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);\n instance.state !== workInProgress &&\n classComponentUpdater.enqueueReplaceState(instance, instance.state, null);\n}\nfunction resolveClassComponentProps(Component, baseProps) {\n var newProps = baseProps;\n if (\"ref\" in baseProps) {\n newProps = {};\n for (var propName in baseProps)\n \"ref\" !== propName && (newProps[propName] = baseProps[propName]);\n }\n if ((Component = Component.defaultProps)) {\n newProps === baseProps && (newProps = assign({}, newProps));\n for (var propName$73 in Component)\n void 0 === newProps[propName$73] &&\n (newProps[propName$73] = Component[propName$73]);\n }\n return newProps;\n}\nfunction defaultOnUncaughtError(error) {\n reportGlobalError(error);\n}\nfunction defaultOnCaughtError(error) {\n console.error(error);\n}\nfunction defaultOnRecoverableError(error) {\n reportGlobalError(error);\n}\nfunction logUncaughtError(root, errorInfo) {\n try {\n var onUncaughtError = root.onUncaughtError;\n onUncaughtError(errorInfo.value, { componentStack: errorInfo.stack });\n } catch (e$74) {\n setTimeout(function () {\n throw e$74;\n });\n }\n}\nfunction logCaughtError(root, boundary, errorInfo) {\n try {\n var onCaughtError = root.onCaughtError;\n onCaughtError(errorInfo.value, {\n componentStack: errorInfo.stack,\n errorBoundary: 1 === boundary.tag ? boundary.stateNode : null\n });\n } catch (e$75) {\n setTimeout(function () {\n throw e$75;\n });\n }\n}\nfunction createRootErrorUpdate(root, errorInfo, lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n lane.payload = { element: null };\n lane.callback = function () {\n logUncaughtError(root, errorInfo);\n };\n return lane;\n}\nfunction createClassErrorUpdate(lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n return lane;\n}\nfunction initializeClassErrorUpdate(update, root, fiber, errorInfo) {\n var getDerivedStateFromError = fiber.type.getDerivedStateFromError;\n if (\"function\" === typeof getDerivedStateFromError) {\n var error = errorInfo.value;\n update.payload = function () {\n return getDerivedStateFromError(error);\n };\n update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n };\n }\n var inst = fiber.stateNode;\n null !== inst &&\n \"function\" === typeof inst.componentDidCatch &&\n (update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n \"function\" !== typeof getDerivedStateFromError &&\n (null === legacyErrorBoundariesThatAlreadyFailed\n ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this]))\n : legacyErrorBoundariesThatAlreadyFailed.add(this));\n var stack = errorInfo.stack;\n this.componentDidCatch(errorInfo.value, {\n componentStack: null !== stack ? stack : \"\"\n });\n });\n}\nfunction throwException(\n root,\n returnFiber,\n sourceFiber,\n value,\n rootRenderLanes\n) {\n sourceFiber.flags |= 32768;\n if (\n null !== value &&\n \"object\" === typeof value &&\n \"function\" === typeof value.then\n ) {\n returnFiber = sourceFiber.alternate;\n null !== returnFiber &&\n propagateParentContextChanges(\n returnFiber,\n sourceFiber,\n rootRenderLanes,\n !0\n );\n sourceFiber = suspenseHandlerStackCursor.current;\n if (null !== sourceFiber) {\n switch (sourceFiber.tag) {\n case 31:\n case 13:\n return (\n null === shellBoundary\n ? renderDidSuspendDelayIfPossible()\n : null === sourceFiber.alternate &&\n 0 === workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 3),\n (sourceFiber.flags &= -257),\n (sourceFiber.flags |= 65536),\n (sourceFiber.lanes = rootRenderLanes),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? (sourceFiber.updateQueue = new Set([value]))\n : returnFiber.add(value),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n case 22:\n return (\n (sourceFiber.flags |= 65536),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? ((returnFiber = {\n transitions: null,\n markerInstances: null,\n retryQueue: new Set([value])\n }),\n (sourceFiber.updateQueue = returnFiber))\n : ((sourceFiber = returnFiber.retryQueue),\n null === sourceFiber\n ? (returnFiber.retryQueue = new Set([value]))\n : sourceFiber.add(value)),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n }\n throw Error(formatProdErrorMessage(435, sourceFiber.tag));\n }\n attachPingListener(root, value, rootRenderLanes);\n renderDidSuspendDelayIfPossible();\n return !1;\n }\n if (isHydrating)\n return (\n (returnFiber = suspenseHandlerStackCursor.current),\n null !== returnFiber\n ? (0 === (returnFiber.flags & 65536) && (returnFiber.flags |= 256),\n (returnFiber.flags |= 65536),\n (returnFiber.lanes = rootRenderLanes),\n value !== HydrationMismatchException &&\n ((root = Error(formatProdErrorMessage(422), { cause: value })),\n queueHydrationError(createCapturedValueAtFiber(root, sourceFiber))))\n : (value !== HydrationMismatchException &&\n ((returnFiber = Error(formatProdErrorMessage(423), {\n cause: value\n })),\n queueHydrationError(\n createCapturedValueAtFiber(returnFiber, sourceFiber)\n )),\n (root = root.current.alternate),\n (root.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (root.lanes |= rootRenderLanes),\n (value = createCapturedValueAtFiber(value, sourceFiber)),\n (rootRenderLanes = createRootErrorUpdate(\n root.stateNode,\n value,\n rootRenderLanes\n )),\n enqueueCapturedUpdate(root, rootRenderLanes),\n 4 !== workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 2)),\n !1\n );\n var wrapperError = Error(formatProdErrorMessage(520), { cause: value });\n wrapperError = createCapturedValueAtFiber(wrapperError, sourceFiber);\n null === workInProgressRootConcurrentErrors\n ? (workInProgressRootConcurrentErrors = [wrapperError])\n : workInProgressRootConcurrentErrors.push(wrapperError);\n 4 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2);\n if (null === returnFiber) return !0;\n value = createCapturedValueAtFiber(value, sourceFiber);\n sourceFiber = returnFiber;\n do {\n switch (sourceFiber.tag) {\n case 3:\n return (\n (sourceFiber.flags |= 65536),\n (root = rootRenderLanes & -rootRenderLanes),\n (sourceFiber.lanes |= root),\n (root = createRootErrorUpdate(sourceFiber.stateNode, value, root)),\n enqueueCapturedUpdate(sourceFiber, root),\n !1\n );\n case 1:\n if (\n ((returnFiber = sourceFiber.type),\n (wrapperError = sourceFiber.stateNode),\n 0 === (sourceFiber.flags & 128) &&\n (\"function\" === typeof returnFiber.getDerivedStateFromError ||\n (null !== wrapperError &&\n \"function\" === typeof wrapperError.componentDidCatch &&\n (null === legacyErrorBoundariesThatAlreadyFailed ||\n !legacyErrorBoundariesThatAlreadyFailed.has(wrapperError)))))\n )\n return (\n (sourceFiber.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (sourceFiber.lanes |= rootRenderLanes),\n (rootRenderLanes = createClassErrorUpdate(rootRenderLanes)),\n initializeClassErrorUpdate(\n rootRenderLanes,\n root,\n sourceFiber,\n value\n ),\n enqueueCapturedUpdate(sourceFiber, rootRenderLanes),\n !1\n );\n }\n sourceFiber = sourceFiber.return;\n } while (null !== sourceFiber);\n return !1;\n}\nvar SelectiveHydrationException = Error(formatProdErrorMessage(461)),\n didReceiveUpdate = !1;\nfunction reconcileChildren(current, workInProgress, nextChildren, renderLanes) {\n workInProgress.child =\n null === current\n ? mountChildFibers(workInProgress, null, nextChildren, renderLanes)\n : reconcileChildFibers(\n workInProgress,\n current.child,\n nextChildren,\n renderLanes\n );\n}\nfunction updateForwardRef(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n Component = Component.render;\n var ref = workInProgress.ref;\n if (\"ref\" in nextProps) {\n var propsWithoutRef = {};\n for (var key in nextProps)\n \"ref\" !== key && (propsWithoutRef[key] = nextProps[key]);\n } else propsWithoutRef = nextProps;\n prepareToReadContext(workInProgress);\n nextProps = renderWithHooks(\n current,\n workInProgress,\n Component,\n propsWithoutRef,\n ref,\n renderLanes\n );\n key = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && key && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null === current) {\n var type = Component.type;\n if (\n \"function\" === typeof type &&\n !shouldConstruct(type) &&\n void 0 === type.defaultProps &&\n null === Component.compare\n )\n return (\n (workInProgress.tag = 15),\n (workInProgress.type = type),\n updateSimpleMemoComponent(\n current,\n workInProgress,\n type,\n nextProps,\n renderLanes\n )\n );\n current = createFiberFromTypeAndProps(\n Component.type,\n null,\n nextProps,\n workInProgress,\n workInProgress.mode,\n renderLanes\n );\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n }\n type = current.child;\n if (!checkScheduledUpdateOrContext(current, renderLanes)) {\n var prevProps = type.memoizedProps;\n Component = Component.compare;\n Component = null !== Component ? Component : shallowEqual;\n if (Component(prevProps, nextProps) && current.ref === workInProgress.ref)\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n }\n workInProgress.flags |= 1;\n current = createWorkInProgress(type, nextProps);\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n}\nfunction updateSimpleMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null !== current) {\n var prevProps = current.memoizedProps;\n if (\n shallowEqual(prevProps, nextProps) &&\n current.ref === workInProgress.ref\n )\n if (\n ((didReceiveUpdate = !1),\n (workInProgress.pendingProps = nextProps = prevProps),\n checkScheduledUpdateOrContext(current, renderLanes))\n )\n 0 !== (current.flags & 131072) && (didReceiveUpdate = !0);\n else\n return (\n (workInProgress.lanes = current.lanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n }\n return updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n );\n}\nfunction updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n nextProps\n) {\n var nextChildren = nextProps.children,\n prevState = null !== current ? current.memoizedState : null;\n null === current &&\n null === workInProgress.stateNode &&\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n if (\"hidden\" === nextProps.mode) {\n if (0 !== (workInProgress.flags & 128)) {\n prevState =\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes;\n if (null !== current) {\n nextProps = workInProgress.child = current.child;\n for (nextChildren = 0; null !== nextProps; )\n (nextChildren =\n nextChildren | nextProps.lanes | nextProps.childLanes),\n (nextProps = nextProps.sibling);\n nextProps = nextChildren & ~prevState;\n } else (nextProps = 0), (workInProgress.child = null);\n return deferHiddenOffscreenComponent(\n current,\n workInProgress,\n prevState,\n renderLanes,\n nextProps\n );\n }\n if (0 !== (renderLanes & 536870912))\n (workInProgress.memoizedState = { baseLanes: 0, cachePool: null }),\n null !== current &&\n pushTransition(\n workInProgress,\n null !== prevState ? prevState.cachePool : null\n ),\n null !== prevState\n ? pushHiddenContext(workInProgress, prevState)\n : reuseHiddenContextOnStack(),\n pushOffscreenSuspenseHandler(workInProgress);\n else\n return (\n (nextProps = workInProgress.lanes = 536870912),\n deferHiddenOffscreenComponent(\n current,\n workInProgress,\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes,\n renderLanes,\n nextProps\n )\n );\n } else\n null !== prevState\n ? (pushTransition(workInProgress, prevState.cachePool),\n pushHiddenContext(workInProgress, prevState),\n reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.memoizedState = null))\n : (null !== current && pushTransition(workInProgress, null),\n reuseHiddenContextOnStack(),\n reuseSuspenseHandlerOnStack(workInProgress));\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nfunction bailoutOffscreenComponent(current, workInProgress) {\n (null !== current && 22 === current.tag) ||\n null !== workInProgress.stateNode ||\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n return workInProgress.sibling;\n}\nfunction deferHiddenOffscreenComponent(\n current,\n workInProgress,\n nextBaseLanes,\n renderLanes,\n remainingChildLanes\n) {\n var JSCompiler_inline_result = peekCacheFromPool();\n JSCompiler_inline_result =\n null === JSCompiler_inline_result\n ? null\n : { parent: CacheContext._currentValue, pool: JSCompiler_inline_result };\n workInProgress.memoizedState = {\n baseLanes: nextBaseLanes,\n cachePool: JSCompiler_inline_result\n };\n null !== current && pushTransition(workInProgress, null);\n reuseHiddenContextOnStack();\n pushOffscreenSuspenseHandler(workInProgress);\n null !== current &&\n propagateParentContextChanges(current, workInProgress, renderLanes, !0);\n workInProgress.childLanes = remainingChildLanes;\n return null;\n}\nfunction mountActivityChildren(workInProgress, nextProps) {\n nextProps = mountWorkInProgressOffscreenFiber(\n { mode: nextProps.mode, children: nextProps.children },\n workInProgress.mode\n );\n nextProps.ref = workInProgress.ref;\n workInProgress.child = nextProps;\n nextProps.return = workInProgress;\n return nextProps;\n}\nfunction retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountActivityChildren(workInProgress, workInProgress.pendingProps);\n current.flags |= 2;\n popSuspenseHandler(workInProgress);\n workInProgress.memoizedState = null;\n return current;\n}\nfunction updateActivityComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n didSuspend = 0 !== (workInProgress.flags & 128);\n workInProgress.flags &= -129;\n if (null === current) {\n if (isHydrating) {\n if (\"hidden\" === nextProps.mode)\n return (\n (current = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.lanes = 536870912),\n bailoutOffscreenComponent(null, current)\n );\n pushDehydratedActivitySuspenseHandler(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" === current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n workInProgress.lanes = 536870912;\n return null;\n }\n return mountActivityChildren(workInProgress, nextProps);\n }\n var prevState = current.memoizedState;\n if (null !== prevState) {\n var dehydrated = prevState.dehydrated;\n pushDehydratedActivitySuspenseHandler(workInProgress);\n if (didSuspend)\n if (workInProgress.flags & 256)\n (workInProgress.flags &= -257),\n (workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n ));\n else if (null !== workInProgress.memoizedState)\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null);\n else throw Error(formatProdErrorMessage(558));\n else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (didSuspend = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || didSuspend)\n ) {\n nextProps = workInProgressRoot;\n if (\n null !== nextProps &&\n ((dehydrated = getBumpedLaneForHydration(nextProps, renderLanes)),\n 0 !== dehydrated && dehydrated !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = dehydrated),\n enqueueConcurrentRenderForLane(current, dehydrated),\n scheduleUpdateOnFiber(nextProps, current, dehydrated),\n SelectiveHydrationException)\n );\n renderDidSuspendDelayIfPossible();\n workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n (current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(dehydrated.nextSibling)),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.flags |= 4096);\n return workInProgress;\n }\n current = createWorkInProgress(current.child, {\n mode: nextProps.mode,\n children: nextProps.children\n });\n current.ref = workInProgress.ref;\n workInProgress.child = current;\n current.return = workInProgress;\n return current;\n}\nfunction markRef(current, workInProgress) {\n var ref = workInProgress.ref;\n if (null === ref)\n null !== current &&\n null !== current.ref &&\n (workInProgress.flags |= 4194816);\n else {\n if (\"function\" !== typeof ref && \"object\" !== typeof ref)\n throw Error(formatProdErrorMessage(284));\n if (null === current || current.ref !== ref)\n workInProgress.flags |= 4194816;\n }\n}\nfunction updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n Component = renderWithHooks(\n current,\n workInProgress,\n Component,\n nextProps,\n void 0,\n renderLanes\n );\n nextProps = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && nextProps && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, Component, renderLanes);\n return workInProgress.child;\n}\nfunction replayFunctionComponent(\n current,\n workInProgress,\n nextProps,\n Component,\n secondArg,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n workInProgress.updateQueue = null;\n nextProps = renderWithHooksAgain(\n workInProgress,\n Component,\n nextProps,\n secondArg\n );\n finishRenderingHooks(current);\n Component = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && Component && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateClassComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n if (null === workInProgress.stateNode) {\n var context = emptyContextObject,\n contextType = Component.contextType;\n \"object\" === typeof contextType &&\n null !== contextType &&\n (context = readContext(contextType));\n context = new Component(nextProps, context);\n workInProgress.memoizedState =\n null !== context.state && void 0 !== context.state ? context.state : null;\n context.updater = classComponentUpdater;\n workInProgress.stateNode = context;\n context._reactInternals = workInProgress;\n context = workInProgress.stateNode;\n context.props = nextProps;\n context.state = workInProgress.memoizedState;\n context.refs = {};\n initializeUpdateQueue(workInProgress);\n contextType = Component.contextType;\n context.context =\n \"object\" === typeof contextType && null !== contextType\n ? readContext(contextType)\n : emptyContextObject;\n context.state = workInProgress.memoizedState;\n contextType = Component.getDerivedStateFromProps;\n \"function\" === typeof contextType &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n contextType,\n nextProps\n ),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof Component.getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n ((contextType = context.state),\n \"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount(),\n contextType !== context.state &&\n classComponentUpdater.enqueueReplaceState(context, context.state, null),\n processUpdateQueue(workInProgress, nextProps, context, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction(),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308);\n nextProps = !0;\n } else if (null === current) {\n context = workInProgress.stateNode;\n var unresolvedOldProps = workInProgress.memoizedProps,\n oldProps = resolveClassComponentProps(Component, unresolvedOldProps);\n context.props = oldProps;\n var oldContext = context.context,\n contextType$jscomp$0 = Component.contextType;\n contextType = emptyContextObject;\n \"object\" === typeof contextType$jscomp$0 &&\n null !== contextType$jscomp$0 &&\n (contextType = readContext(contextType$jscomp$0));\n var getDerivedStateFromProps = Component.getDerivedStateFromProps;\n contextType$jscomp$0 =\n \"function\" === typeof getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate;\n unresolvedOldProps = workInProgress.pendingProps !== unresolvedOldProps;\n contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((unresolvedOldProps || oldContext !== contextType) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n contextType\n ));\n hasForceUpdate = !1;\n var oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n oldContext = workInProgress.memoizedState;\n unresolvedOldProps || oldState !== oldContext || hasForceUpdate\n ? (\"function\" === typeof getDerivedStateFromProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n getDerivedStateFromProps,\n nextProps\n ),\n (oldContext = workInProgress.memoizedState)),\n (oldProps =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n oldProps,\n nextProps,\n oldState,\n oldContext,\n contextType\n ))\n ? (contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n (\"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount()),\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = oldContext)),\n (context.props = nextProps),\n (context.state = oldContext),\n (context.context = contextType),\n (nextProps = oldProps))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (nextProps = !1));\n } else {\n context = workInProgress.stateNode;\n cloneUpdateQueue(current, workInProgress);\n contextType = workInProgress.memoizedProps;\n contextType$jscomp$0 = resolveClassComponentProps(Component, contextType);\n context.props = contextType$jscomp$0;\n getDerivedStateFromProps = workInProgress.pendingProps;\n oldState = context.context;\n oldContext = Component.contextType;\n oldProps = emptyContextObject;\n \"object\" === typeof oldContext &&\n null !== oldContext &&\n (oldProps = readContext(oldContext));\n unresolvedOldProps = Component.getDerivedStateFromProps;\n (oldContext =\n \"function\" === typeof unresolvedOldProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate) ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((contextType !== getDerivedStateFromProps || oldState !== oldProps) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n oldProps\n ));\n hasForceUpdate = !1;\n oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n var newState = workInProgress.memoizedState;\n contextType !== getDerivedStateFromProps ||\n oldState !== newState ||\n hasForceUpdate ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies))\n ? (\"function\" === typeof unresolvedOldProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n unresolvedOldProps,\n nextProps\n ),\n (newState = workInProgress.memoizedState)),\n (contextType$jscomp$0 =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n contextType$jscomp$0,\n nextProps,\n oldState,\n newState,\n oldProps\n ) ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies)))\n ? (oldContext ||\n (\"function\" !== typeof context.UNSAFE_componentWillUpdate &&\n \"function\" !== typeof context.componentWillUpdate) ||\n (\"function\" === typeof context.componentWillUpdate &&\n context.componentWillUpdate(nextProps, newState, oldProps),\n \"function\" === typeof context.UNSAFE_componentWillUpdate &&\n context.UNSAFE_componentWillUpdate(\n nextProps,\n newState,\n oldProps\n )),\n \"function\" === typeof context.componentDidUpdate &&\n (workInProgress.flags |= 4),\n \"function\" === typeof context.getSnapshotBeforeUpdate &&\n (workInProgress.flags |= 1024))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = newState)),\n (context.props = nextProps),\n (context.state = newState),\n (context.context = oldProps),\n (nextProps = contextType$jscomp$0))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (nextProps = !1));\n }\n context = nextProps;\n markRef(current, workInProgress);\n nextProps = 0 !== (workInProgress.flags & 128);\n context || nextProps\n ? ((context = workInProgress.stateNode),\n (Component =\n nextProps && \"function\" !== typeof Component.getDerivedStateFromError\n ? null\n : context.render()),\n (workInProgress.flags |= 1),\n null !== current && nextProps\n ? ((workInProgress.child = reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n )),\n (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n Component,\n renderLanes\n )))\n : reconcileChildren(current, workInProgress, Component, renderLanes),\n (workInProgress.memoizedState = context.state),\n (current = workInProgress.child))\n : (current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n ));\n return current;\n}\nfunction mountHostRootWithoutHydrating(\n current,\n workInProgress,\n nextChildren,\n renderLanes\n) {\n resetHydrationState();\n workInProgress.flags |= 256;\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nvar SUSPENDED_MARKER = {\n dehydrated: null,\n treeContext: null,\n retryLane: 0,\n hydrationErrors: null\n};\nfunction mountSuspenseOffscreenState(renderLanes) {\n return { baseLanes: renderLanes, cachePool: getSuspendedCache() };\n}\nfunction getRemainingWorkInPrimaryTree(\n current,\n primaryTreeDidDefer,\n renderLanes\n) {\n current = null !== current ? current.childLanes & ~renderLanes : 0;\n primaryTreeDidDefer && (current |= workInProgressDeferredLane);\n return current;\n}\nfunction updateSuspenseComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n showFallback = !1,\n didSuspend = 0 !== (workInProgress.flags & 128),\n JSCompiler_temp;\n (JSCompiler_temp = didSuspend) ||\n (JSCompiler_temp =\n null !== current && null === current.memoizedState\n ? !1\n : 0 !== (suspenseStackCursor.current & 2));\n JSCompiler_temp && ((showFallback = !0), (workInProgress.flags &= -129));\n JSCompiler_temp = 0 !== (workInProgress.flags & 32);\n workInProgress.flags &= -33;\n if (null === current) {\n if (isHydrating) {\n showFallback\n ? pushPrimaryTreeSuspenseHandler(workInProgress)\n : reuseSuspenseHandlerOnStack(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" !== current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n isSuspenseInstanceFallback(current)\n ? (workInProgress.lanes = 32)\n : (workInProgress.lanes = 536870912);\n return null;\n }\n var nextPrimaryChildren = nextProps.children;\n nextProps = nextProps.fallback;\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (showFallback = workInProgress.mode),\n (nextPrimaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"hidden\", children: nextPrimaryChildren },\n showFallback\n )),\n (nextProps = createFiberFromFragment(\n nextProps,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.sibling = nextProps),\n (workInProgress.child = nextPrimaryChildren),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState = mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(null, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);\n }\n var prevState = current.memoizedState;\n if (\n null !== prevState &&\n ((nextPrimaryChildren = prevState.dehydrated), null !== nextPrimaryChildren)\n ) {\n if (didSuspend)\n workInProgress.flags & 256\n ? (pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags &= -257),\n (workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n )))\n : null !== workInProgress.memoizedState\n ? (reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null))\n : (reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (nextProps = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: nextProps.children },\n showFallback\n )),\n (nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n ),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState =\n mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n (workInProgress = bailoutOffscreenComponent(null, nextProps)));\n else if (\n (pushPrimaryTreeSuspenseHandler(workInProgress),\n isSuspenseInstanceFallback(nextPrimaryChildren))\n ) {\n JSCompiler_temp =\n nextPrimaryChildren.nextSibling &&\n nextPrimaryChildren.nextSibling.dataset;\n if (JSCompiler_temp) var digest = JSCompiler_temp.dgst;\n JSCompiler_temp = digest;\n nextProps = Error(formatProdErrorMessage(419));\n nextProps.stack = \"\";\n nextProps.digest = JSCompiler_temp;\n queueHydrationError({ value: nextProps, source: null, stack: null });\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (JSCompiler_temp = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || JSCompiler_temp)\n ) {\n JSCompiler_temp = workInProgressRoot;\n if (\n null !== JSCompiler_temp &&\n ((nextProps = getBumpedLaneForHydration(JSCompiler_temp, renderLanes)),\n 0 !== nextProps && nextProps !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = nextProps),\n enqueueConcurrentRenderForLane(current, nextProps),\n scheduleUpdateOnFiber(JSCompiler_temp, current, nextProps),\n SelectiveHydrationException)\n );\n isSuspenseInstancePending(nextPrimaryChildren) ||\n renderDidSuspendDelayIfPossible();\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n isSuspenseInstancePending(nextPrimaryChildren)\n ? ((workInProgress.flags |= 192),\n (workInProgress.child = current.child),\n (workInProgress = null))\n : ((current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(\n nextPrimaryChildren.nextSibling\n )),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountSuspensePrimaryChildren(\n workInProgress,\n nextProps.children\n )),\n (workInProgress.flags |= 4096));\n return workInProgress;\n }\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (prevState = current.child),\n (digest = prevState.sibling),\n (nextProps = createWorkInProgress(prevState, {\n mode: \"hidden\",\n children: nextProps.children\n })),\n (nextProps.subtreeFlags = prevState.subtreeFlags & 65011712),\n null !== digest\n ? (nextPrimaryChildren = createWorkInProgress(\n digest,\n nextPrimaryChildren\n ))\n : ((nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2)),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n bailoutOffscreenComponent(null, nextProps),\n (nextProps = workInProgress.child),\n (nextPrimaryChildren = current.child.memoizedState),\n null === nextPrimaryChildren\n ? (nextPrimaryChildren = mountSuspenseOffscreenState(renderLanes))\n : ((showFallback = nextPrimaryChildren.cachePool),\n null !== showFallback\n ? ((prevState = CacheContext._currentValue),\n (showFallback =\n showFallback.parent !== prevState\n ? { parent: prevState, pool: prevState }\n : showFallback))\n : (showFallback = getSuspendedCache()),\n (nextPrimaryChildren = {\n baseLanes: nextPrimaryChildren.baseLanes | renderLanes,\n cachePool: showFallback\n })),\n (nextProps.memoizedState = nextPrimaryChildren),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(current.child, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n renderLanes = current.child;\n current = renderLanes.sibling;\n renderLanes = createWorkInProgress(renderLanes, {\n mode: \"visible\",\n children: nextProps.children\n });\n renderLanes.return = workInProgress;\n renderLanes.sibling = null;\n null !== current &&\n ((JSCompiler_temp = workInProgress.deletions),\n null === JSCompiler_temp\n ? ((workInProgress.deletions = [current]), (workInProgress.flags |= 16))\n : JSCompiler_temp.push(current));\n workInProgress.child = renderLanes;\n workInProgress.memoizedState = null;\n return renderLanes;\n}\nfunction mountSuspensePrimaryChildren(workInProgress, primaryChildren) {\n primaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: primaryChildren },\n workInProgress.mode\n );\n primaryChildren.return = workInProgress;\n return (workInProgress.child = primaryChildren);\n}\nfunction mountWorkInProgressOffscreenFiber(offscreenProps, mode) {\n offscreenProps = createFiberImplClass(22, offscreenProps, null, mode);\n offscreenProps.lanes = 0;\n return offscreenProps;\n}\nfunction retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountSuspensePrimaryChildren(\n workInProgress,\n workInProgress.pendingProps.children\n );\n current.flags |= 2;\n workInProgress.memoizedState = null;\n return current;\n}\nfunction scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {\n fiber.lanes |= renderLanes;\n var alternate = fiber.alternate;\n null !== alternate && (alternate.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);\n}\nfunction initSuspenseListRenderState(\n workInProgress,\n isBackwards,\n tail,\n lastContentRow,\n tailMode,\n treeForkCount\n) {\n var renderState = workInProgress.memoizedState;\n null === renderState\n ? (workInProgress.memoizedState = {\n isBackwards: isBackwards,\n rendering: null,\n renderingStartTime: 0,\n last: lastContentRow,\n tail: tail,\n tailMode: tailMode,\n treeForkCount: treeForkCount\n })\n : ((renderState.isBackwards = isBackwards),\n (renderState.rendering = null),\n (renderState.renderingStartTime = 0),\n (renderState.last = lastContentRow),\n (renderState.tail = tail),\n (renderState.tailMode = tailMode),\n (renderState.treeForkCount = treeForkCount));\n}\nfunction updateSuspenseListComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n revealOrder = nextProps.revealOrder,\n tailMode = nextProps.tail;\n nextProps = nextProps.children;\n var suspenseContext = suspenseStackCursor.current,\n shouldForceFallback = 0 !== (suspenseContext & 2);\n shouldForceFallback\n ? ((suspenseContext = (suspenseContext & 1) | 2),\n (workInProgress.flags |= 128))\n : (suspenseContext &= 1);\n push(suspenseStackCursor, suspenseContext);\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n nextProps = isHydrating ? treeForkCount : 0;\n if (!shouldForceFallback && null !== current && 0 !== (current.flags & 128))\n a: for (current = workInProgress.child; null !== current; ) {\n if (13 === current.tag)\n null !== current.memoizedState &&\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (19 === current.tag)\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (null !== current.child) {\n current.child.return = current;\n current = current.child;\n continue;\n }\n if (current === workInProgress) break a;\n for (; null === current.sibling; ) {\n if (null === current.return || current.return === workInProgress)\n break a;\n current = current.return;\n }\n current.sibling.return = current.return;\n current = current.sibling;\n }\n switch (revealOrder) {\n case \"forwards\":\n renderLanes = workInProgress.child;\n for (revealOrder = null; null !== renderLanes; )\n (current = renderLanes.alternate),\n null !== current &&\n null === findFirstSuspended(current) &&\n (revealOrder = renderLanes),\n (renderLanes = renderLanes.sibling);\n renderLanes = revealOrder;\n null === renderLanes\n ? ((revealOrder = workInProgress.child), (workInProgress.child = null))\n : ((revealOrder = renderLanes.sibling), (renderLanes.sibling = null));\n initSuspenseListRenderState(\n workInProgress,\n !1,\n revealOrder,\n renderLanes,\n tailMode,\n nextProps\n );\n break;\n case \"backwards\":\n case \"unstable_legacy-backwards\":\n renderLanes = null;\n revealOrder = workInProgress.child;\n for (workInProgress.child = null; null !== revealOrder; ) {\n current = revealOrder.alternate;\n if (null !== current && null === findFirstSuspended(current)) {\n workInProgress.child = revealOrder;\n break;\n }\n current = revealOrder.sibling;\n revealOrder.sibling = renderLanes;\n renderLanes = revealOrder;\n revealOrder = current;\n }\n initSuspenseListRenderState(\n workInProgress,\n !0,\n renderLanes,\n null,\n tailMode,\n nextProps\n );\n break;\n case \"together\":\n initSuspenseListRenderState(\n workInProgress,\n !1,\n null,\n null,\n void 0,\n nextProps\n );\n break;\n default:\n workInProgress.memoizedState = null;\n }\n return workInProgress.child;\n}\nfunction bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {\n null !== current && (workInProgress.dependencies = current.dependencies);\n workInProgressRootSkippedLanes |= workInProgress.lanes;\n if (0 === (renderLanes & workInProgress.childLanes))\n if (null !== current) {\n if (\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n 0 === (renderLanes & workInProgress.childLanes))\n )\n return null;\n } else return null;\n if (null !== current && workInProgress.child !== current.child)\n throw Error(formatProdErrorMessage(153));\n if (null !== workInProgress.child) {\n current = workInProgress.child;\n renderLanes = createWorkInProgress(current, current.pendingProps);\n workInProgress.child = renderLanes;\n for (renderLanes.return = workInProgress; null !== current.sibling; )\n (current = current.sibling),\n (renderLanes = renderLanes.sibling =\n createWorkInProgress(current, current.pendingProps)),\n (renderLanes.return = workInProgress);\n renderLanes.sibling = null;\n }\n return workInProgress.child;\n}\nfunction checkScheduledUpdateOrContext(current, renderLanes) {\n if (0 !== (current.lanes & renderLanes)) return !0;\n current = current.dependencies;\n return null !== current && checkIfContextChanged(current) ? !0 : !1;\n}\nfunction attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n) {\n switch (workInProgress.tag) {\n case 3:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n resetHydrationState();\n break;\n case 27:\n case 5:\n pushHostContext(workInProgress);\n break;\n case 4:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n break;\n case 10:\n pushProvider(\n workInProgress,\n workInProgress.type,\n workInProgress.memoizedProps.value\n );\n break;\n case 31:\n if (null !== workInProgress.memoizedState)\n return (\n (workInProgress.flags |= 128),\n pushDehydratedActivitySuspenseHandler(workInProgress),\n null\n );\n break;\n case 13:\n var state$102 = workInProgress.memoizedState;\n if (null !== state$102) {\n if (null !== state$102.dehydrated)\n return (\n pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags |= 128),\n null\n );\n if (0 !== (renderLanes & workInProgress.child.childLanes))\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n pushPrimaryTreeSuspenseHandler(workInProgress);\n current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n return null !== current ? current.sibling : null;\n }\n pushPrimaryTreeSuspenseHandler(workInProgress);\n break;\n case 19:\n var didSuspendBefore = 0 !== (current.flags & 128);\n state$102 = 0 !== (renderLanes & workInProgress.childLanes);\n state$102 ||\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n (state$102 = 0 !== (renderLanes & workInProgress.childLanes)));\n if (didSuspendBefore) {\n if (state$102)\n return updateSuspenseListComponent(\n current,\n workInProgress,\n renderLanes\n );\n workInProgress.flags |= 128;\n }\n didSuspendBefore = workInProgress.memoizedState;\n null !== didSuspendBefore &&\n ((didSuspendBefore.rendering = null),\n (didSuspendBefore.tail = null),\n (didSuspendBefore.lastEffect = null));\n push(suspenseStackCursor, suspenseStackCursor.current);\n if (state$102) break;\n else return null;\n case 22:\n return (\n (workInProgress.lanes = 0),\n updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n )\n );\n case 24:\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n }\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n}\nfunction beginWork(current, workInProgress, renderLanes) {\n if (null !== current)\n if (current.memoizedProps !== workInProgress.pendingProps)\n didReceiveUpdate = !0;\n else {\n if (\n !checkScheduledUpdateOrContext(current, renderLanes) &&\n 0 === (workInProgress.flags & 128)\n )\n return (\n (didReceiveUpdate = !1),\n attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n )\n );\n didReceiveUpdate = 0 !== (current.flags & 131072) ? !0 : !1;\n }\n else\n (didReceiveUpdate = !1),\n isHydrating &&\n 0 !== (workInProgress.flags & 1048576) &&\n pushTreeId(workInProgress, treeForkCount, workInProgress.index);\n workInProgress.lanes = 0;\n switch (workInProgress.tag) {\n case 16:\n a: {\n var props = workInProgress.pendingProps;\n current = resolveLazy(workInProgress.elementType);\n workInProgress.type = current;\n if (\"function\" === typeof current)\n shouldConstruct(current)\n ? ((props = resolveClassComponentProps(current, props)),\n (workInProgress.tag = 1),\n (workInProgress = updateClassComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )))\n : ((workInProgress.tag = 0),\n (workInProgress = updateFunctionComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )));\n else {\n if (void 0 !== current && null !== current) {\n var $$typeof = current.$$typeof;\n if ($$typeof === REACT_FORWARD_REF_TYPE) {\n workInProgress.tag = 11;\n workInProgress = updateForwardRef(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n } else if ($$typeof === REACT_MEMO_TYPE) {\n workInProgress.tag = 14;\n workInProgress = updateMemoComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n }\n }\n workInProgress = getComponentNameFromType(current) || current;\n throw Error(formatProdErrorMessage(306, workInProgress, \"\"));\n }\n }\n return workInProgress;\n case 0:\n return updateFunctionComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 1:\n return (\n (props = workInProgress.type),\n ($$typeof = resolveClassComponentProps(\n props,\n workInProgress.pendingProps\n )),\n updateClassComponent(\n current,\n workInProgress,\n props,\n $$typeof,\n renderLanes\n )\n );\n case 3:\n a: {\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n );\n if (null === current) throw Error(formatProdErrorMessage(387));\n props = workInProgress.pendingProps;\n var prevState = workInProgress.memoizedState;\n $$typeof = prevState.element;\n cloneUpdateQueue(current, workInProgress);\n processUpdateQueue(workInProgress, props, null, renderLanes);\n var nextState = workInProgress.memoizedState;\n props = nextState.cache;\n pushProvider(workInProgress, CacheContext, props);\n props !== prevState.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n );\n suspendIfUpdateReadFromEntangledAsyncAction();\n props = nextState.element;\n if (prevState.isDehydrated)\n if (\n ((prevState = {\n element: props,\n isDehydrated: !1,\n cache: nextState.cache\n }),\n (workInProgress.updateQueue.baseState = prevState),\n (workInProgress.memoizedState = prevState),\n workInProgress.flags & 256)\n ) {\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else if (props !== $$typeof) {\n $$typeof = createCapturedValueAtFiber(\n Error(formatProdErrorMessage(424)),\n workInProgress\n );\n queueHydrationError($$typeof);\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else {\n current = workInProgress.stateNode.containerInfo;\n switch (current.nodeType) {\n case 9:\n current = current.body;\n break;\n default:\n current =\n \"HTML\" === current.nodeName\n ? current.ownerDocument.body\n : current;\n }\n nextHydratableInstance = getNextHydratable(current.firstChild);\n hydrationParentFiber = workInProgress;\n isHydrating = !0;\n hydrationErrors = null;\n rootOrSingletonContext = !0;\n renderLanes = mountChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n );\n for (workInProgress.child = renderLanes; renderLanes; )\n (renderLanes.flags = (renderLanes.flags & -3) | 4096),\n (renderLanes = renderLanes.sibling);\n }\n else {\n resetHydrationState();\n if (props === $$typeof) {\n workInProgress = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n break a;\n }\n reconcileChildren(current, workInProgress, props, renderLanes);\n }\n workInProgress = workInProgress.child;\n }\n return workInProgress;\n case 26:\n return (\n markRef(current, workInProgress),\n null === current\n ? (renderLanes = getResource(\n workInProgress.type,\n null,\n workInProgress.pendingProps,\n null\n ))\n ? (workInProgress.memoizedState = renderLanes)\n : isHydrating ||\n ((renderLanes = workInProgress.type),\n (current = workInProgress.pendingProps),\n (props = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n ).createElement(renderLanes)),\n (props[internalInstanceKey] = workInProgress),\n (props[internalPropsKey] = current),\n setInitialProperties(props, renderLanes, current),\n markNodeAsHoistable(props),\n (workInProgress.stateNode = props))\n : (workInProgress.memoizedState = getResource(\n workInProgress.type,\n current.memoizedProps,\n workInProgress.pendingProps,\n current.memoizedState\n )),\n null\n );\n case 27:\n return (\n pushHostContext(workInProgress),\n null === current &&\n isHydrating &&\n ((props = workInProgress.stateNode =\n resolveSingletonInstance(\n workInProgress.type,\n workInProgress.pendingProps,\n rootInstanceStackCursor.current\n )),\n (hydrationParentFiber = workInProgress),\n (rootOrSingletonContext = !0),\n ($$typeof = nextHydratableInstance),\n isSingletonScope(workInProgress.type)\n ? ((previousHydratableOnEnteringScopedSingleton = $$typeof),\n (nextHydratableInstance = getNextHydratable(props.firstChild)))\n : (nextHydratableInstance = $$typeof)),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n markRef(current, workInProgress),\n null === current && (workInProgress.flags |= 4194304),\n workInProgress.child\n );\n case 5:\n if (null === current && isHydrating) {\n if (($$typeof = props = nextHydratableInstance))\n (props = canHydrateInstance(\n props,\n workInProgress.type,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== props\n ? ((workInProgress.stateNode = props),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = getNextHydratable(props.firstChild)),\n (rootOrSingletonContext = !1),\n ($$typeof = !0))\n : ($$typeof = !1);\n $$typeof || throwOnHydrationMismatch(workInProgress);\n }\n pushHostContext(workInProgress);\n $$typeof = workInProgress.type;\n prevState = workInProgress.pendingProps;\n nextState = null !== current ? current.memoizedProps : null;\n props = prevState.children;\n shouldSetTextContent($$typeof, prevState)\n ? (props = null)\n : null !== nextState &&\n shouldSetTextContent($$typeof, nextState) &&\n (workInProgress.flags |= 32);\n null !== workInProgress.memoizedState &&\n (($$typeof = renderWithHooks(\n current,\n workInProgress,\n TransitionAwareHostComponent,\n null,\n null,\n renderLanes\n )),\n (HostTransitionContext._currentValue = $$typeof));\n markRef(current, workInProgress);\n reconcileChildren(current, workInProgress, props, renderLanes);\n return workInProgress.child;\n case 6:\n if (null === current && isHydrating) {\n if ((current = renderLanes = nextHydratableInstance))\n (renderLanes = canHydrateTextInstance(\n renderLanes,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== renderLanes\n ? ((workInProgress.stateNode = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null),\n (current = !0))\n : (current = !1);\n current || throwOnHydrationMismatch(workInProgress);\n }\n return null;\n case 13:\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n case 4:\n return (\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n ),\n (props = workInProgress.pendingProps),\n null === current\n ? (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n ))\n : reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 11:\n return updateForwardRef(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 7:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps,\n renderLanes\n ),\n workInProgress.child\n );\n case 8:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 12:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 10:\n return (\n (props = workInProgress.pendingProps),\n pushProvider(workInProgress, workInProgress.type, props.value),\n reconcileChildren(current, workInProgress, props.children, renderLanes),\n workInProgress.child\n );\n case 9:\n return (\n ($$typeof = workInProgress.type._context),\n (props = workInProgress.pendingProps.children),\n prepareToReadContext(workInProgress),\n ($$typeof = readContext($$typeof)),\n (props = props($$typeof)),\n (workInProgress.flags |= 1),\n reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 14:\n return updateMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 15:\n return updateSimpleMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 19:\n return updateSuspenseListComponent(current, workInProgress, renderLanes);\n case 31:\n return updateActivityComponent(current, workInProgress, renderLanes);\n case 22:\n return updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n );\n case 24:\n return (\n prepareToReadContext(workInProgress),\n (props = readContext(CacheContext)),\n null === current\n ? (($$typeof = peekCacheFromPool()),\n null === $$typeof &&\n (($$typeof = workInProgressRoot),\n (prevState = createCache()),\n ($$typeof.pooledCache = prevState),\n prevState.refCount++,\n null !== prevState && ($$typeof.pooledCacheLanes |= renderLanes),\n ($$typeof = prevState)),\n (workInProgress.memoizedState = { parent: props, cache: $$typeof }),\n initializeUpdateQueue(workInProgress),\n pushProvider(workInProgress, CacheContext, $$typeof))\n : (0 !== (current.lanes & renderLanes) &&\n (cloneUpdateQueue(current, workInProgress),\n processUpdateQueue(workInProgress, null, null, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction()),\n ($$typeof = current.memoizedState),\n (prevState = workInProgress.memoizedState),\n $$typeof.parent !== props\n ? (($$typeof = { parent: props, cache: props }),\n (workInProgress.memoizedState = $$typeof),\n 0 === workInProgress.lanes &&\n (workInProgress.memoizedState =\n workInProgress.updateQueue.baseState =\n $$typeof),\n pushProvider(workInProgress, CacheContext, props))\n : ((props = prevState.cache),\n pushProvider(workInProgress, CacheContext, props),\n props !== $$typeof.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n ))),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 29:\n throw workInProgress.pendingProps;\n }\n throw Error(formatProdErrorMessage(156, workInProgress.tag));\n}\nfunction markUpdate(workInProgress) {\n workInProgress.flags |= 4;\n}\nfunction preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n oldProps,\n newProps,\n renderLanes\n) {\n if ((type = 0 !== (workInProgress.mode & 32))) type = !1;\n if (type) {\n if (\n ((workInProgress.flags |= 16777216),\n (renderLanes & 335544128) === renderLanes)\n )\n if (workInProgress.stateNode.complete) workInProgress.flags |= 8192;\n else if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n } else workInProgress.flags &= -16777217;\n}\nfunction preloadResourceAndSuspendIfNeeded(workInProgress, resource) {\n if (\"stylesheet\" !== resource.type || 0 !== (resource.state.loading & 4))\n workInProgress.flags &= -16777217;\n else if (((workInProgress.flags |= 16777216), !preloadResource(resource)))\n if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n}\nfunction scheduleRetryEffect(workInProgress, retryQueue) {\n null !== retryQueue && (workInProgress.flags |= 4);\n workInProgress.flags & 16384 &&\n ((retryQueue =\n 22 !== workInProgress.tag ? claimNextRetryLane() : 536870912),\n (workInProgress.lanes |= retryQueue),\n (workInProgressSuspendedRetryLanes |= retryQueue));\n}\nfunction cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {\n if (!isHydrating)\n switch (renderState.tailMode) {\n case \"hidden\":\n hasRenderedATailFallback = renderState.tail;\n for (var lastTailNode = null; null !== hasRenderedATailFallback; )\n null !== hasRenderedATailFallback.alternate &&\n (lastTailNode = hasRenderedATailFallback),\n (hasRenderedATailFallback = hasRenderedATailFallback.sibling);\n null === lastTailNode\n ? (renderState.tail = null)\n : (lastTailNode.sibling = null);\n break;\n case \"collapsed\":\n lastTailNode = renderState.tail;\n for (var lastTailNode$106 = null; null !== lastTailNode; )\n null !== lastTailNode.alternate && (lastTailNode$106 = lastTailNode),\n (lastTailNode = lastTailNode.sibling);\n null === lastTailNode$106\n ? hasRenderedATailFallback || null === renderState.tail\n ? (renderState.tail = null)\n : (renderState.tail.sibling = null)\n : (lastTailNode$106.sibling = null);\n }\n}\nfunction bubbleProperties(completedWork) {\n var didBailout =\n null !== completedWork.alternate &&\n completedWork.alternate.child === completedWork.child,\n newChildLanes = 0,\n subtreeFlags = 0;\n if (didBailout)\n for (var child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags & 65011712),\n (subtreeFlags |= child$107.flags & 65011712),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n else\n for (child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags),\n (subtreeFlags |= child$107.flags),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n completedWork.subtreeFlags |= subtreeFlags;\n completedWork.childLanes = newChildLanes;\n return didBailout;\n}\nfunction completeWork(current, workInProgress, renderLanes) {\n var newProps = workInProgress.pendingProps;\n popTreeContext(workInProgress);\n switch (workInProgress.tag) {\n case 16:\n case 15:\n case 0:\n case 11:\n case 7:\n case 8:\n case 12:\n case 9:\n case 14:\n return bubbleProperties(workInProgress), null;\n case 1:\n return bubbleProperties(workInProgress), null;\n case 3:\n renderLanes = workInProgress.stateNode;\n newProps = null;\n null !== current && (newProps = current.memoizedState.cache);\n workInProgress.memoizedState.cache !== newProps &&\n (workInProgress.flags |= 2048);\n popProvider(CacheContext);\n popHostContainer();\n renderLanes.pendingContext &&\n ((renderLanes.context = renderLanes.pendingContext),\n (renderLanes.pendingContext = null));\n if (null === current || null === current.child)\n popHydrationState(workInProgress)\n ? markUpdate(workInProgress)\n : null === current ||\n (current.memoizedState.isDehydrated &&\n 0 === (workInProgress.flags & 256)) ||\n ((workInProgress.flags |= 1024),\n upgradeHydrationErrorsToRecoverable());\n bubbleProperties(workInProgress);\n return null;\n case 26:\n var type = workInProgress.type,\n nextResource = workInProgress.memoizedState;\n null === current\n ? (markUpdate(workInProgress),\n null !== nextResource\n ? (bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n null,\n newProps,\n renderLanes\n )))\n : nextResource\n ? nextResource !== current.memoizedState\n ? (markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n (workInProgress.flags &= -16777217))\n : ((current = current.memoizedProps),\n current !== newProps && markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n current,\n newProps,\n renderLanes\n ));\n return null;\n case 27:\n popHostContext(workInProgress);\n renderLanes = rootInstanceStackCursor.current;\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n current = contextStackCursor.current;\n popHydrationState(workInProgress)\n ? prepareToHydrateHostInstance(workInProgress, current)\n : ((current = resolveSingletonInstance(type, newProps, renderLanes)),\n (workInProgress.stateNode = current),\n markUpdate(workInProgress));\n }\n bubbleProperties(workInProgress);\n return null;\n case 5:\n popHostContext(workInProgress);\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n nextResource = contextStackCursor.current;\n if (popHydrationState(workInProgress))\n prepareToHydrateHostInstance(workInProgress, nextResource);\n else {\n var ownerDocument = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n );\n switch (nextResource) {\n case 1:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case 2:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n default:\n switch (type) {\n case \"svg\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case \"math\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n case \"script\":\n nextResource = ownerDocument.createElement(\"div\");\n nextResource.innerHTML = \" + diff --git a/chrome-extension/public/test_api_keys.html b/chrome-extension/public/test_api_keys.html new file mode 100644 index 00000000..516d2082 --- /dev/null +++ b/chrome-extension/public/test_api_keys.html @@ -0,0 +1,393 @@ + + + + + + API Key Diagnostics Test + + + +
+

🔑 API Key Diagnostics Test

+

This test will diagnose why GET_API_KEY messages are timing out in the offscreen document.

+ + + +
+ + + +
+ +
+ Click a button above to run tests and see results here... +
+ +
+

📋 Test Instructions:

+
    +
  1. Make sure the Chrome extension is loaded and active
  2. +
  3. Open this test page in a new tab
  4. +
  5. Click "Run API Key Retrieval Test" first
  6. +
  7. Then try "Check Storage for Encrypted Keys"
  8. +
  9. Analyze the console output and results for timeout issues
  10. +
+
+
+ + + + \ No newline at end of file diff --git a/chrome-extension/public/test_api_keys.js b/chrome-extension/public/test_api_keys.js new file mode 100644 index 00000000..941c4dbc --- /dev/null +++ b/chrome-extension/public/test_api_keys.js @@ -0,0 +1,276 @@ +// API Key Diagnostics Test Script +let originalConsoleLog = console.log; +let originalConsoleError = console.error; +let originalConsoleWarn = console.warn; +let capturedLogs = []; + +// Capture console output +function captureConsoleOutput() { + console.log = function(...args) { + capturedLogs.push(['log', ...args]); + originalConsoleLog.apply(console, args); + }; + console.error = function(...args) { + capturedLogs.push(['error', ...args]); + originalConsoleError.apply(console, args); + }; + console.warn = function(...args) { + capturedLogs.push(['warn', ...args]); + originalConsoleWarn.apply(console, args); + }; +} + +// Restore console output +function restoreConsoleOutput() { + console.log = originalConsoleLog; + console.error = originalConsoleError; + console.warn = originalConsoleWarn; +} + +// Format captured logs for display +function formatCapturedLogs() { + return capturedLogs.map(([level, ...args]) => { + const timestamp = new Date().toLocaleTimeString(); + const prefix = `[${timestamp}] ${level.toUpperCase()}:`; + return `${prefix} ${args.join(' ')}`; + }).join('\n'); +} + +// Clear captured logs +function clearCapturedLogs() { + capturedLogs = []; +} + +// Update status display +function updateStatus(message, type = 'info') { + const statusEl = document.getElementById('status'); + statusEl.textContent = message; + statusEl.className = `status ${type}`; + statusEl.style.display = 'block'; +} + +// Update results display +function updateResults(content) { + const resultsEl = document.getElementById('results'); + resultsEl.textContent = content; +} + +// Enable/disable buttons +function setButtonsEnabled(enabled) { + document.getElementById('testAPIKeyBtn').disabled = !enabled; + document.getElementById('checkStorageBtn').disabled = !enabled; +} + +// Test if offscreen document is accessible +async function testOffscreenAccess() { + try { + // Try to access the offscreen document + const offscreenUrl = chrome.runtime.getURL('offscreen.html'); + const response = await fetch(offscreenUrl); + if (response.ok) { + return { success: true, message: 'Offscreen document accessible' }; + } else { + return { success: false, message: `Offscreen document returned ${response.status}` }; + } + } catch (error) { + return { success: false, message: `Cannot access offscreen document: ${error.message}` }; + } +} + +// Run API Key Retrieval Test +async function runAPIKeyTest() { + updateStatus('🔄 Running API Key Retrieval Test...', 'running'); + setButtonsEnabled(false); + clearCapturedLogs(); + captureConsoleOutput(); + + try { + // First check if we can access the offscreen document + const offscreenCheck = await testOffscreenAccess(); + console.log('[TEST_FRAMEWORK] Offscreen access check:', offscreenCheck); + + // Try to communicate with offscreen document to test API key retrieval + console.log('[TEST_FRAMEWORK] Testing direct API key retrieval via GET_API_KEY message...'); + + // Test the specific key ID that was timing out: 'ozon-analyzer-basic_analysis-ru' + const testKeyId = 'ozon-analyzer-basic_analysis-ru'; + console.log(`[TEST_FRAMEWORK] Testing key ID: ${testKeyId}`); + + const startTime = Date.now(); + const firstTestMessage = { + type: 'GET_API_KEY', + data: { keyId: testKeyId }, + testName: 'api_key_timeout_test', + requestId: 'test_' + Date.now(), + timestamp: Date.now() + }; + + console.log('[TEST_FRAMEWORK] Sending GET_API_KEY message:', JSON.stringify(firstTestMessage, null, 2)); + + const firstResponse = await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error(`Message timeout after 10000ms for keyId: ${testKeyId}`)); + }, 10000); // 10 second timeout + + chrome.runtime.sendMessage(firstTestMessage, (response) => { + clearTimeout(timeoutId); + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(response); + } + }); + }); + + const endTime = Date.now(); + const duration = endTime - startTime; + + console.log(`[TEST_FRAMEWORK] Response received in ${duration}ms`); + console.log('[TEST_FRAMEWORK] Response:', firstResponse); + + if (firstResponse && firstResponse.apiKey) { + console.log('[TEST_FRAMEWORK] ✅ API key retrieved successfully'); + console.log(`[TEST_FRAMEWORK] Key length: ${firstResponse.apiKey.length} characters`); + console.log(`[TEST_FRAMEWORK] Key starts with: ${firstResponse.apiKey.substring(0, 10)}...`); + + // Test another key ID to verify the fix + const testKeyId2 = 'ozon-analyzer-deep_analysis-en'; + console.log(`[TEST_FRAMEWORK] Testing second key ID: ${testKeyId2}`); + + const testMessage2 = { + type: 'GET_API_KEY', + data: { keyId: testKeyId2 }, + testName: 'api_key_timeout_test_2', + requestId: 'test2_' + Date.now(), + timestamp: Date.now() + }; + + const startTime2 = Date.now(); + const response2 = await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error(`Message timeout after 10000ms for keyId: ${testKeyId2}`)); + }, 10000); + + chrome.runtime.sendMessage(testMessage2, (response) => { + clearTimeout(timeoutId); + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(response); + } + }); + }); + + const endTime2 = Date.now(); + const duration2 = endTime2 - startTime2; + + console.log(`[TEST_FRAMEWORK] Second response received in ${duration2}ms`); + console.log('[TEST_FRAMEWORK] Second response:', response2); + + if (response2 && response2.apiKey) { + console.log('[TEST_FRAMEWORK] ✅ Second API key retrieved successfully'); + console.log(`[TEST_FRAMEWORK] Second key length: ${response2.apiKey.length} characters`); + } else { + console.log('[TEST_FRAMEWORK] ❌ Second API key retrieval failed'); + console.log('[TEST_FRAMEWORK] Second response error:', response2?.error); + } + + } else { + console.log('[TEST_FRAMEWORK] ❌ API key retrieval failed'); + console.log('[TEST_FRAMEWORK] Response error:', firstResponse?.error); + } + + // Test completed immediately - no need for additional wait + // Wait a moment for all logs to be captured + setTimeout(() => { + restoreConsoleOutput(); + const results = formatCapturedLogs(); + updateResults(results); + updateStatus('✅ API Key Retrieval Test Completed', 'success'); + setButtonsEnabled(true); + }, 1000); + + } catch (error) { + restoreConsoleOutput(); + console.error('[TEST_FRAMEWORK] Test execution failed:', error); + updateResults(`❌ Test execution failed: ${error.message}\n\nCaptured logs:\n${formatCapturedLogs()}`); + updateStatus('❌ Test Failed', 'error'); + setButtonsEnabled(true); + } +} + +// Run Storage Check +async function runStorageCheck() { + updateStatus('🔄 Checking Storage for Encrypted Keys...', 'running'); + setButtonsEnabled(false); + clearCapturedLogs(); + captureConsoleOutput(); + + try { + console.log('[TEST_FRAMEWORK] Starting storage check...'); + + const testMessage = { + type: 'EXECUTE_PYTHON_CODE', + code: ` +import js +# Call the storage check function if available +if hasattr(js, 'checkEncryptedAPIKeys'): + result = js.checkEncryptedAPIKeys() + print("[PYTHON_TEST] checkEncryptedAPIKeys called") +else: + print("[PYTHON_TEST] checkEncryptedAPIKeys not available in js bridge") + `, + testName: 'storage_check', + requestId: 'storage_' + Date.now(), + timestamp: Date.now() + }; + + console.log('[TEST_FRAMEWORK] Sending storage check message to offscreen:', testMessage.type); + + const response = await new Promise((resolve, reject) => { + chrome.runtime.sendMessage(testMessage, (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(response); + } + }); + }); + + console.log('[TEST_FRAMEWORK] Received response from offscreen:', response); + + // Wait a moment for all logs to be captured + setTimeout(() => { + restoreConsoleOutput(); + const results = formatCapturedLogs(); + updateResults(results); + updateStatus('✅ Storage Check Completed', 'success'); + setButtonsEnabled(true); + }, 1000); + + } catch (error) { + restoreConsoleOutput(); + console.error('[TEST_FRAMEWORK] Storage check failed:', error); + updateResults(`❌ Storage check failed: ${error.message}\n\nCaptured logs:\n${formatCapturedLogs()}`); + updateStatus('❌ Storage Check Failed', 'error'); + setButtonsEnabled(true); + } +} + +// Clear results +function clearResults() { + updateResults('Results cleared. Click a button above to run tests...'); + document.getElementById('status').style.display = 'none'; + clearCapturedLogs(); +} + +// Initialize +document.addEventListener('DOMContentLoaded', function() { + console.log('[TEST_FRAMEWORK] API Key Diagnostics Test page loaded'); + updateStatus('Ready to run tests', 'success'); +}); + +// Make functions globally available +window.runAPIKeyTest = runAPIKeyTest; +window.runStorageCheck = runStorageCheck; +window.clearResults = clearResults; \ No newline at end of file diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index 390906cb..49b06444 100755 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -15,6 +15,7 @@ import { getApiKeyForModel, callAiModel } from './ai-api-client'; import { exampleThemeStorage, pluginSettingsStorage, getPluginSettings } from '@extension/storage'; import type { PluginSettings } from '@extension/storage'; import { ensureOffscreenDocument } from '../../../src/background/offscreen-manager'; +import { APIKeyManager } from '../../../pages/options/src/utils/encryption'; console.log('[background] Storage modules loaded'); // Глобальный счетчик для генерации уникальных messageId @@ -1459,72 +1460,56 @@ chrome.runtime.onMessage.addListener( } // ШАГ 5: Проверить настройки плагина с manifest defaults и пользовательскими настройками - const customSettingsKeys = manifest?.options ? Object.keys(manifest.options) : []; + const customSettingsKeys = manifest?.options ? Object.keys(manifest.options).filter((key: string) => key !== 'prompts') : []; console.log('[BACKGROUND] 🔍 Начинаем получение настроек плагина:', msg.pluginId); - console.log('[BACKGROUND] 📋 Доступные ключи настроек:', customSettingsKeys); + console.log('[BACKGROUND] 📋 Доступные ключи настроек (исключая prompts):', customSettingsKeys); console.log('[BACKGROUND] 📋 Manifest defaults:', manifestDefaults); const pluginSettings = await getPluginSettings(msg.pluginId, manifestDefaults, customSettingsKeys); + console.log('[BACKGROUND] ✅ Настройки плагина получены из chrome.storage.local:', pluginSettings); + console.log('[BACKGROUND] 🔑 Значение response_language:', pluginSettings?.response_language); + console.log('[BACKGROUND] 📝 Prompts loaded from manifest:', !!pluginSettings.prompts); + console.log('[BACKGROUND] 📊 Все полученные настройки плагина:', JSON.stringify(pluginSettings, null, 2)); - // Загрузка LLM настроек и API ключей для ozon-analyzer плагина + // Load prompts from manifest.json for all plugins let enrichedPluginSettings = { ...pluginSettings }; - if (msg.pluginId === 'ozon-analyzer') { - try { - console.log('[BACKGROUND] 🔧 Загружаем LLM настройки и API ключи для ozon-analyzer'); - // Загружаем LLM настройки из специального хранилища - const llmSettingsResult = await chrome.storage.local.get(['plugin-ozon-analyzer-settings']); - const llmSettings = llmSettingsResult['plugin-ozon-analyzer-settings']; + // Load prompts from manifest.json if available + if (manifest?.options?.prompts) { + console.log('[BACKGROUND] 📝 Loading prompts from manifest.json for plugin:', msg.pluginId); - if (llmSettings) { - console.log('[BACKGROUND] ✅ Найдены LLM настройки:', JSON.stringify(llmSettings, null, 2)); + const manifestPrompts = manifest.options.prompts; + enrichedPluginSettings.prompts = {}; - // Добавляем llm_settings в pluginSettings - enrichedPluginSettings.llm_settings = { - basic_analysis: llmSettings.basic_analysis, - deep_analysis: llmSettings.deep_analysis - }; + // Process each prompt type (basic_analysis, deep_analysis, etc.) + for (const [promptType, promptConfig] of Object.entries(manifestPrompts)) { + (enrichedPluginSettings.prompts as any)[promptType] = {}; + + // Process each language (ru, en, etc.) + for (const [language, languageConfig] of Object.entries(promptConfig as any)) { + const defaultPrompt = (languageConfig as any).default; + const llmConfig = (languageConfig as any).LLM; + + // Check if Default LLM is defined + const defaultLLM = llmConfig?.default; + if (!defaultLLM) { + console.warn(`[BACKGROUND] ⚠️ WARNING: No Default LLM defined for ${promptType} in ${language} for plugin ${msg.pluginId}`); + console.warn(`[BACKGROUND] ⚠️ Request will be impossible without Default LLM configuration`); + } - // Добавляем api_keys в pluginSettings - enrichedPluginSettings.api_keys = llmSettings.api_keys; - - console.log('[BACKGROUND] ✅ LLM настройки добавлены в pluginSettings'); - } else { - console.log('[BACKGROUND] ⚠️ LLM настройки не найдены в хранилище'); - // Используем дефолтные значения если настройки не найдены - enrichedPluginSettings.llm_settings = { - basic_analysis: { - ru: { llm: '', custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.' }, - en: { llm: '', custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.' } - }, - deep_analysis: { - ru: { llm: '', custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.' }, - en: { llm: '', custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.' } - } + (enrichedPluginSettings.prompts as any)[promptType][language] = { + custom_prompt: defaultPrompt, + llm: defaultLLM || null }; - enrichedPluginSettings.api_keys = { default: '' }; } - } catch (llmError) { - console.error('[BACKGROUND] ❌ Ошибка загрузки LLM настроек:', llmError); - // Используем дефолтные значения при ошибке - enrichedPluginSettings.llm_settings = { - basic_analysis: { - ru: { llm: '', custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.' }, - en: { llm: '', custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.' } - }, - deep_analysis: { - ru: { llm: '', custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.' }, - en: { llm: '', custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.' } - } - }; - enrichedPluginSettings.api_keys = { default: '' }; } + + console.log('[BACKGROUND] ✅ Prompts loaded from manifest.json:', JSON.stringify(enrichedPluginSettings.prompts, null, 2)); + } else { + console.log('[BACKGROUND] ℹ️ No prompts defined in manifest.json for plugin:', msg.pluginId); } - console.log('[BACKGROUND] ✅ Настройки плагина получены из chrome.storage.local:', enrichedPluginSettings); - console.log('[BACKGROUND] 🔑 Значение response_language:', enrichedPluginSettings?.response_language); - console.log('[BACKGROUND] 📊 Все полученные настройки плагина:', JSON.stringify(enrichedPluginSettings, null, 2)); - if (!pluginSettings.enabled) { + if (!enrichedPluginSettings.enabled) { console.log('[background][RUN_WORKFLOW][INFO] Plugin disabled'); sendResponse({ error: 'Плагин отключен' }); return true; @@ -1638,7 +1623,7 @@ chrome.runtime.onMessage.addListener( const requestId = msg.requestId || `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const transferId = `${requestId}_html_${Date.now()}`; - // Используем enrichedPluginSettings (с LLM настройками) вместо обычных pluginSettings + // Используем enrichedPluginSettings (с prompts из manifest.json) вместо обычных pluginSettings const settingsToSend = enrichedPluginSettings; if (htmlTransmissionMode === 'direct') { @@ -1665,7 +1650,7 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked fallback'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', settingsToSend); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', JSON.stringify(settingsToSend, null, 2)); const transferState = { chunks: chunkingResult.chunks, @@ -1707,7 +1692,7 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked передачи'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', settingsToSend); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', JSON.stringify(settingsToSend, null, 2)); console.log('[BACKGROUND] 🔑 Response language в transfer metadata:', settingsToSend?.response_language); const transferState = { @@ -2122,6 +2107,38 @@ chrome.runtime.onMessage.addListener( return true; // Указываем асинхронную обработку } + // === GET_API_KEY: Обработчик запросов API ключей === + if (msg.type === 'GET_API_KEY') { + console.log('[background][GET_API_KEY] 📨 Received GET_API_KEY request'); + console.log('[background][GET_API_KEY] Key ID:', msg.data?.keyId); + + (async () => { + try { + if (!msg.data?.keyId) { + console.error('[background][GET_API_KEY] ❌ Missing keyId in request'); + sendResponse({ apiKey: null, error: 'Missing keyId' }); + return; + } + + const apiKey = await APIKeyManager.getDecryptedKey(msg.data.keyId); + console.log('[background][GET_API_KEY] ✅ API key retrieved:', !!apiKey); + + sendResponse({ + apiKey: apiKey || null + }); + + } catch (error: unknown) { + console.error('[background][GET_API_KEY] ❌ Error retrieving API key:', error); + sendResponse({ + apiKey: null, + error: (error as Error).message + }); + } + })(); + + return true; // Указываем асинхронную обработку + } + // === DEBUG: Добавляем логи для отслеживания неизвестных сообщений === if (!msg.type) { console.log('[background][DEBUG] Получено сообщение:', msg); @@ -2185,13 +2202,14 @@ const handleHostApiMessage = async ( } case 'llm_call': { try { - const { modelAlias, options, pluginId } = message.data as { + const { modelAlias, options, pluginId, apiKeyId } = message.data as { modelAlias: string; options: any; - pluginId?: string + pluginId?: string; + apiKeyId?: string; }; - console.log('[HOST API] LLM call requested:', { modelAlias, pluginId }); + console.log('[HOST API] LLM call requested:', { modelAlias, pluginId, apiKeyId }); const currentPlugin = pluginId || 'ozon-analyzer'; const manifestUrl = chrome.runtime.getURL(`plugins/${currentPlugin}/manifest.json`); @@ -2227,11 +2245,15 @@ const handleHostApiMessage = async ( console.log('[HOST API] Using model:', actualModel, 'for alias:', modelAlias); - const apiKey = await getApiKeyForModel(actualModel); + // Получаем API-ключ: если apiKeyId указан, используем его, иначе используем model alias + const keyId = apiKeyId || actualModel; + console.log('[HOST API] Getting API key for:', keyId); + + const apiKey = await APIKeyManager.getDecryptedKey(keyId); if (!apiKey) { sendResponse({ error: true, - error_message: `API ключ для модели ${actualModel} не найден` + error_message: `API ключ для модели ${actualModel} не найден. KeyId: ${keyId}` }); return true; } @@ -2257,6 +2279,24 @@ const handleHostApiMessage = async ( } break; } + case 'GET_API_KEY': { + try { + const { keyId } = message.data as { keyId: string }; + console.log('[HOST API] Getting API key for:', keyId); + + const apiKey = await APIKeyManager.getDecryptedKey(keyId); + sendResponse({ + apiKey: apiKey || null + }); + } catch (error) { + console.error('[HOST API] Error getting API key:', error); + sendResponse({ + apiKey: null, + error: (error as Error).message + }); + } + break; + } case 'get_setting': { try { const { settingName, defaultValue, category, pluginId } = message.data as { @@ -2544,11 +2584,12 @@ async function handleMessage(message: any, sender: any): Promise { // Загрузить настройки из manifest.json для получения дефолтных значений const manifestUrl = chrome.runtime.getURL(`plugins/${message.pluginId}/manifest.json`); let manifestDefaults: Partial = {}; + let manifest: any = null; try { const manifestResponse = await fetch(manifestUrl); if (manifestResponse.ok) { - const manifest = await manifestResponse.json(); + manifest = await manifestResponse.json(); console.log(`[background][PORT][RUN_WORKFLOW] ✅ Manifest loaded for ${message.pluginId}:`, manifest); const manifestSettings = manifest.options || {}; manifestDefaults = { @@ -2567,7 +2608,8 @@ async function handleMessage(message: any, sender: any): Promise { console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 🔍 Получаем настройки плагина из chrome.storage.local'); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 📋 Manifest defaults:', manifestDefaults); - const settings = await getPluginSettings(message.pluginId, manifestDefaults); + const customSettingsKeys = manifest?.options ? Object.keys(manifest.options).filter((key: string) => key !== 'prompts') : []; + const settings = await getPluginSettings(message.pluginId, manifestDefaults, customSettingsKeys); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] ✅ Настройки плагина получены:', settings); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 🔑 Response language:', settings?.response_language); diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 0a33873f..1415969c 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.870", + "version": "1.0.907", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index 7fb5dfa1..c36bb720 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1395", + "version": "1.0.1432", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index acf6cd43..3dfa61f0 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 7ed8b9eb..b1abb73e 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1400", + "version": "0.5.1437", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 6f8bd072..05d6bb67 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 6b518236..39340b1e 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index f69ffd79..f25961ec 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 4a1be499..983f993c 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 8bcd7e7b..66287cd6 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 677ab957..8101447d 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index a45228ef..42711abd 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index d6664a14..3c4b60b0 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 98777aa3..bf41af8d 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1421", + "version": "0.5.1458", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index e7e71752..cf5ade72 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 47553218..132ac677 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 2c0aa6be..ff266d4a 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index ec3622d2..8406147f 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 5b334117..026dfde0 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 340915ce..ddec6514 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index a236543d..4d5607c8 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/APPOptions.tsx b/pages/options/src/components/APPOptions.tsx index 74e9ade1..be474f99 100755 --- a/pages/options/src/components/APPOptions.tsx +++ b/pages/options/src/components/APPOptions.tsx @@ -76,7 +76,7 @@ export const APPOptions: React.FC<{ isLight: boolean }> = ({ isLight }) => { onRemoveCustomKey={removeCustomKey} onUpdateKey={updateKey} onUpdateCustomKeyName={updateCustomKeyName} - getStatusText={status => getStatusText(status, t)} + getStatusText={getStatusText} getStatusClass={getStatusClass} theme={theme} onThemeChange={handleThemeChange} diff --git a/pages/options/src/hooks/usePluginSettings.ts b/pages/options/src/hooks/usePluginSettings.ts index 2b37f0de..fb310324 100755 --- a/pages/options/src/hooks/usePluginSettings.ts +++ b/pages/options/src/hooks/usePluginSettings.ts @@ -34,25 +34,63 @@ export interface PluginPromptSettings { const STORAGE_KEY = 'plugin-ozon-analyzer-settings'; +// Function to load default prompts from manifest.json +const loadDefaultPromptsFromManifest = async (): Promise<{ basic_analysis: { ru: string; en: string }; deep_analysis: { ru: string; en: string } }> => { + try { + // Load manifest.json from the plugin directory + const manifestResponse = await fetch(chrome.runtime.getURL('plugins/ozon-analyzer/manifest.json')); + const manifest = await manifestResponse.json(); + + const prompts = manifest.options?.prompts; + if (!prompts) { + throw new Error('Prompts not found in manifest'); + } + + return { + basic_analysis: { + ru: prompts.basic_analysis?.ru?.default || '', + en: prompts.basic_analysis?.en?.default || '', + }, + deep_analysis: { + ru: prompts.deep_analysis?.ru?.default || '', + en: prompts.deep_analysis?.en?.default || '', + }, + }; + } catch (error) { + console.error('Failed to load prompts from manifest:', error); + // Fallback to hardcoded defaults if manifest loading fails + return { + basic_analysis: { + ru: 'верни слово basic_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)', + en: 'верни слово basic_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)', + }, + deep_analysis: { + ru: 'верни слово deep_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)', + en: 'верни слово deep_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)', + }, + }; + } +}; + const DEFAULT_SETTINGS: PluginSettings = { basic_analysis: { ru: { llm: '', - custom_prompt: 'Проведи базовый анализ товара на Ozon. Опиши основные характеристики, преимущества и недостатки.', + custom_prompt: '', }, en: { llm: '', - custom_prompt: 'Perform basic analysis of the product on Ozon. Describe main characteristics, advantages and disadvantages.', + custom_prompt: '', }, }, deep_analysis: { ru: { llm: '', - custom_prompt: 'Проведи глубокий анализ товара на Ozon. Включи детальное описание, сравнение с конкурентами, анализ отзывов и рекомендации по улучшению.', + custom_prompt: '', }, en: { llm: '', - custom_prompt: 'Perform deep analysis of the product on Ozon. Include detailed description, competitor comparison, review analysis and improvement recommendations.', + custom_prompt: '', }, }, api_keys: { @@ -65,10 +103,51 @@ export const usePluginSettings = () => { const [isLoading, setIsLoading] = React.useState(true); React.useEffect(() => { - loadSettings(); + initializeSettings(); }, []); - const loadSettings = async () => { + const initializeSettings = async () => { + try { + // Load default prompts from manifest first + const defaultPrompts = await loadDefaultPromptsFromManifest(); + + // Create initial settings with manifest defaults + const initialSettings: PluginSettings = { + basic_analysis: { + ru: { + llm: '', + custom_prompt: defaultPrompts.basic_analysis.ru, + }, + en: { + llm: '', + custom_prompt: defaultPrompts.basic_analysis.en, + }, + }, + deep_analysis: { + ru: { + llm: '', + custom_prompt: defaultPrompts.deep_analysis.ru, + }, + en: { + llm: '', + custom_prompt: defaultPrompts.deep_analysis.en, + }, + }, + api_keys: { + default: '', + }, + }; + + // Then load user settings and merge with defaults + await loadSettings(initialSettings); + } catch (error) { + console.error('Failed to initialize settings:', error); + // Fallback to loading with empty defaults + await loadSettings(DEFAULT_SETTINGS); + } + }; + + const loadSettings = async (defaultSettings: PluginSettings = DEFAULT_SETTINGS) => { try { setIsLoading(true); const result = await chrome.storage.local.get([STORAGE_KEY]); @@ -82,36 +161,36 @@ export const usePluginSettings = () => { } setSettings({ - ...DEFAULT_SETTINGS, + ...defaultSettings, ...storedSettings, basic_analysis: { ru: { - ...DEFAULT_SETTINGS.basic_analysis.ru, + ...defaultSettings.basic_analysis.ru, ...storedSettings.basic_analysis?.ru, }, en: { - ...DEFAULT_SETTINGS.basic_analysis.en, + ...defaultSettings.basic_analysis.en, ...storedSettings.basic_analysis?.en, }, }, deep_analysis: { ru: { - ...DEFAULT_SETTINGS.deep_analysis.ru, + ...defaultSettings.deep_analysis.ru, ...storedSettings.deep_analysis?.ru, }, en: { - ...DEFAULT_SETTINGS.deep_analysis.en, + ...defaultSettings.deep_analysis.en, ...storedSettings.deep_analysis?.en, }, }, api_keys: decryptedApiKeys, }); } else { - setSettings(DEFAULT_SETTINGS); + setSettings(defaultSettings); } } catch (error) { console.error('Failed to load plugin settings:', error); - setSettings(DEFAULT_SETTINGS); + setSettings(defaultSettings); } finally { setIsLoading(false); } diff --git a/pages/options/vite.config.mts b/pages/options/vite.config.mts index d5fa2fc4..fe4c073b 100755 --- a/pages/options/vite.config.mts +++ b/pages/options/vite.config.mts @@ -12,7 +12,7 @@ const pkg = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'u function versionReplacePlugin() { return { name: 'html-version-replace', - transformIndexHtml(html) { + transformIndexHtml(html: string) { return html.replace(/__EXT_VERSION__/g, pkg.version); }, }; diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 9c1e625d..f3d45e15 100755 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1413", + "version": "0.5.1450", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 4a8a1dbc..569f9b74 100755 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1418", + "version": "0.27.1455", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 9e838ad2..6c969956 100755 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1420", + "version": "0.27.1457", "description": "The Pyodide JavaScript package", "keywords": [ "python", From 8339c5a677bb8632f0e7ad82089dfc22ddb307f9 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Sat, 25 Oct 2025 16:49:40 +0500 Subject: [PATCH 12/15] =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- chrome-extension/public/offscreen.js | 31 +++++++++ .../plugins/ozon-analyzer/mcp_server.py | 51 ++++++++++---- chrome-extension/public/pyodide/package.json | 2 +- .../side-panel/assets/index-Da7zCQoy.js | 50 -------------- .../side-panel/assets/index-Da7zCQoy.js.map | 1 - .../side-panel/assets/index-DaFqBU4q.js | 50 ++++++++++++++ .../side-panel/assets/index-DaFqBU4q.js.map | 1 + chrome-extension/public/side-panel/index.html | 2 +- chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/options/src/components/LLMSelector.tsx | 7 +- .../options/src/components/PluginDetails.tsx | 68 ++++++++++--------- pages/options/src/locales/ru.json | 2 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- 36 files changed, 186 insertions(+), 131 deletions(-) delete mode 100644 chrome-extension/public/side-panel/assets/index-Da7zCQoy.js delete mode 100644 chrome-extension/public/side-panel/assets/index-Da7zCQoy.js.map create mode 100644 chrome-extension/public/side-panel/assets/index-DaFqBU4q.js create mode 100644 chrome-extension/public/side-panel/assets/index-DaFqBU4q.js.map diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 08eae43b..5920b9ab 100755 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1432", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1432", + "version": "1.0.1435", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1435", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index ac49aadc..ea02d83c 100755 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/offscreen.js b/chrome-extension/public/offscreen.js index 37f13063..1ba52cd5 100755 --- a/chrome-extension/public/offscreen.js +++ b/chrome-extension/public/offscreen.js @@ -709,6 +709,14 @@ async function initializePyodide() { console.log('[OFFSCREEN_DIAGNOSIS] jsOptions.apiKeyId:', jsOptions.apiKeyId); console.log('[OFFSCREEN_DIAGNOSIS] window.geminiApiKey exists:', typeof window.geminiApiKey); + // Check pluginSettings for API key + console.log('[OFFSCREEN_DIAGNOSIS] Checking pluginSettings for API key...'); + console.log('[OFFSCREEN_DIAGNOSIS] pluginSettings exists:', !!window.pluginSettings); + console.log('[OFFSCREEN_DIAGNOSIS] pluginSettings.api_keys exists:', !!(window.pluginSettings && window.pluginSettings.api_keys)); + if (window.pluginSettings && window.pluginSettings.api_keys) { + console.log('[OFFSCREEN_DIAGNOSIS] Available API keys:', Object.keys(window.pluginSettings.api_keys)); + } + let apiKey = jsOptions.apiKey; // Если передан apiKeyId, получаем API-ключ через запрос к background @@ -747,6 +755,26 @@ async function initializePyodide() { console.log('[OFFSCREEN_DIAGNOSIS] window.geminiApiKey length:', window.geminiApiKey ? window.geminiApiKey.length : 'N/A'); } + // Try to get API key from pluginSettings if still not found + if (!apiKey && window.pluginSettings && window.pluginSettings.api_keys) { + console.log('[OFFSCREEN_DIAGNOSIS] Trying to get API key from pluginSettings...'); + // Try different key patterns + const keyPatterns = [ + jsOptions.apiKeyId, + 'gemini', + 'default', + 'ozon-analyzer-default' + ]; + + for (const pattern of keyPatterns) { + if (pattern && window.pluginSettings.api_keys[pattern]) { + apiKey = window.pluginSettings.api_keys[pattern]; + console.log('[OFFSCREEN_DIAGNOSIS] ✅ Found API key in pluginSettings for pattern:', pattern); + break; + } + } + } + console.log('[OFFSCREEN_DIAGNOSIS] Final API key available:', !!apiKey); // Если API ключ все еще не найден, возвращаем понятное сообщение об ошибке @@ -1924,6 +1952,9 @@ chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { currentPageKey = pageKey; window.geminiApiKey = message.geminiApiKey; + // Store pluginSettings globally for API key access + window.pluginSettings = pluginSettings; + // ПОЛУЧАЕМ HTML ДАННЫЕ ИЗ HTML_DIRECT STORAGE ИЛИ НАПРЯМУЮ let workflowPayload; if (useChunks && transferId) { diff --git a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py index 34b37ef8..50c1c8c1 100755 --- a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py +++ b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py @@ -5065,25 +5065,27 @@ def get_selected_llm_for_analysis(plugin_settings: Dict[str, Any], analysis_type try: console_log(f"🔍 get_selected_llm_for_analysis: analysis_type={analysis_type}, content_language={content_language}") - # Проверяем настройки плагина на наличие кастомных LLM настроек + # Проверяем настройки плагина на наличие selected_llms if plugin_settings and isinstance(plugin_settings, dict): - llm_settings = safe_dict_get(plugin_settings, 'llm_settings', {}) - if llm_settings and isinstance(llm_settings, dict): - # Ищем настройку для конкретного типа анализа и языка - analysis_key = f"{analysis_type}_{content_language}" - selected_llm = safe_dict_get(llm_settings, analysis_key, None) - - if selected_llm: - console_log(f"✅ Найдена кастомная LLM для {analysis_key}: {selected_llm}") - return selected_llm + selected_llms = safe_dict_get(plugin_settings, 'selected_llms', {}) + if selected_llms and isinstance(selected_llms, dict): + # Ищем настройку для конкретного типа анализа + analysis_settings = safe_dict_get(selected_llms, analysis_type, {}) + if analysis_settings and isinstance(analysis_settings, dict): + # Ищем настройку для конкретного языка + selected_llm = safe_dict_get(analysis_settings, content_language, None) + + if selected_llm: + console_log(f"✅ Найдена выбранная LLM для {analysis_type}.{content_language}: {selected_llm}") + return selected_llm # Fallback: ищем настройку только для типа анализа - selected_llm = safe_dict_get(llm_settings, analysis_type, None) - if selected_llm: - console_log(f"✅ Найдена кастомная LLM для {analysis_type}: {selected_llm}") + selected_llm = safe_dict_get(selected_llms, analysis_type, None) + if selected_llm and isinstance(selected_llm, str): + console_log(f"✅ Найдена выбранная LLM для {analysis_type}: {selected_llm}") return selected_llm - console_log(f"ℹ️ Кастомная LLM не найдена, используем default") + console_log(f"ℹ️ Выбранная LLM не найдена, используем default") return 'default' except Exception as e: @@ -5126,6 +5128,27 @@ def get_api_key_for_analysis(plugin_settings: Dict[str, Any], analysis_type: str console_log(f"✅ Найден API ключ для варианта '{key_variant}': {api_key[:10]}...") return api_key.strip() + # Для default LLM пытаемся получить ключ из глобальных настроек + if selected_llm == 'default': + console_log(f"ℹ️ Для default LLM пытаемся получить API ключ из глобальных настроек") + # Пытаемся получить API ключ для Gemini из глобальных настроек + try: + # Проверяем наличие gemini_api_key в plugin_settings + gemini_key = safe_dict_get(plugin_settings, 'gemini_api_key', None) + if gemini_key and isinstance(gemini_key, str) and len(gemini_key.strip()) > 0: + console_log(f"✅ Найден Gemini API ключ в plugin_settings: {gemini_key[:10]}...") + return gemini_key.strip() + + # Проверяем наличие api_keys.gemini в plugin_settings + api_keys = safe_dict_get(plugin_settings, 'api_keys', {}) + if api_keys and isinstance(api_keys, dict): + gemini_key = safe_dict_get(api_keys, 'gemini', None) + if gemini_key and isinstance(gemini_key, str) and len(gemini_key.strip()) > 0: + console_log(f"✅ Найден Gemini API ключ в api_keys.gemini: {gemini_key[:10]}...") + return gemini_key.strip() + except Exception as e: + console_log(f"⚠️ Ошибка при получении Gemini API ключа: {str(e)}") + console_log(f"ℹ️ API ключ не найден в plugin_settings, будет использоваться ключ по умолчанию из background") return None diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index 6c969956..fd707d1e 100755 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1457", + "version": "0.27.1460", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js b/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js deleted file mode 100644 index 195e4f9a..00000000 --- a/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js +++ /dev/null @@ -1,50 +0,0 @@ -(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))c(S);new MutationObserver(S=>{for(const C of S)if(C.type==="childList")for(const M of C.addedNodes)M.tagName==="LINK"&&M.rel==="modulepreload"&&c(M)}).observe(document,{childList:!0,subtree:!0});function f(S){const C={};return S.integrity&&(C.integrity=S.integrity),S.referrerPolicy&&(C.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?C.credentials="include":S.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function c(S){if(S.ep)return;S.ep=!0;const C=f(S);fetch(S.href,C)}})();var fs=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function lh(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Do={exports:{}},Qn={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Wd;function ah(){if(Wd)return Qn;Wd=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function f(c,S,C){var M=null;if(C!==void 0&&(M=""+C),S.key!==void 0&&(M=""+S.key),"key"in S){C={};for(var F in S)F!=="key"&&(C[F]=S[F])}else C=S;return S=C.ref,{$$typeof:u,type:c,key:M,ref:S!==void 0?S:null,props:C}}return Qn.Fragment=r,Qn.jsx=f,Qn.jsxs=f,Qn}var Fd;function nh(){return Fd||(Fd=1,Do.exports=ah()),Do.exports}var y=nh();const ih=({isDraftSaved:u,isDraftLoading:r,draftError:f,messageLength:c,minLength:S,maxLength:C})=>{const M=()=>r?y.jsx("div",{className:"draft-status-loader"}):f?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):u?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cr?"Загрузка черновика...":f||(u?"Черновик сохранен":c>0&&c=S&&c<=C?"Черновик будет сохранен автоматически":c>C?"Превышен лимит символов":""),U=()=>r?"draft-status loading":f?"draft-status error":u?"draft-status saved":c>0&&c=S&&c<=C?"draft-status ready":c>C?"draft-status error":"draft-status";return c===0&&!r&&!f?null:y.jsxs("div",{className:U(),children:[M(),y.jsx("span",{className:"draft-status-text",children:F()})]})};var Uo={exports:{}},be={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Id;function sh(){if(Id)return be;Id=1;var u=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),f=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),C=Symbol.for("react.consumer"),M=Symbol.for("react.context"),F=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),q=Symbol.for("react.lazy"),X=Symbol.for("react.activity"),W=Symbol.iterator;function se(g){return g===null||typeof g!="object"?null:(g=W&&g[W]||g["@@iterator"],typeof g=="function"?g:null)}var $={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ee=Object.assign,te={};function _(g,N,J){this.props=g,this.context=N,this.refs=te,this.updater=J||$}_.prototype.isReactComponent={},_.prototype.setState=function(g,N){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,N,"setState")},_.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function K(){}K.prototype=_.prototype;function P(g,N,J){this.props=g,this.context=N,this.refs=te,this.updater=J||$}var B=P.prototype=new K;B.constructor=P,ee(B,_.prototype),B.isPureReactComponent=!0;var le=Array.isArray;function ne(){}var A={H:null,A:null,T:null,S:null},D=Object.prototype.hasOwnProperty;function ae(g,N,J){var I=J.ref;return{$$typeof:u,type:g,key:N,ref:I!==void 0?I:null,props:J}}function ye(g,N){return ae(g.type,N,g.props)}function ze(g){return typeof g=="object"&&g!==null&&g.$$typeof===u}function he(g){var N={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(J){return N[J]})}var Re=/\/+/g;function et(g,N){return typeof g=="object"&&g!==null&&g.key!=null?he(""+g.key):N.toString(36)}function Pe(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(ne,ne):(g.status="pending",g.then(function(N){g.status==="pending"&&(g.status="fulfilled",g.value=N)},function(N){g.status==="pending"&&(g.status="rejected",g.reason=N)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function z(g,N,J,I,re){var pe=typeof g;(pe==="undefined"||pe==="boolean")&&(g=null);var ge=!1;if(g===null)ge=!0;else switch(pe){case"bigint":case"string":case"number":ge=!0;break;case"object":switch(g.$$typeof){case u:case r:ge=!0;break;case q:return ge=g._init,z(ge(g._payload),N,J,I,re)}}if(ge)return re=re(g),ge=I===""?"."+et(g,0):I,le(re)?(J="",ge!=null&&(J=ge.replace(Re,"$&/")+"/"),z(re,N,J,"",function(pt){return pt})):re!=null&&(ze(re)&&(re=ye(re,J+(re.key==null||g&&g.key===re.key?"":(""+re.key).replace(Re,"$&/")+"/")+ge)),N.push(re)),1;ge=0;var Je=I===""?".":I+":";if(le(g))for(var Ge=0;Ge>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},oh="Platform Settings",ch="Available plugins",rh="AI API Keys",fh="Free",dh="Custom API keys",gh="+ Add new key",mh="Enter API key",yh="Enter key name",hh="Save all keys",ph="Test connections",vh="General Settings",bh="Automatic plugin updates",Sh="Show notifications",_h="Interface theme:",Eh="Light",xh="Dark",Ah="System",Th="Security",Ch="Verify plugin signatures",zh="Isolated execution mode",Mh="Performance",Oh="Maximum number of active plugins:",Dh="Cache plugin data",Uh="Plugin Details",jh={options:uh,options_settings_title:oh,options_plugins_title:ch,options_settings_aiKeys_title:rh,options_settings_aiKeys_badges_free:fh,options_settings_aiKeys_customKeys_title:dh,options_settings_aiKeys_customKeys_addButton:gh,options_settings_aiKeys_customKeys_keyPlaceholder:mh,options_settings_aiKeys_customKeys_namePlaceholder:yh,options_settings_aiKeys_actions_save:hh,options_settings_aiKeys_actions_test:ph,options_settings_general_title:vh,options_settings_general_autoUpdate:bh,options_settings_general_showNotifications:Sh,options_settings_general_theme:_h,options_settings_general_theme_light:Eh,options_settings_general_theme_dark:xh,options_settings_general_theme_system:Ah,options_settings_security_title:Th,options_settings_security_checkSignatures:Ch,options_settings_security_isolatedMode:zh,options_settings_performance_title:Mh,options_settings_performance_maxPlugins:Oh,options_settings_performance_cacheData:Dh,options_plugins_details_title:Uh},wh={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",basic_analysis:"Базовый",deep_analysis:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},Nh="Настройки Платформы",Rh="Доступные плагины",Hh="API Ключи нейросетей",Gh="Бесплатный",Kh="Пользовательские API ключи",Bh="+ Добавить новый ключ",Lh="Введите API ключ",qh="Введите название ключа",Yh="Сохранить все ключи",Ph="Тестировать подключения",Xh="Общие настройки",Qh="Автоматическое обновление плагинов",Vh="Показывать уведомления",Zh="Тема интерфейса:",kh="Светлая",Jh="Тёмная",$h="Системная",Wh="Безопасность",Fh="Проверять подписи плагинов",Ih="Изолированный режим выполнения",ep="Производительность",tp="Максимальное количество активных плагинов:",lp="Кэширование данных плагинов",ap="Детали плагина",np={options:wh,options_settings_title:Nh,options_plugins_title:Rh,options_settings_aiKeys_title:Hh,options_settings_aiKeys_badges_free:Gh,options_settings_aiKeys_customKeys_title:Kh,options_settings_aiKeys_customKeys_addButton:Bh,options_settings_aiKeys_customKeys_keyPlaceholder:Lh,options_settings_aiKeys_customKeys_namePlaceholder:qh,options_settings_aiKeys_actions_save:Yh,options_settings_aiKeys_actions_test:Ph,options_settings_general_title:Xh,options_settings_general_autoUpdate:Qh,options_settings_general_showNotifications:Vh,options_settings_general_theme:Zh,options_settings_general_theme_light:kh,options_settings_general_theme_dark:Jh,options_settings_general_theme_system:$h,options_settings_security_title:Wh,options_settings_security_checkSignatures:Fh,options_settings_security_isolatedMode:Ih,options_settings_performance_title:ep,options_settings_performance_maxPlugins:tp,options_settings_performance_cacheData:lp,options_plugins_details_title:ap},tg={en:jh,ru:np},_g=(u="en")=>({t:V.useMemo(()=>{const f=tg[u]||{},c=(S,C)=>C.split(".").reduce((M,F)=>M&&M[F]?M[F]:void 0,S);return S=>{const C=c(f,S);return C!==void 0?C:S}},[u,tg]),locale:u}),jo=({checked:u,onChange:r,disabled:f,label:c,iconOn:S,iconOff:C})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:f?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:u,disabled:f,onChange:M=>r(M.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:u?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:u?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:u?S:C})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),ip=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),sp=({children:u})=>{const[r,f]=hs.useState(null),c=hs.useCallback(()=>f(null),[]);if(r)return y.jsx(ip,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return f(S),null}};class wo{static ALGORITHM="AES-GCM";static KEY_LENGTH=256;static IV_LENGTH=12;static async getEncryptionKey(){try{const r=await chrome.storage.local.get(["encryptionKey"]);if(r.encryptionKey){const C=new Uint8Array(r.encryptionKey);return await crypto.subtle.importKey("raw",C,this.ALGORITHM,!1,["encrypt","decrypt"])}const f=await crypto.subtle.generateKey({name:this.ALGORITHM,length:this.KEY_LENGTH},!0,["encrypt","decrypt"]),c=await crypto.subtle.exportKey("raw",f),S=new Uint8Array(c);return await chrome.storage.local.set({encryptionKey:Array.from(S)}),f}catch(r){throw console.error("Failed to get/create encryption key:",r),new Error("Не удалось инициализировать шифрование")}}static async encrypt(r){try{const f=await this.getEncryptionKey(),c=crypto.getRandomValues(new Uint8Array(this.IV_LENGTH)),S=new TextEncoder().encode(r),C=await crypto.subtle.encrypt({name:this.ALGORITHM,iv:c},f,S),M=new Uint8Array(C),F=new Uint8Array(c.length+M.length);return F.set(c),F.set(M,c.length),btoa(String.fromCharCode(...F))}catch(f){throw console.error("Encryption failed:",f),new Error("Ошибка шифрования")}}static async decrypt(r){try{const f=await this.getEncryptionKey(),c=new Uint8Array(atob(r).split("").map(F=>F.charCodeAt(0))),S=c.slice(0,this.IV_LENGTH),C=c.slice(this.IV_LENGTH),M=await crypto.subtle.decrypt({name:this.ALGORITHM,iv:S},f,C);return new TextDecoder().decode(M)}catch(f){throw console.error("Decryption failed:",f),new Error("Ошибка расшифрования")}}static validateAPIKey(r){return!r||typeof r!="string"?{isValid:!1,error:"Ключ не может быть пустым"}:r.length<10?{isValid:!1,error:"Ключ слишком короткий"}:r.length>200?{isValid:!1,error:"Ключ слишком длинный"}:/[<>\"'&]/.test(r)?{isValid:!1,error:"Ключ содержит недопустимые символы"}:{isValid:!0}}}class yt{static async saveEncryptedKey(r,f){try{const c=wo.validateAPIKey(f);if(!c.isValid)throw new Error(c.error);const S=await wo.encrypt(f),C=await this.getAllEncryptedKeys();C[r]=S,await chrome.storage.local.set({encryptedApiKeys:C})}catch(c){throw console.error("Failed to save encrypted API key:",c),c}}static async getDecryptedKey(r){try{const c=(await this.getAllEncryptedKeys())[r];return c?await wo.decrypt(c):null}catch(f){return console.error("Failed to get decrypted API key:",f),null}}static async removeKey(r){try{const f=await this.getAllEncryptedKeys();delete f[r],await chrome.storage.local.set({encryptedApiKeys:f})}catch(f){throw console.error("Failed to remove API key:",f),f}}static async getAllEncryptedKeys(){try{return(await chrome.storage.local.get(["encryptedApiKeys"])).encryptedApiKeys||{}}catch(r){return console.error("Failed to get encrypted keys:",r),{}}}static async getAllKeyIds(){try{const r=await this.getAllEncryptedKeys();return Object.keys(r)}catch(r){return console.error("Failed to get key IDs:",r),[]}}static async keyExists(r){try{const f=await this.getAllEncryptedKeys();return r in f}catch(f){return console.error("Failed to check key existence:",f),!1}}}const No="plugin-ozon-analyzer-settings",up=async()=>{try{const f=(await(await fetch(chrome.runtime.getURL("plugins/ozon-analyzer/manifest.json"))).json()).options?.prompts;if(!f)throw new Error("Prompts not found in manifest");return{basic_analysis:{ru:f.basic_analysis?.ru?.default||"",en:f.basic_analysis?.en?.default||""},deep_analysis:{ru:f.deep_analysis?.ru?.default||"",en:f.deep_analysis?.en?.default||""}}}catch(u){return console.error("Failed to load prompts from manifest:",u),{basic_analysis:{ru:"верни слово basic_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)",en:"верни слово basic_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)"},deep_analysis:{ru:"верни слово deep_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)",en:"верни слово deep_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)"}}}},ds={basic_analysis:{ru:{llm:"",custom_prompt:""},en:{llm:"",custom_prompt:""}},deep_analysis:{ru:{llm:"",custom_prompt:""},en:{llm:"",custom_prompt:""}},api_keys:{default:""}},Eg=()=>{const[u,r]=V.useState(ds),[f,c]=V.useState(!0);V.useEffect(()=>{S()},[]);const S=async()=>{try{const $=await up(),ee={basic_analysis:{ru:{llm:"",custom_prompt:$.basic_analysis.ru},en:{llm:"",custom_prompt:$.basic_analysis.en}},deep_analysis:{ru:{llm:"",custom_prompt:$.deep_analysis.ru},en:{llm:"",custom_prompt:$.deep_analysis.en}},api_keys:{default:""}};await C(ee)}catch($){console.error("Failed to initialize settings:",$),await C(ds)}},C=async($=ds)=>{try{c(!0);const te=(await chrome.storage.local.get([No]))[No];if(te){const _={...te.api_keys};te.api_keys?.default&&(_.default=await yt.getDecryptedKey("ozon-analyzer-default")||""),r({...$,...te,basic_analysis:{ru:{...$.basic_analysis.ru,...te.basic_analysis?.ru},en:{...$.basic_analysis.en,...te.basic_analysis?.en}},deep_analysis:{ru:{...$.deep_analysis.ru,...te.deep_analysis?.ru},en:{...$.deep_analysis.en,...te.deep_analysis?.en}},api_keys:_})}else r($)}catch(ee){console.error("Failed to load plugin settings:",ee),r($)}finally{c(!1)}},M=async $=>{try{const ee={...$};$.api_keys?.default?await yt.saveEncryptedKey("ozon-analyzer-default",$.api_keys.default):await yt.removeKey("ozon-analyzer-default"),ee.api_keys={default:""},await chrome.storage.local.set({[No]:ee}),r($)}catch(ee){throw console.error("Failed to save plugin settings:",ee),ee}};return{settings:u,isLoading:f,updateBasicAnalysisSettings:async($,ee)=>{const te={...u,basic_analysis:{...u.basic_analysis,[$]:ee}};await M(te)},updateDeepAnalysisSettings:async($,ee)=>{const te={...u,deep_analysis:{...u.deep_analysis,[$]:ee}};await M(te)},updateAPIKey:async $=>{const ee={...u,api_keys:{default:$}};await M(ee)},getBasicAnalysisSettings:$=>u.basic_analysis[$],getDeepAnalysisSettings:$=>u.deep_analysis[$],getAPIKey:()=>u.api_keys?.default||"",resetToDefaults:async()=>{await M(ds)},saveSettings:M,loadSettings:C}},op=({promptType:u,language:r,globalAIKeys:f,defaultLLMCurl:c,hasDefaultLLM:S,onLLMChange:C})=>{const{settings:M,updateBasicAnalysisSettings:F,updateDeepAnalysisSettings:U}=Eg(),[v,q]=V.useState(c),[X,W]=V.useState(""),se=V.useRef(null);V.useEffect(()=>{(async()=>{const K=`ozon-analyzer-${u}-${r}`,P=await yt.getDecryptedKey(K)||"";W(P)})()},[u,r]),V.useEffect(()=>{const _=u==="basic_analysis"?M.basic_analysis[r]:M.deep_analysis[r];let K="";_?K=_.llm||(S?"default":""):K=S?"default":"",q(K)},[u,r,M,S]);const $=async _=>{q(_),await(u==="basic_analysis"?F:U)(r,{llm:_,custom_prompt:u==="basic_analysis"?M.basic_analysis[r].custom_prompt:M.deep_analysis[r].custom_prompt}),C(_,_==="default"?X:void 0)},ee=_=>{W(_),se.current&&clearTimeout(se.current),se.current=setTimeout(async()=>{try{const K=`ozon-analyzer-${u}-${r}`;await yt.saveEncryptedKey(K,_),C(v,_)}catch(K){console.error("Failed to save API key:",K)}},500)},te=()=>[{value:"default",label:"Default LLM"},...f.map(K=>({value:K.id,label:K.name}))];return y.jsxs("div",{style:{marginBottom:"16px"},children:[y.jsxs("label",{style:{display:"block",marginBottom:"8px",fontWeight:"bold"},children:["Выбор нейросети для ",u==="basic_analysis"?"базового":"глубокого"," анализа:"]}),y.jsx("select",{value:v,onChange:_=>$(_.target.value),style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",marginBottom:"8px"},children:te().map(_=>y.jsx("option",{value:_.value,children:_.label},_.value))}),v==="default"&&y.jsxs("div",{children:[y.jsx("label",{style:{display:"block",marginBottom:"4px",fontSize:"14px"},children:"API ключ:"}),y.jsx("input",{type:"password",value:X,onChange:_=>ee(_.target.value),placeholder:"Введите API ключ",style:{width:"100%",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})]})},cp=()=>{const{t:u}=_g("ru"),[r,f]=V.useState([{id:"gemini-flash-lite",name:"Google Gemini Flash Lite - Базовый анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0},{id:"gemini-pro",name:"Gemini 2.5 Pro - Глубокий анализ",key:"",status:"not_configured",isFixed:!0,isFree:!0}]),[c,S]=V.useState([]),C=V.useRef({});V.useEffect(()=>(M(),()=>{Object.values(C.current).forEach(_=>{clearTimeout(_)}),C.current={}}),[]);const M=async()=>{try{console.log("[useAIKeys] Starting to load AI keys...");const K=["gemini-flash-lite","gemini-pro"].map(async le=>{const ne=await yt.getDecryptedKey(le);return console.log(`[useAIKeys] Loaded key ${le}:`,ne?"present":"empty"),{keyId:le,decryptedKey:ne}}),P=await Promise.all(K);console.log("[useAIKeys] Fixed keys results:",P),f(le=>le.map(ne=>{const A=P.find(D=>D.keyId===ne.id);return console.log(`[useAIKeys] Setting key ${ne.id}:`,A?.decryptedKey?"configured":"not_configured"),{...ne,key:A?.decryptedKey||"",status:A?.decryptedKey?"configured":"not_configured"}}));const B=await chrome.storage.local.get(["customKeys"]);if(console.log("[useAIKeys] Custom keys metadata:",B.customKeys),B.customKeys){const le=await Promise.all(B.customKeys.map(async ne=>{const A=await yt.getDecryptedKey(ne.id);return console.log(`[useAIKeys] Custom key ${ne.id}:`,A?"present":"empty"),{...ne,key:A||"",status:A?"configured":"not_configured"}}));console.log("[useAIKeys] Setting custom keys:",le),S(le)}else console.log("[useAIKeys] No custom keys found"),S([]);console.log("[useAIKeys] AI keys loaded successfully")}catch(_){console.error("Failed to load AI keys:",_),f(K=>K.map(P=>({...P,key:"",status:"not_configured"}))),S([])}};return{aiKeys:r,customKeys:c,saveAIKeys:async()=>{try{console.log("[useAIKeys] Starting to save AI keys..."),console.log("[useAIKeys] Current aiKeys:",r),console.log("[useAIKeys] Current customKeys:",c);const _=r.map(async B=>{B.key?(console.log(`[useAIKeys] Saving fixed key ${B.id}`),await yt.saveEncryptedKey(B.id,B.key)):(console.log(`[useAIKeys] Removing fixed key ${B.id}`),await yt.removeKey(B.id))});await Promise.all(_);const K=c.map(async B=>{B.key?(console.log(`[useAIKeys] Saving custom key ${B.id}`),await yt.saveEncryptedKey(B.id,B.key)):(console.log(`[useAIKeys] Removing custom key ${B.id}`),await yt.removeKey(B.id))});await Promise.all(K);const P=c.map(B=>({id:B.id,name:B.name,isFixed:!1,isFree:!1}));console.log("[useAIKeys] Saving custom keys metadata:",P),await chrome.storage.local.set({customKeys:P}),f(B=>B.map(le=>({...le,status:le.key?"configured":"not_configured"}))),S(B=>B.map(le=>({...le,status:le.key?"configured":"not_configured"}))),console.log("[useAIKeys] AI keys saved successfully"),alert(u("options.settings.aiKeys.messages.saved"))}catch(_){console.error("Failed to save AI keys:",_),alert(u("options.settings.aiKeys.messages.saveError"))}},testAIKeys:async()=>{f(_=>_.map(K=>({...K,status:"testing"}))),S(_=>_.map(K=>({...K,status:"testing"}))),setTimeout(()=>{f(_=>_.map(K=>({...K,status:K.key?"configured":"not_configured"}))),S(_=>_.map(K=>({...K,status:K.key?"configured":"not_configured"}))),alert(u("options.settings.aiKeys.messages.testComplete"))},2e3)},addCustomKey:()=>{const _={id:`custom-${Date.now()}`,name:`Пользовательский ключ ${c.length+1}`,key:"",status:"not_configured"};S(K=>[...K,_])},removeCustomKey:async _=>{try{await yt.removeKey(_),S(K=>K.filter(P=>P.id!==_))}catch(K){console.error("Failed to remove custom key:",K),S(P=>P.filter(B=>B.id!==_))}},updateKey:(_,K,P=!1)=>{console.log(`[useAIKeys] Updating key ${_} with value:`,K?"present":"empty"),P?S(B=>B.map(le=>le.id===_?{...le,key:K,status:K?"configured":"not_configured"}:le)):f(B=>B.map(le=>le.id===_?{...le,key:K,status:K?"configured":"not_configured"}:le)),C.current[_]&&clearTimeout(C.current[_]),C.current[_]=setTimeout(async()=>{try{if(console.log(`[useAIKeys] Auto-saving key ${_} after delay`),K?await yt.saveEncryptedKey(_,K):await yt.removeKey(_),P){const le=(await chrome.storage.local.get(["customKeys"])).customKeys||[],ne=le.map(D=>D.id===_?{...D,name:D.name}:D);ne.findIndex(D=>D.id===_)===-1&&K&&ne.push({id:_,name:`Пользовательский ключ ${le.length+1}`,isFixed:!1,isFree:!1}),await chrome.storage.local.set({customKeys:ne.filter(D=>K||D.id!==_)})}console.log(`[useAIKeys] Key ${_} auto-saved successfully`)}catch(B){console.error(`[useAIKeys] Failed to auto-save key ${_}:`,B)}finally{delete C.current[_]}},1e3)},updateCustomKeyName:(_,K)=>{S(P=>P.map(B=>B.id===_?{...B,name:K,status:B.key?"configured":"not_configured"}:B))},getStatusText:_=>{switch(_){case"configured":return u("options.settings.aiKeys.status.configured");case"not_configured":return u("options.settings.aiKeys.status.notConfigured");case"testing":return u("options.settings.aiKeys.status.testing");default:return u("options.settings.aiKeys.status.notConfigured")}},getStatusClass:_=>{switch(_){case"configured":return"status-configured";case"not_configured":return"status-not-configured";case"testing":return"status-testing";default:return""}},getAPIKeyForMCP:async _=>{try{return await yt.getDecryptedKey(_)}catch(K){return console.error("Failed to get API key for MCP:",K),null}},isKeyConfigured:async _=>{try{return await yt.keyExists(_)}catch(K){return console.error("Failed to check if key is configured:",K),!1}}}},rp=(...u)=>u.filter(Boolean).join(" "),fp=({value:u,manifest:r,disabled:f,onSave:c,locale:S,t:C,globalAIKeys:M,pluginSettings:F})=>{const[U,v]=V.useState("basic_analysis"),[q,X]=V.useState("ru"),[W,se]=V.useState(""),$=(B,le)=>{try{const ne=r?.options?.prompts;return!ne||!((ne[B]||{})[le]||{}).LLM?.default?"default":B==="basic_analysis"?"gemini-flash-lite":B==="deep_analysis"?"gemini-pro":"default"}catch{return"default"}},ee=(B,le)=>{try{const ne=r?.options?.prompts;return ne?!!((ne[B]||{})[le]||{}).LLM?.default:!1}catch{return!1}},te=()=>{try{const B=r?.options?.prompts;if(!B)return"";const A=((B[U]||{})[q]||{}).default||"";return typeof A=="object"?JSON.stringify(A,null,2):A}catch{return""}},_=()=>{try{const ne=((u||{})[U]||{})[q];return typeof ne=="string"?ne:ne==null?"":JSON.stringify(ne,null,2)}catch{return""}};V.useEffect(()=>{se(_())},[U,q,u]);const K=()=>{se(te())},P=()=>{try{const B={...u};B[U]||(B[U]={ru:"",en:""}),B[U][q]=W,c(B)}catch(B){console.error("Failed to save custom prompt:",B)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:C("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:C("options.plugins.prompts.type")}),y.jsxs("select",{value:U,onChange:B=>v(B.target.value),disabled:f,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"basic_analysis",children:C("options.plugins.prompts.basic_analysis")}),y.jsx("option",{value:"deep_analysis",children:C("options.plugins.prompts.deep_analysis")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:C("options.plugins.prompts.language")}),y.jsxs("select",{value:q,onChange:B=>X(B.target.value),disabled:f,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:C("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:C("options.plugins.prompts.english")})]})]})]}),y.jsx(op,{promptType:U,language:q,globalAIKeys:M,defaultLLMCurl:$(U,q),hasDefaultLLM:ee(U,q),onLLMChange:(B,le)=>{console.log(`LLM changed for ${U} ${q}:`,B,le);const ne={...F};ne.selected_llms||(ne.selected_llms={}),ne.selected_llms[U]||(ne.selected_llms[U]={}),ne.selected_llms[U][q]=B,typeof window<"u"&&window.pyodide&&window.pyodide.globals&&(window.pyodide.globals.pluginSettings=ne)}}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:C("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:te(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:K,disabled:f,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:f?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:C("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:C("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:W,onChange:B=>se(B.target.value),disabled:f,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:P,disabled:f,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:f?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:C("options.plugins.prompts.save")})})]})})},dp=u=>{const{selectedPlugin:r,locale:f="en",onUpdateSetting:c}=u,{t:S}=_g(f),{aiKeys:C}=cp(),{settings:M}=Eg(),[F,U]=V.useState(null),[v,q]=V.useState(null),X=A=>A?typeof A=="string"?A:A[f]||A.ru||A.en||"":"";if(V.useEffect(()=>{r?.manifest?.options&&$()},[r?.id]),V.useEffect(()=>{if(M&&typeof window<"u"&&window.pyodide&&window.pyodide.globals){const A={basic_analysis:{ru:M.basic_analysis?.ru?.llm||"default",en:M.basic_analysis?.en?.llm||"default"},deep_analysis:{ru:M.deep_analysis?.ru?.llm||"default",en:M.deep_analysis?.en?.llm||"default"}},D={...window.pyodide.globals.pluginSettings||{},selected_llms:A};window.pyodide.globals.pluginSettings=D,console.log("Updated pluginSettings in pyodide.globals:",D)}},[M]),!r||typeof r!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:S("options_plugins_details_title")}),y.jsx("p",{children:S("options.plugins.details.selectPlugin")})]});const W=r.settings||{enabled:!0,autorun:!1},se=r.manifest?.host_permissions||[],$=async()=>{if(!(!r||!r.manifest?.options||v!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const D=Object.keys(r.manifest.options).map(ze=>`${r.id}_${ze}`),ae=await chrome.storage.local.get(D),ye={};Object.entries(ae).forEach(([ze,he])=>{const Re=ze.replace(`${r.id}_`,"");he!==void 0&&(ye[Re]=he)}),q(ye)}}catch(A){console.warn("Failed to load custom settings from chrome.storage.local:",A)}},ee=async(A,D)=>{if(r)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const ae=`${r.id}_${A}`;await chrome.storage.local.set({[ae]:D}),q(ye=>({...ye,[A]:D})),A==="prompts"&&await te()}else console.warn("chrome.storage.local is not available")}catch(ae){throw console.error(`Failed to save setting ${A} to chrome.storage.local:`,ae),ae}},te=async()=>{if(r)try{const A=`${r.id}_prompts`,D=await chrome.storage.local.get([A]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${r.id}`),console.log(` Storage key: ${A}`),console.log(" Сохраненные промпты:",D[A]),D[A]){const ae=D[A];console.log(" Структура промптов:"),console.log(` - basic_analysis.ru: ${ae.basic_analysis?.ru?"✓":"✗"} (${ae.basic_analysis?.ru?.length||0} символов)`),console.log(` - basic_analysis.en: ${ae.basic_analysis?.en?"✓":"✗"} (${ae.basic_analysis?.en?.length||0} символов)`),console.log(` - deep_analysis.ru: ${ae.deep_analysis?.ru?"✓":"✗"} (${ae.deep_analysis?.ru?.length||0} символов)`),console.log(` - deep_analysis.en: ${ae.deep_analysis?.en?"✓":"✗"} (${ae.deep_analysis?.en?.length||0} символов)`)}}catch(A){console.error("Ошибка диагностики промптов:",A)}},_=(A,D)=>{if(v&&v[A]!==void 0)return v[A];if(A==="prompts"&&typeof D=="object"&&D!==null){const ae=D,ye={basic_analysis:{ru:{},en:{}},deep_analysis:{ru:{},en:{}}};return ae.basic_analysis?.ru?.default&&(ye.basic_analysis.ru=ae.basic_analysis.ru.default),ae.basic_analysis?.en?.default&&(ye.basic_analysis.en=ae.basic_analysis.en.default),ae.deep_analysis?.ru?.default&&(ye.deep_analysis.ru=ae.deep_analysis.ru.default),ae.deep_analysis?.en?.default&&(ye.deep_analysis.en=ae.deep_analysis.en.default),ye}return D},K=(A,D)=>{const ae=_(A,D.default),ye=F===A||!(W.enabled??!0),ze=X(D.label),he=X(D.description);if(A==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(fp,{value:ae,manifest:r.manifest,disabled:ye,onSave:Re=>P(A,Re),locale:f,t:S,globalAIKeys:C,pluginSettings:M})},A);if(D.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:ae,disabled:ye,onChange:Re=>P(A,Re),label:y.jsxs(y.Fragment,{children:[ze,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"})]})})},A);if(D.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{className:"info-icon",title:he,children:"i"}),y.jsx("select",{id:A,name:A,value:ae,disabled:ye,onChange:Re=>P(A,Re.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:D.values?.map(Re=>y.jsx("option",{value:Re,children:X(D.labels?.[Re])||Re},Re))})]})},A);if(D.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"text",id:A,name:A,value:ae,disabled:ye,onChange:Re=>P(A,Re.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},A);if(D.type==="number"){const Re=et=>{const Pe=parseFloat(et.target.value);if(isNaN(Pe))return;let z=Pe;D.min!==void 0&&zD.max&&(z=D.max),P(A,z)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ze,he&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:he}),y.jsx("input",{type:"number",id:A,name:A,value:ae,disabled:ye,onChange:Re,min:D.min,max:D.max,step:D.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},A)}return null},P=async(A,D)=>{if(!r)return;const ae=r.manifest?.options;if(ae&&ae[A]){v===null&&await $();try{U(A),await ee(A,D)}catch(ye){console.error(`Failed to update custom setting ${A}:`,ye)}finally{U(null)}}else if(c)try{U(A),await c(r.id,A,D)}catch(ye){console.error(`Failed to update setting ${A}:`,ye)}finally{U(null)}},le=Object.entries(r.manifest?.options??{}).map(([A,D])=>K(A,D)).filter(A=>A!==null),ne=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:W.enabled??!0,disabled:F==="enabled",onChange:A=>P("enabled",A),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(jo,{checked:W.autorun??!1,disabled:F==="autorun"||!(W.enabled??!0),onChange:A=>P("autorun",A),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(sp,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:r.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",r.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:rp("status-badge",W.enabled?"status-active":"status-inactive"),children:W.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",r.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",r.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:r.description})]}),se.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:se.map((A,D)=>y.jsx("li",{children:A},D))})]}),Array.isArray(r.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(r.manifest?.permissions??[]).map((A,D)=>y.jsx("li",{children:A},D))})]}):null,y.jsx(ne,{}),r.manifest?.options&&Object.keys(r.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),le]}):null]})]})})},gp=({plugin:u,onUpdateSetting:r})=>{const f=r?async(S,C,M)=>{try{return await r(S,C,M),!0}catch(F){return console.error(`Failed to update setting ${C}:`,F),!1}}:void 0,c={selectedPlugin:{...u,description:u.description||"Описание не указано",icon:u.icon||"",manifest:u.manifest||{}},locale:"ru",onUpdateSetting:f};return y.jsx(dp,{...c})},lg=function(u){if(!u)return"unknown-page";try{const r=new URL(u);return r.search="",r.hash="",r.toString()}catch{return u}},mp=({pluginId:u,pageKey:r,debounceMs:f=1e3})=>{const[c,S]=V.useState(""),[C,M]=V.useState(!1),[F,U]=V.useState(!1),[v,q]=V.useState(null),[X,W]=V.useState(""),se=V.useRef(null),$=V.useRef(""),ee=V.useCallback(async P=>{if(P!==$.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:u,pageKey:r,text:P});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:P}),$.current=P,M(!0),q(null),W(P),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:u,pageKey:r,text:P})}catch(B){console.error("[useLazyChatSync] Error saving draft:",B),q("Ошибка сохранения черновика"),M(!1)}}},[u,r]),te=V.useCallback(async()=>{U(!0),q(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:u,pageKey:r});try{const P=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r});P?.draftText?(S(P.draftText),$.current=P.draftText,M(!0),W(P.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:u,pageKey:r,draft:P.draftText})):(S(""),$.current="",M(!1),W(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:u,pageKey:r}))}catch(P){console.error("[useLazyChatSync] Error loading draft:",P),q("Ошибка загрузки черновика")}finally{U(!1)}},[u,r]),_=V.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:u,pageKey:r});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:u,pageKey:r,draftText:""}),$.current="",M(!1),q(null),W(""),S(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:u,pageKey:r})}catch(P){console.error("[useLazyChatSync] Error clearing draft:",P),q("Ошибка очистки черновика")}},[u,r]),K=V.useCallback(P=>{S(P),se.current&&clearTimeout(se.current),P.length===0?_():se.current=setTimeout(()=>{ee(P)},f),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:u,pageKey:r,text:P})},[f,ee,_,u,r]);return V.useEffect(()=>()=>{se.current&&clearTimeout(se.current)},[]),V.useEffect(()=>{te()},[te]),V.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",r),M(!1),U(!1),q(null),$.current="",se.current&&(clearTimeout(se.current),se.current=null),te()},[r,te]),{message:c,setMessage:K,isDraftSaved:C,isDraftLoading:F,draftError:v,loadDraft:te,clearDraft:_,draftText:X}};var ys={exports:{}},yp=ys.exports,ag;function hp(){return ag||(ag=1,(function(u,r){(function(f,c){c()})(yp,function(){function f(v,q){return typeof q>"u"?q={autoBom:!1}:typeof q!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),q={autoBom:!q}),q.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(v.type)?new Blob(["\uFEFF",v],{type:v.type}):v}function c(v,q,X){var W=new XMLHttpRequest;W.open("GET",v),W.responseType="blob",W.onload=function(){U(W.response,q,X)},W.onerror=function(){console.error("could not download file")},W.send()}function S(v){var q=new XMLHttpRequest;q.open("HEAD",v,!1);try{q.send()}catch{}return 200<=q.status&&299>=q.status}function C(v){try{v.dispatchEvent(new MouseEvent("click"))}catch{var q=document.createEvent("MouseEvents");q.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),v.dispatchEvent(q)}}var M=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof fs=="object"&&fs.global===fs?fs:void 0,F=M.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),U=M.saveAs||(typeof window!="object"||window!==M?function(){}:"download"in HTMLAnchorElement.prototype&&!F?function(v,q,X){var W=M.URL||M.webkitURL,se=document.createElement("a");q=q||v.name||"download",se.download=q,se.rel="noopener",typeof v=="string"?(se.href=v,se.origin===location.origin?C(se):S(se.href)?c(v,q,X):C(se,se.target="_blank")):(se.href=W.createObjectURL(v),setTimeout(function(){W.revokeObjectURL(se.href)},4e4),setTimeout(function(){C(se)},0))}:"msSaveOrOpenBlob"in navigator?function(v,q,X){if(q=q||v.name||"download",typeof v!="string")navigator.msSaveOrOpenBlob(f(v,X),q);else if(S(v))c(v,q,X);else{var W=document.createElement("a");W.href=v,W.target="_blank",setTimeout(function(){C(W)})}}:function(v,q,X,W){if(W=W||open("","_blank"),W&&(W.document.title=W.document.body.innerText="downloading..."),typeof v=="string")return c(v,q,X);var se=v.type==="application/octet-stream",$=/constructor/i.test(M.HTMLElement)||M.safari,ee=/CriOS\/[\d]+/.test(navigator.userAgent);if((ee||se&&$||F)&&typeof FileReader<"u"){var te=new FileReader;te.onloadend=function(){var P=te.result;P=ee?P:P.replace(/^data:[^;]*;/,"data:attachment/file;"),W?W.location.href=P:location=P,W=null},te.readAsDataURL(v)}else{var _=M.URL||M.webkitURL,K=_.createObjectURL(v);W?W.location=K:location.href=K,W=null,setTimeout(function(){_.revokeObjectURL(K)},4e4)}});M.saveAs=U.saveAs=U,u.exports=U})})(ys)),ys.exports}var pp=hp(),Ja;(function(u){u.Local="local",u.Sync="sync",u.Managed="managed",u.Session="session"})(Ja||(Ja={}));var Qo;(function(u){u.ExtensionPagesOnly="TRUSTED_CONTEXTS",u.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(Qo||(Qo={}));const ka=globalThis.chrome,ng=async(u,r)=>{const f=S=>typeof S=="function",c=S=>S instanceof Promise;return f(u)?(c(u),u(r)):u};let ig=!1;const sg=u=>{if(ka&&!ka.storage[u])throw new Error(`"storage" permission in manifest.ts: "storage ${u}" isn't defined`)},xg=(u,r,f)=>{let c=null,S=!1,C=[];const M=f?.storageEnum??Ja.Local,F=f?.liveUpdate??!1,U=f?.serialization?.serialize??(te=>te),v=f?.serialization?.deserialize??(te=>te);ig===!1&&M===Ja.Session&&f?.sessionAccessForContentScripts===!0&&(sg(M),ka?.storage[M].setAccessLevel({accessLevel:Qo.ExtensionPagesAndContentScripts}).catch(te=>{console.error(te),console.error("Please call .setAccessLevel() into different context, like a background script.")}),ig=!0);const q=async()=>{sg(M);const te=await ka?.storage[M].get([u]);return te?v(te[u])??r:r},X=async te=>{S||(c=await q()),c=await ng(te,c),await ka?.storage[M].set({[u]:U(c)}),$()},W=te=>(C=[...C,te],()=>{C=C.filter(_=>_!==te)}),se=()=>c,$=()=>{C.forEach(te=>te())},ee=async te=>{if(te[u]===void 0)return;const _=v(te[u].newValue);c!==_&&(c=await ng(_,c),$())};return q().then(te=>{c=te,S=!0,$()}),F&&ka?.storage[M].onChanged.addListener(ee),{get:q,set:X,getSnapshot:se,subscribe:W}},ug=xg("theme-storage-key",{theme:"system",isLight:Ag()},{storageEnum:Ja.Local,liveUpdate:!0});function Ag(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Ro={...ug,toggle:async()=>{await ug.set(u=>{let r;switch(u.theme){case"light":r="dark";break;case"dark":r="system";break;case"system":default:r="light";break}const f=r==="system"?Ag():r==="light";return{theme:r,isLight:f}})}},Ho=xg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Ja.Local,liveUpdate:!0}),og={...Ho,setAlignment:async u=>{await Ho.set({alignment:u})},getAlignment:async()=>(await Ho.get()).alignment},vp=({plugin:u,currentView:r,isRunning:f,isPaused:c,currentTabUrl:S,onStart:C,onPause:M,onStop:F,onClose:U})=>{const[v,q]=V.useState("chat"),[X,W]=V.useState(lg(S)),[se,$]=V.useState("left");V.useEffect(()=>{const j=lg(S);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:X,newPageKey:j,currentTabUrl:S,timestamp:new Date().toISOString()}),W(j)},[S]),V.useEffect(()=>{const j=async()=>{const p=await og.getAlignment();$(p)};return j(),og.subscribe(()=>{j()})},[]);const{message:ee,setMessage:te,isDraftSaved:_,isDraftLoading:K,draftError:P,loadDraft:B,clearDraft:le,draftText:ne}=mp({pluginId:u.id,pageKey:X,debounceMs:1e3}),[A,D]=V.useState([]),[ae,ye]=V.useState(!1),[ze,he]=V.useState(null),[Re,et]=V.useState(60),[Pe,z]=V.useState(!1),k=V.useRef(null),Q=V.useRef(null);V.useEffect(()=>{},[f]);const Ae=()=>{C()},xe=u.name||(typeof u.manifest?.name=="string"?u.manifest.name:"")||u.id,g=u.id,N=V.useCallback(async j=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...j,messageId:h};try{return await chrome.runtime.sendMessage(p)}catch(Y){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",Y),Y}},[]),J=V.useCallback(j=>{const h=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...j,messageId:h};chrome.runtime.sendMessage(p)},[]),I=V.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const j={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},h={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},Y=R=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:R,hasMessages:R&&"messages"in R,hasChat:R&&"chat"in R,messagesValue:R?.messages,chatValue:R?.chat,isMessagesArray:Array.isArray(R?.messages),isChatArray:Array.isArray(R?.chat),responseType:typeof R,responseKeys:R?Object.keys(R):"response is null/undefined",timestamp:new Date().toISOString()}),R===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let H=null;const Z=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],fe=(_e,ve)=>{let Ce=_e;for(const Xe of ve)if(Ce&&typeof Ce=="object"&&Xe in Ce)Ce=Ce[Xe];else return;return Ce};if(Array.isArray(R))H=R,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:H.length,firstMessage:H[0]?{id:H[0].id,content:H[0].content||H[0].text,role:H[0].role,timestamp:H[0].timestamp}:"no messages"});else if(R&&typeof R=="object"){if(R.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",R.error),he(`Ошибка от background: ${R.error}`),D([]);return}for(const{path:_e,description:ve}of Z){const Ce=fe(R,_e);if(Array.isArray(Ce)){H=Ce,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${ve}':`,{length:H.length,firstMessage:H[0]?{id:H[0].id,content:H[0].content||H[0].text,role:H[0].role,timestamp:H[0].timestamp}:"no messages"});break}}H||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof R,responseKeys:Object.keys(R),responseSample:JSON.stringify(R).substring(0,500),timestamp:new Date().toISOString()}),H=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:R,responseType:typeof R,responseStringified:JSON.stringify(R).substring(0,200),timestamp:new Date().toISOString()}),H=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:H,isArray:Array.isArray(H),length:H?.length,firstMessage:H?.[0],firstMessageType:H?.[0]?typeof H[0]:"none"}),Array.isArray(H)&&H.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",H.length);const _e=H.filter(ve=>!ve||typeof ve!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",ve),!1):!0).map((ve,Ce)=>{try{let Xe=ve.content||ve.text||"";typeof Xe=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",Xe),Xe=JSON.stringify(Xe)):Xe==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Xe=""):Xe=String(Xe);const Pl={id:ve.id||String(ve.timestamp||Date.now()+Ce),text:Xe,isUser:ve.role?ve.role==="user":!!ve.isUser,timestamp:ve.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${Ce}:`,{id:Pl.id,textLength:Pl.text.length,textType:typeof Pl.text,isUser:Pl.isUser}),Pl}catch(Xe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${Ce}:`,Xe,ve),{id:`error_${Date.now()}_${Ce}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:H.length,convertedCount:_e.length,firstConverted:_e[0],allConverted:_e.map(ve=>{try{const Ce=typeof ve.text=="string"?ve.text.substring(0,50):String(ve.text||"").substring(0,50);return{id:ve.id,text:Ce,isUser:ve.isUser,textType:typeof ve.text}}catch(Ce){return console.warn("[PluginControlPanel] Error processing message text:",Ce,ve),{id:ve.id,text:"[ERROR: invalid text]",isUser:ve.isUser,textType:typeof ve.text}}})}),D(_e)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),Y(j)}catch(R){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",R)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),Y(h)}catch(R){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",R)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),Y(p)}catch(R){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",R)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);V.useCallback(j=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:j,hasMessages:j&&"messages"in j,hasChat:j&&"chat"in j,messagesValue:j?.messages,chatValue:j?.chat,isMessagesArray:Array.isArray(j?.messages),isChatArray:Array.isArray(j?.chat),responseType:typeof j,responseKeys:j?Object.keys(j):"response is null/undefined",timestamp:new Date().toISOString()}),j===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let h=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],Y=(R,H)=>{let Z=R;for(const fe of H)if(Z&&typeof Z=="object"&&fe in Z)Z=Z[fe];else return;return Z};if(Array.isArray(j))h=j,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});else if(j&&typeof j=="object"){if(j.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",j.error),he(`Ошибка от background: ${j.error}`),D([]);return}for(const{path:R,description:H}of p){const Z=Y(j,R);if(Array.isArray(Z)){h=Z,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${H}':`,{length:h.length,firstMessage:h[0]?{id:h[0].id,content:h[0].content||h[0].text,role:h[0].role,timestamp:h[0].timestamp}:"no messages"});break}}h||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof j,responseKeys:Object.keys(j),responseSample:JSON.stringify(j).substring(0,500),timestamp:new Date().toISOString()}),h=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:j,responseType:typeof j,responseStringified:JSON.stringify(j).substring(0,200),timestamp:new Date().toISOString()}),h=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:h,isArray:Array.isArray(h),length:h?.length,firstMessage:h?.[0],firstMessageType:h?.[0]?typeof h[0]:"none"}),Array.isArray(h)&&h.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",h.length);const R=h.filter(H=>!H||typeof H!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",H),!1):!0).map((H,Z)=>{try{let fe=H.content||H.text||"",_e=H.timestamp||Date.now();if(typeof fe=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",fe),fe=JSON.stringify(fe);else if(fe==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),fe="";else{fe=String(fe),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${Z}:`,fe);try{const Ce=JSON.parse(fe);typeof Ce=="object"&&Ce!==null&&"content"in Ce&&(console.log("[PluginControlPanel] Распарсен JSON из content:",Ce),fe=String(Ce.content||""),Ce.timestamp&&typeof Ce.timestamp=="number"&&(_e=Ce.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${Z}:`,fe)}const ve={id:H.id||String(_e+Z),text:fe,isUser:H.role?H.role==="user":!!H.isUser,timestamp:_e};return console.log(`[PluginControlPanel] Конвертировано сообщение ${Z}:`,{id:ve.id,textLength:ve.text.length,textType:typeof ve.text,isUser:ve.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${Z}:`,ve.text),ve}catch(fe){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${Z}:`,fe,H),{id:`error_${Date.now()}_${Z}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:h.length,convertedCount:R.length,firstConverted:R[0],allConverted:R.map(H=>{try{const Z=typeof H.text=="string"?H.text.substring(0,50):String(H.text||"").substring(0,50);return{id:H.id,text:Z,isUser:H.isUser,textType:typeof H.text}}catch(Z){return console.warn("[PluginControlPanel] Error processing message text:",Z,H),{id:H.id,text:"[ERROR: invalid text]",isUser:H.isUser,textType:typeof H.text}}})}),D(R)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const re=V.useCallback(async()=>{ye(!0),he(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:g,pageKey:X,currentTabUrl:S,timestamp:new Date().toISOString(),isRunning:f,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const j=await N({type:"GET_PLUGIN_CHAT",pluginId:g,pageKey:X});console.log("[PluginControlPanel] loadChat - получен ответ от background:",j),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:ae,messagesCount:A.length,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),j?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",j.error),he(`Ошибка загрузки чата: ${j.error}`),D([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(h=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:h,hasMessages:h&&"messages"in h,hasChat:h&&"chat"in h,messagesValue:h?.messages,chatValue:h?.chat,isMessagesArray:Array.isArray(h?.messages),isChatArray:Array.isArray(h?.chat),responseType:typeof h,responseKeys:h?Object.keys(h):"response is null/undefined",timestamp:new Date().toISOString()}),h===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),D([]);return}let p=null;const Y=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],R=(H,Z)=>{let fe=H;for(const _e of Z)if(fe&&typeof fe=="object"&&_e in fe)fe=fe[_e];else return;return fe};if(Array.isArray(h))p=h,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(h&&typeof h=="object"){if(h.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",h.error),he(`Ошибка от background: ${h.error}`),D([]);return}for(const{path:H,description:Z}of Y){const fe=R(h,H);if(Array.isArray(fe)){p=fe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${Z}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof h,responseKeys:Object.keys(h),responseSample:JSON.stringify(h).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:h,responseType:typeof h,responseStringified:JSON.stringify(h).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const H=p.filter(Z=>!Z||typeof Z!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",Z),!1):!0).map((Z,fe)=>{try{let _e=Z.content||Z.text||"",ve=Z.timestamp||Date.now();if(typeof _e=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",_e),_e=JSON.stringify(_e);else if(_e==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),_e="";else{_e=String(_e);try{const Xe=JSON.parse(_e);typeof Xe=="object"&&Xe!==null&&"content"in Xe&&(console.log("[PluginControlPanel] Распаршен JSON из content:",Xe),_e=String(Xe.content||""),Xe.timestamp&&typeof Xe.timestamp=="number"&&(ve=Xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const Ce={id:Z.id||String(ve+fe),text:_e,isUser:Z.role?Z.role==="user":!!Z.isUser,timestamp:ve};return console.log(`[PluginControlPanel] Конвертировано сообщение ${fe}:`,{id:Ce.id,textLength:Ce.text.length,textType:typeof Ce.text,isUser:Ce.isUser}),Ce}catch(_e){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${fe}:`,_e,Z),{id:`error_${Date.now()}_${fe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:H.length,firstConverted:H[0],allConverted:H.map(Z=>{try{const fe=typeof Z.text=="string"?Z.text.substring(0,50):String(Z.text||"").substring(0,50);return{id:Z.id,text:fe,isUser:Z.isUser,textType:typeof Z.text}}catch(fe){return console.warn("[PluginControlPanel] Error processing message text:",fe,Z),{id:Z.id,text:"[ERROR: invalid text]",isUser:Z.isUser,textType:typeof Z.text}}})}),D(H)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),D([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(j),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(j){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",j),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:ae,messagesCount:A.length,errorType:typeof j,errorMessage:j instanceof Error?j.message:String(j),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:j,errorType:typeof j,errorMessage:j instanceof Error?j.message:String(j),errorStack:j instanceof Error?j.stack:"No stack trace",errorName:j instanceof Error?j.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:g,pageKey:X}),ye(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let h="Ошибка связи с background";j instanceof Error?(h+=`: ${j.message}`,j.name==="TypeError"&&j.message.includes("substring")&&(h+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:j,stack:j.stack,context:{pluginId:g,pageKey:X}}))):h+=`: ${String(j)}`,he(h),D([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[g,X,N,S,f,c]);V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:g,pageKey:X,currentTabUrl:S,timestamp:new Date().toISOString()}),re().catch(j=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",j)})},[re]),V.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),re().catch(j=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",j)}),B()},[X,re,B]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:g,pageKey:X,timestamp:new Date().toISOString()});const j=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===g&&p.pageKey===X&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:ae,messagesCount:A.length,currentPageKey:X,pluginId:g,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),re().catch(Y=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",Y)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:ae,messagesCount:A.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),ye(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),he(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,R=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const Z=JSON.parse(Y);typeof Z=="object"&&Z!==null&&"content"in Z&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",Z),Y=String(Z.content||""),Z.timestamp&&typeof Z.timestamp=="number"&&(R=Z.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const H={id:p.message.id||`pyodide_${R}_${Math.random()}`,text:Y,isUser:!1,timestamp:R};console.log("[PluginControlPanel] Adding Pyodide message to chat:",H),D(Z=>[...Z,H]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",Y,p);const R={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(H=>[...H,R])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},h=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),he(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(j),chrome.runtime.onMessage.removeListener(h),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(j),chrome.runtime.onMessage.removeListener(h)}},[g,X,J,re]),V.useEffect(()=>{r==="chat"&&B()},[r,B]),V.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:g,pageKey:X,draftText:ne,message:ee,currentView:r,isRunning:f,isPaused:c})}),V.useEffect(()=>{const j=console.error;return console.error=(...h)=>{const p=h.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",h),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),j.apply(console,h)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=j,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),V.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const j=h=>{const p=h.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let Y=p.message.content,R=p.timestamp||Date.now();if(typeof Y=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",Y),Y=JSON.stringify(Y);else if(Y==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),Y="Пустое сообщение от Pyodide";else{Y=String(Y);try{const Z=JSON.parse(Y);typeof Z=="object"&&Z!==null&&"content"in Z&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",Z),Y=String(Z.content||""),Z.timestamp&&typeof Z.timestamp=="number"&&(R=Z.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const H={id:p.message.id||`pyodide_${R}_${Math.random()}`,text:Y,isUser:!1,timestamp:R};console.log("[PluginControlPanel] Adding Pyodide message to chat:",H),D(Z=>[...Z,H]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(Y){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",Y,p);const R={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${Y instanceof Error?Y.message:String(Y)}]`,isUser:!1,timestamp:Date.now()};D(H=>[...H,R])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",j),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",j),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),V.useEffect(()=>{typeof ne=="string"&&(te(ne),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",ne))},[ne,te]);const pe=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:ee}),!ee.trim())return;const j={id:Date.now().toString(),text:ee.trim(),timestamp:Date.now()};te(""),he(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),J({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:g,pageKey:X,message:{role:"user",content:j.text,timestamp:j.timestamp}}),le()};V.useEffect(()=>{const j=p=>{if(!Pe)return;const Y=document.querySelector(".chat-view");if(!Y)return;const R=Y.getBoundingClientRect(),H=R.bottom-p.clientY,Z=100,fe=R.height-80;H>=Z&&H<=fe&&et(R.height-H)},h=()=>{z(!1),document.body.style.cursor="",document.body.style.userSelect=""};return Pe&&(document.addEventListener("mousemove",j),document.addEventListener("mouseup",h)),()=>{document.removeEventListener("mousemove",j),document.removeEventListener("mouseup",h)}},[Pe]),V.useEffect(()=>{k.current?.scrollIntoView({behavior:"smooth"})},[A]),V.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:A.length,firstMessage:A[0]?{id:A[0].id,text:typeof A[0].text=="string"?A[0].text.substring(0,50):String(A[0].text||"").substring(0,50),isUser:A[0].isUser,timestamp:A[0].timestamp,textType:typeof A[0].text}:null,allMessages:A.map(j=>{try{const h=typeof j.text=="string"?j.text.substring(0,30):String(j.text||"").substring(0,30);return{id:j.id,text:h,isUser:j.isUser,textType:typeof j.text}}catch(h){return console.warn("[PluginControlPanel] Error in message logging:",h,j),{id:j.id,text:"[LOGGING ERROR]",isUser:j.isUser,textType:typeof j.text}}}),timestamp:new Date().toISOString()})},[A]),V.useEffect(()=>{r==="chat"&&setTimeout(()=>Q.current?.focus(),100)},[r]);const ge=j=>{te(j.target.value);const h=j.target;h.style.height="auto";const p=Math.min(Math.max(h.scrollHeight,60),200);h.style.height=`${p}px`,et(p)},Je=j=>{j.key==="Enter"&&!j.shiftKey&&(j.preventDefault(),pe())},Ge=()=>{ye(!0),he(null),J({type:"DELETE_PLUGIN_CHAT",pluginId:g,pageKey:X}),D([]),le()},pt=()=>{const j=JSON.stringify(A,null,2),h=new Blob([j],{type:"application/json"});pp.saveAs(h,`plugin-chat-${g}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":se},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:u.iconUrl||`plugins/${u.id}/${u.icon||"icon.svg"}`,alt:`${xe} icon`,onError:j=>{const h=typeof xe=="string"&&xe.length>0?xe.charAt(0):"P";j.currentTarget.src=`data:image/svg+xml;utf8,${h}`}}),y.jsx("h3",{children:xe})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${f?"stop-mode":"start-mode"}`,onClick:Ae,disabled:f||c,title:f?"Остановить":"Запустить",children:f?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:M,disabled:!f||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:F,disabled:!f,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:U,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${v==="chat"?"active":""}`,onClick:()=>q("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${v==="details"?"active":""}`,onClick:()=>q("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[v==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:Ge,disabled:ae||!!ze,children:ae?"Очистка...":ze?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:pt,disabled:ae||!!ze,children:ae?"Экспорт...":ze?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:I,disabled:ae,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[ae&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),ze&&y.jsx("div",{className:"chat-error",children:ze}),!ae&&!ze&&A.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:A.map((j,h)=>{console.log("[PluginControlPanel] render message:",h,j);let p=j.text,Y=j.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",h,":",j.text);try{const R=JSON.parse(p);if(typeof R=="object"&&R!==null&&"content"in R){console.log("[PluginControlPanel] Парсинг JSON в рендере:",R);let H=R.content;typeof H=="object"?p=JSON.stringify(H):p=String(H||""),R.timestamp&&typeof R.timestamp=="number"&&(Y=R.timestamp)}else if(typeof R=="string")p=R;else if(typeof R=="object"&&R!==null){const H=Object.values(R).filter(Z=>typeof Z=="string");H.length>0?p=String(H[0]):p=JSON.stringify(R)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",h,":",p),y.jsx("div",{className:`chat-message ${j.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(Y).toLocaleTimeString()})]})},j.id||h)})}),y.jsx("div",{ref:k})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:Q,className:"message-textarea",value:ee,onChange:ge,onKeyPress:Je,placeholder:"Напишите сообщение...",style:{height:`${Re}px`}}),y.jsx("button",{className:"send-btn",onClick:pe,disabled:!ee.trim(),children:"📤"}),y.jsx(ih,{isDraftSaved:_,isDraftLoading:K,draftError:P,messageLength:ee.length,minLength:10,maxLength:1e3})]})]}),v==="details"&&y.jsx(gp,{plugin:u})]})]})},bp=({toasts:u,onRemove:r})=>y.jsx("div",{className:"toast-container",children:u.map(f=>y.jsx(Sp,{toast:f,onRemove:r},f.id))}),Sp=({toast:u,onRemove:r})=>{const[f,c]=V.useState(!1),[S,C]=V.useState(!1);V.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),u.duration>0){const F=setTimeout(()=>{M()},u.duration);return()=>clearTimeout(F)}},[u.duration]);const M=V.useCallback(()=>{C(!0),setTimeout(()=>{r(u.id)},300)},[u.id,r]);return y.jsx("div",{className:`toast toast-${u.type} ${f?"toast-visible":""} ${S?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:u.message}),y.jsx("button",{className:"toast-close",onClick:M,"aria-label":"Закрыть уведомление",children:"×"})]})})},_p=({theme:u,isLight:r,onToggle:f,isInSidebar:c=!1})=>{const S=()=>{switch(u){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},C=()=>{switch(u){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},M={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:f,style:M,title:C(),children:S()})};function Tg(u){var r,f,c="";if(typeof u=="string"||typeof u=="number")c+=u;else if(typeof u=="object")if(Array.isArray(u)){var S=u.length;for(r=0;r{const r=Tp(u),{conflictingClassGroups:f,conflictingClassGroupModifiers:c}=u;return{getClassGroupId:M=>{const F=M.split($o);return F[0]===""&&F.length!==1&&F.shift(),Cg(F,r)||Ap(M)},getConflictingClassGroupIds:(M,F)=>{const U=f[M]||[];return F&&c[M]?[...U,...c[M]]:U}}},Cg=(u,r)=>{if(u.length===0)return r.classGroupId;const f=u[0],c=r.nextPart.get(f),S=c?Cg(u.slice(1),c):void 0;if(S)return S;if(r.validators.length===0)return;const C=u.join($o);return r.validators.find(({validator:M})=>M(C))?.classGroupId},cg=/^\[(.+)\]$/,Ap=u=>{if(cg.test(u)){const r=cg.exec(u)[1],f=r?.substring(0,r.indexOf(":"));if(f)return"arbitrary.."+f}},Tp=u=>{const{theme:r,classGroups:f}=u,c={nextPart:new Map,validators:[]};for(const S in f)Vo(f[S],c,S,r);return c},Vo=(u,r,f,c)=>{u.forEach(S=>{if(typeof S=="string"){const C=S===""?r:rg(r,S);C.classGroupId=f;return}if(typeof S=="function"){if(Cp(S)){Vo(S(c),r,f,c);return}r.validators.push({validator:S,classGroupId:f});return}Object.entries(S).forEach(([C,M])=>{Vo(M,rg(r,C),f,c)})})},rg=(u,r)=>{let f=u;return r.split($o).forEach(c=>{f.nextPart.has(c)||f.nextPart.set(c,{nextPart:new Map,validators:[]}),f=f.nextPart.get(c)}),f},Cp=u=>u.isThemeGetter,zp=u=>{if(u<1)return{get:()=>{},set:()=>{}};let r=0,f=new Map,c=new Map;const S=(C,M)=>{f.set(C,M),r++,r>u&&(r=0,c=f,f=new Map)};return{get(C){let M=f.get(C);if(M!==void 0)return M;if((M=c.get(C))!==void 0)return S(C,M),M},set(C,M){f.has(C)?f.set(C,M):S(C,M)}}},Zo="!",ko=":",Mp=ko.length,Op=u=>{const{prefix:r,experimentalParseClassName:f}=u;let c=S=>{const C=[];let M=0,F=0,U=0,v;for(let $=0;$U?v-U:void 0;return{modifiers:C,hasImportantModifier:W,baseClassName:X,maybePostfixModifierPosition:se}};if(r){const S=r+ko,C=c;c=M=>M.startsWith(S)?C(M.substring(S.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:M,maybePostfixModifierPosition:void 0}}if(f){const S=c;c=C=>f({className:C,parseClassName:S})}return c},Dp=u=>u.endsWith(Zo)?u.substring(0,u.length-1):u.startsWith(Zo)?u.substring(1):u,Up=u=>{const r=Object.fromEntries(u.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const S=[];let C=[];return c.forEach(M=>{M[0]==="["||r[M]?(S.push(...C.sort(),M),C=[]):C.push(M)}),S.push(...C.sort()),S}},jp=u=>({cache:zp(u.cacheSize),parseClassName:Op(u),sortModifiers:Up(u),...xp(u)}),wp=/\s+/,Np=(u,r)=>{const{parseClassName:f,getClassGroupId:c,getConflictingClassGroupIds:S,sortModifiers:C}=r,M=[],F=u.trim().split(wp);let U="";for(let v=F.length-1;v>=0;v-=1){const q=F[v],{isExternal:X,modifiers:W,hasImportantModifier:se,baseClassName:$,maybePostfixModifierPosition:ee}=f(q);if(X){U=q+(U.length>0?" "+U:U);continue}let te=!!ee,_=c(te?$.substring(0,ee):$);if(!_){if(!te){U=q+(U.length>0?" "+U:U);continue}if(_=c($),!_){U=q+(U.length>0?" "+U:U);continue}te=!1}const K=C(W).join(":"),P=se?K+Zo:K,B=P+_;if(M.includes(B))continue;M.push(B);const le=S(_,te);for(let ne=0;ne0?" "+U:U)}return U};function Rp(){let u=0,r,f,c="";for(;u{if(typeof u=="string")return u;let r,f="";for(let c=0;cX(q),u());return f=jp(v),c=f.cache.get,S=f.cache.set,C=F,F(U)}function F(U){const v=c(U);if(v)return v;const q=Np(U,f);return S(U,q),q}return function(){return C(Rp.apply(null,arguments))}}const st=u=>{const r=f=>f[u]||[];return r.isThemeGetter=!0,r},Mg=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,Og=/^\((?:(\w[\w-]*):)?(.+)\)$/i,Gp=/^\d+\/\d+$/,Kp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,Bp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Lp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,qp=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,Yp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,Za=u=>Gp.test(u),Te=u=>!!u&&!Number.isNaN(Number(u)),Yl=u=>!!u&&Number.isInteger(Number(u)),Go=u=>u.endsWith("%")&&Te(u.slice(0,-1)),hl=u=>Kp.test(u),Pp=()=>!0,Xp=u=>Bp.test(u)&&!Lp.test(u),Dg=()=>!1,Qp=u=>qp.test(u),Vp=u=>Yp.test(u),Zp=u=>!ue(u)&&!oe(u),kp=u=>$a(u,wg,Dg),ue=u=>Mg.test(u),oa=u=>$a(u,Ng,Xp),Ko=u=>$a(u,Ip,Te),fg=u=>$a(u,Ug,Dg),Jp=u=>$a(u,jg,Vp),gs=u=>$a(u,Rg,Qp),oe=u=>Og.test(u),Vn=u=>Wa(u,Ng),$p=u=>Wa(u,e0),dg=u=>Wa(u,Ug),Wp=u=>Wa(u,wg),Fp=u=>Wa(u,jg),ms=u=>Wa(u,Rg,!0),$a=(u,r,f)=>{const c=Mg.exec(u);return c?c[1]?r(c[1]):f(c[2]):!1},Wa=(u,r,f=!1)=>{const c=Og.exec(u);return c?c[1]?r(c[1]):f:!1},Ug=u=>u==="position"||u==="percentage",jg=u=>u==="image"||u==="url",wg=u=>u==="length"||u==="size"||u==="bg-size",Ng=u=>u==="length",Ip=u=>u==="number",e0=u=>u==="family-name",Rg=u=>u==="shadow",t0=()=>{const u=st("color"),r=st("font"),f=st("text"),c=st("font-weight"),S=st("tracking"),C=st("leading"),M=st("breakpoint"),F=st("container"),U=st("spacing"),v=st("radius"),q=st("shadow"),X=st("inset-shadow"),W=st("text-shadow"),se=st("drop-shadow"),$=st("blur"),ee=st("perspective"),te=st("aspect"),_=st("ease"),K=st("animate"),P=()=>["auto","avoid","all","avoid-page","page","left","right","column"],B=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],le=()=>[...B(),oe,ue],ne=()=>["auto","hidden","clip","visible","scroll"],A=()=>["auto","contain","none"],D=()=>[oe,ue,U],ae=()=>[Za,"full","auto",...D()],ye=()=>[Yl,"none","subgrid",oe,ue],ze=()=>["auto",{span:["full",Yl,oe,ue]},Yl,oe,ue],he=()=>[Yl,"auto",oe,ue],Re=()=>["auto","min","max","fr",oe,ue],et=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],Pe=()=>["start","end","center","stretch","center-safe","end-safe"],z=()=>["auto",...D()],k=()=>[Za,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...D()],Q=()=>[u,oe,ue],Ae=()=>[...B(),dg,fg,{position:[oe,ue]}],xe=()=>["no-repeat",{repeat:["","x","y","space","round"]}],g=()=>["auto","cover","contain",Wp,kp,{size:[oe,ue]}],N=()=>[Go,Vn,oa],J=()=>["","none","full",v,oe,ue],I=()=>["",Te,Vn,oa],re=()=>["solid","dashed","dotted","double"],pe=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ge=()=>[Te,Go,dg,fg],Je=()=>["","none",$,oe,ue],Ge=()=>["none",Te,oe,ue],pt=()=>["none",Te,oe,ue],j=()=>[Te,oe,ue],h=()=>[Za,"full",...D()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[hl],breakpoint:[hl],color:[Pp],container:[hl],"drop-shadow":[hl],ease:["in","out","in-out"],font:[Zp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[hl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[hl],shadow:[hl],spacing:["px",Te],text:[hl],"text-shadow":[hl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",Za,ue,oe,te]}],container:["container"],columns:[{columns:[Te,ue,oe,F]}],"break-after":[{"break-after":P()}],"break-before":[{"break-before":P()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:le()}],overflow:[{overflow:ne()}],"overflow-x":[{"overflow-x":ne()}],"overflow-y":[{"overflow-y":ne()}],overscroll:[{overscroll:A()}],"overscroll-x":[{"overscroll-x":A()}],"overscroll-y":[{"overscroll-y":A()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:ae()}],"inset-x":[{"inset-x":ae()}],"inset-y":[{"inset-y":ae()}],start:[{start:ae()}],end:[{end:ae()}],top:[{top:ae()}],right:[{right:ae()}],bottom:[{bottom:ae()}],left:[{left:ae()}],visibility:["visible","invisible","collapse"],z:[{z:[Yl,"auto",oe,ue]}],basis:[{basis:[Za,"full","auto",F,...D()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Te,Za,"auto","initial","none",ue]}],grow:[{grow:["",Te,oe,ue]}],shrink:[{shrink:["",Te,oe,ue]}],order:[{order:[Yl,"first","last","none",oe,ue]}],"grid-cols":[{"grid-cols":ye()}],"col-start-end":[{col:ze()}],"col-start":[{"col-start":he()}],"col-end":[{"col-end":he()}],"grid-rows":[{"grid-rows":ye()}],"row-start-end":[{row:ze()}],"row-start":[{"row-start":he()}],"row-end":[{"row-end":he()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":Re()}],"auto-rows":[{"auto-rows":Re()}],gap:[{gap:D()}],"gap-x":[{"gap-x":D()}],"gap-y":[{"gap-y":D()}],"justify-content":[{justify:[...et(),"normal"]}],"justify-items":[{"justify-items":[...Pe(),"normal"]}],"justify-self":[{"justify-self":["auto",...Pe()]}],"align-content":[{content:["normal",...et()]}],"align-items":[{items:[...Pe(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...Pe(),{baseline:["","last"]}]}],"place-content":[{"place-content":et()}],"place-items":[{"place-items":[...Pe(),"baseline"]}],"place-self":[{"place-self":["auto",...Pe()]}],p:[{p:D()}],px:[{px:D()}],py:[{py:D()}],ps:[{ps:D()}],pe:[{pe:D()}],pt:[{pt:D()}],pr:[{pr:D()}],pb:[{pb:D()}],pl:[{pl:D()}],m:[{m:z()}],mx:[{mx:z()}],my:[{my:z()}],ms:[{ms:z()}],me:[{me:z()}],mt:[{mt:z()}],mr:[{mr:z()}],mb:[{mb:z()}],ml:[{ml:z()}],"space-x":[{"space-x":D()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":D()}],"space-y-reverse":["space-y-reverse"],size:[{size:k()}],w:[{w:[F,"screen",...k()]}],"min-w":[{"min-w":[F,"screen","none",...k()]}],"max-w":[{"max-w":[F,"screen","none","prose",{screen:[M]},...k()]}],h:[{h:["screen","lh",...k()]}],"min-h":[{"min-h":["screen","lh","none",...k()]}],"max-h":[{"max-h":["screen","lh",...k()]}],"font-size":[{text:["base",f,Vn,oa]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,oe,Ko]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Go,ue]}],"font-family":[{font:[$p,ue,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[S,oe,ue]}],"line-clamp":[{"line-clamp":[Te,"none",oe,Ko]}],leading:[{leading:[C,...D()]}],"list-image":[{"list-image":["none",oe,ue]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",oe,ue]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:Q()}],"text-color":[{text:Q()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...re(),"wavy"]}],"text-decoration-thickness":[{decoration:[Te,"from-font","auto",oe,oa]}],"text-decoration-color":[{decoration:Q()}],"underline-offset":[{"underline-offset":[Te,"auto",oe,ue]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:D()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",oe,ue]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",oe,ue]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Ae()}],"bg-repeat":[{bg:xe()}],"bg-size":[{bg:g()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},Yl,oe,ue],radial:["",oe,ue],conic:[Yl,oe,ue]},Fp,Jp]}],"bg-color":[{bg:Q()}],"gradient-from-pos":[{from:N()}],"gradient-via-pos":[{via:N()}],"gradient-to-pos":[{to:N()}],"gradient-from":[{from:Q()}],"gradient-via":[{via:Q()}],"gradient-to":[{to:Q()}],rounded:[{rounded:J()}],"rounded-s":[{"rounded-s":J()}],"rounded-e":[{"rounded-e":J()}],"rounded-t":[{"rounded-t":J()}],"rounded-r":[{"rounded-r":J()}],"rounded-b":[{"rounded-b":J()}],"rounded-l":[{"rounded-l":J()}],"rounded-ss":[{"rounded-ss":J()}],"rounded-se":[{"rounded-se":J()}],"rounded-ee":[{"rounded-ee":J()}],"rounded-es":[{"rounded-es":J()}],"rounded-tl":[{"rounded-tl":J()}],"rounded-tr":[{"rounded-tr":J()}],"rounded-br":[{"rounded-br":J()}],"rounded-bl":[{"rounded-bl":J()}],"border-w":[{border:I()}],"border-w-x":[{"border-x":I()}],"border-w-y":[{"border-y":I()}],"border-w-s":[{"border-s":I()}],"border-w-e":[{"border-e":I()}],"border-w-t":[{"border-t":I()}],"border-w-r":[{"border-r":I()}],"border-w-b":[{"border-b":I()}],"border-w-l":[{"border-l":I()}],"divide-x":[{"divide-x":I()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":I()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...re(),"hidden","none"]}],"divide-style":[{divide:[...re(),"hidden","none"]}],"border-color":[{border:Q()}],"border-color-x":[{"border-x":Q()}],"border-color-y":[{"border-y":Q()}],"border-color-s":[{"border-s":Q()}],"border-color-e":[{"border-e":Q()}],"border-color-t":[{"border-t":Q()}],"border-color-r":[{"border-r":Q()}],"border-color-b":[{"border-b":Q()}],"border-color-l":[{"border-l":Q()}],"divide-color":[{divide:Q()}],"outline-style":[{outline:[...re(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Te,oe,ue]}],"outline-w":[{outline:["",Te,Vn,oa]}],"outline-color":[{outline:Q()}],shadow:[{shadow:["","none",q,ms,gs]}],"shadow-color":[{shadow:Q()}],"inset-shadow":[{"inset-shadow":["none",X,ms,gs]}],"inset-shadow-color":[{"inset-shadow":Q()}],"ring-w":[{ring:I()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:Q()}],"ring-offset-w":[{"ring-offset":[Te,oa]}],"ring-offset-color":[{"ring-offset":Q()}],"inset-ring-w":[{"inset-ring":I()}],"inset-ring-color":[{"inset-ring":Q()}],"text-shadow":[{"text-shadow":["none",W,ms,gs]}],"text-shadow-color":[{"text-shadow":Q()}],opacity:[{opacity:[Te,oe,ue]}],"mix-blend":[{"mix-blend":[...pe(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":pe()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Te]}],"mask-image-linear-from-pos":[{"mask-linear-from":ge()}],"mask-image-linear-to-pos":[{"mask-linear-to":ge()}],"mask-image-linear-from-color":[{"mask-linear-from":Q()}],"mask-image-linear-to-color":[{"mask-linear-to":Q()}],"mask-image-t-from-pos":[{"mask-t-from":ge()}],"mask-image-t-to-pos":[{"mask-t-to":ge()}],"mask-image-t-from-color":[{"mask-t-from":Q()}],"mask-image-t-to-color":[{"mask-t-to":Q()}],"mask-image-r-from-pos":[{"mask-r-from":ge()}],"mask-image-r-to-pos":[{"mask-r-to":ge()}],"mask-image-r-from-color":[{"mask-r-from":Q()}],"mask-image-r-to-color":[{"mask-r-to":Q()}],"mask-image-b-from-pos":[{"mask-b-from":ge()}],"mask-image-b-to-pos":[{"mask-b-to":ge()}],"mask-image-b-from-color":[{"mask-b-from":Q()}],"mask-image-b-to-color":[{"mask-b-to":Q()}],"mask-image-l-from-pos":[{"mask-l-from":ge()}],"mask-image-l-to-pos":[{"mask-l-to":ge()}],"mask-image-l-from-color":[{"mask-l-from":Q()}],"mask-image-l-to-color":[{"mask-l-to":Q()}],"mask-image-x-from-pos":[{"mask-x-from":ge()}],"mask-image-x-to-pos":[{"mask-x-to":ge()}],"mask-image-x-from-color":[{"mask-x-from":Q()}],"mask-image-x-to-color":[{"mask-x-to":Q()}],"mask-image-y-from-pos":[{"mask-y-from":ge()}],"mask-image-y-to-pos":[{"mask-y-to":ge()}],"mask-image-y-from-color":[{"mask-y-from":Q()}],"mask-image-y-to-color":[{"mask-y-to":Q()}],"mask-image-radial":[{"mask-radial":[oe,ue]}],"mask-image-radial-from-pos":[{"mask-radial-from":ge()}],"mask-image-radial-to-pos":[{"mask-radial-to":ge()}],"mask-image-radial-from-color":[{"mask-radial-from":Q()}],"mask-image-radial-to-color":[{"mask-radial-to":Q()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":B()}],"mask-image-conic-pos":[{"mask-conic":[Te]}],"mask-image-conic-from-pos":[{"mask-conic-from":ge()}],"mask-image-conic-to-pos":[{"mask-conic-to":ge()}],"mask-image-conic-from-color":[{"mask-conic-from":Q()}],"mask-image-conic-to-color":[{"mask-conic-to":Q()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Ae()}],"mask-repeat":[{mask:xe()}],"mask-size":[{mask:g()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",oe,ue]}],filter:[{filter:["","none",oe,ue]}],blur:[{blur:Je()}],brightness:[{brightness:[Te,oe,ue]}],contrast:[{contrast:[Te,oe,ue]}],"drop-shadow":[{"drop-shadow":["","none",se,ms,gs]}],"drop-shadow-color":[{"drop-shadow":Q()}],grayscale:[{grayscale:["",Te,oe,ue]}],"hue-rotate":[{"hue-rotate":[Te,oe,ue]}],invert:[{invert:["",Te,oe,ue]}],saturate:[{saturate:[Te,oe,ue]}],sepia:[{sepia:["",Te,oe,ue]}],"backdrop-filter":[{"backdrop-filter":["","none",oe,ue]}],"backdrop-blur":[{"backdrop-blur":Je()}],"backdrop-brightness":[{"backdrop-brightness":[Te,oe,ue]}],"backdrop-contrast":[{"backdrop-contrast":[Te,oe,ue]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Te,oe,ue]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Te,oe,ue]}],"backdrop-invert":[{"backdrop-invert":["",Te,oe,ue]}],"backdrop-opacity":[{"backdrop-opacity":[Te,oe,ue]}],"backdrop-saturate":[{"backdrop-saturate":[Te,oe,ue]}],"backdrop-sepia":[{"backdrop-sepia":["",Te,oe,ue]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":D()}],"border-spacing-x":[{"border-spacing-x":D()}],"border-spacing-y":[{"border-spacing-y":D()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",oe,ue]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Te,"initial",oe,ue]}],ease:[{ease:["linear","initial",_,oe,ue]}],delay:[{delay:[Te,oe,ue]}],animate:[{animate:["none",K,oe,ue]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[ee,oe,ue]}],"perspective-origin":[{"perspective-origin":le()}],rotate:[{rotate:Ge()}],"rotate-x":[{"rotate-x":Ge()}],"rotate-y":[{"rotate-y":Ge()}],"rotate-z":[{"rotate-z":Ge()}],scale:[{scale:pt()}],"scale-x":[{"scale-x":pt()}],"scale-y":[{"scale-y":pt()}],"scale-z":[{"scale-z":pt()}],"scale-3d":["scale-3d"],skew:[{skew:j()}],"skew-x":[{"skew-x":j()}],"skew-y":[{"skew-y":j()}],transform:[{transform:[oe,ue,"","none","gpu","cpu"]}],"transform-origin":[{origin:le()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:h()}],"translate-x":[{"translate-x":h()}],"translate-y":[{"translate-y":h()}],"translate-z":[{"translate-z":h()}],"translate-none":["translate-none"],accent:[{accent:Q()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:Q()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",oe,ue]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":D()}],"scroll-mx":[{"scroll-mx":D()}],"scroll-my":[{"scroll-my":D()}],"scroll-ms":[{"scroll-ms":D()}],"scroll-me":[{"scroll-me":D()}],"scroll-mt":[{"scroll-mt":D()}],"scroll-mr":[{"scroll-mr":D()}],"scroll-mb":[{"scroll-mb":D()}],"scroll-ml":[{"scroll-ml":D()}],"scroll-p":[{"scroll-p":D()}],"scroll-px":[{"scroll-px":D()}],"scroll-py":[{"scroll-py":D()}],"scroll-ps":[{"scroll-ps":D()}],"scroll-pe":[{"scroll-pe":D()}],"scroll-pt":[{"scroll-pt":D()}],"scroll-pr":[{"scroll-pr":D()}],"scroll-pb":[{"scroll-pb":D()}],"scroll-pl":[{"scroll-pl":D()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",oe,ue]}],fill:[{fill:["none",...Q()]}],"stroke-w":[{stroke:[Te,Vn,oa,Ko]}],stroke:[{stroke:["none",...Q()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},l0=Hp(t0),gg=(...u)=>l0(Ep(u));var Bo,mg;function a0(){if(mg)return Bo;mg=1;var u=function(K){return r(K)&&!f(K)};function r(_){return!!_&&typeof _=="object"}function f(_){var K=Object.prototype.toString.call(_);return K==="[object RegExp]"||K==="[object Date]"||C(_)}var c=typeof Symbol=="function"&&Symbol.for,S=c?Symbol.for("react.element"):60103;function C(_){return _.$$typeof===S}function M(_){return Array.isArray(_)?[]:{}}function F(_,K){return K.clone!==!1&&K.isMergeableObject(_)?ee(M(_),_,K):_}function U(_,K,P){return _.concat(K).map(function(B){return F(B,P)})}function v(_,K){if(!K.customMerge)return ee;var P=K.customMerge(_);return typeof P=="function"?P:ee}function q(_){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(_).filter(function(K){return Object.propertyIsEnumerable.call(_,K)}):[]}function X(_){return Object.keys(_).concat(q(_))}function W(_,K){try{return K in _}catch{return!1}}function se(_,K){return W(_,K)&&!(Object.hasOwnProperty.call(_,K)&&Object.propertyIsEnumerable.call(_,K))}function $(_,K,P){var B={};return P.isMergeableObject(_)&&X(_).forEach(function(le){B[le]=F(_[le],P)}),X(K).forEach(function(le){se(_,le)||(W(_,le)&&P.isMergeableObject(K[le])?B[le]=v(le,P)(_[le],K[le],P):B[le]=F(K[le],P))}),B}function ee(_,K,P){P=P||{},P.arrayMerge=P.arrayMerge||U,P.isMergeableObject=P.isMergeableObject||u,P.cloneUnlessOtherwiseSpecified=F;var B=Array.isArray(K),le=Array.isArray(_),ne=B===le;return ne?B?P.arrayMerge(_,K,P):$(_,K,P):F(K,P)}ee.all=function(K,P){if(!Array.isArray(K))throw new Error("first argument should be an array");return K.reduce(function(B,le){return ee(B,le,P)},{})};var te=ee;return Bo=te,Bo}a0();var Lo={exports:{}},Zn={},qo={exports:{}},Yo={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var yg;function n0(){return yg||(yg=1,(function(u){function r(z,k){var Q=z.length;z.push(k);e:for(;0>>1,xe=z[Ae];if(0>>1;AeS(J,Q))IS(re,J)?(z[Ae]=re,z[I]=Q,Ae=I):(z[Ae]=J,z[N]=Q,Ae=N);else if(IS(re,Q))z[Ae]=re,z[I]=Q,Ae=I;else break e}}return k}function S(z,k){var Q=z.sortIndex-k.sortIndex;return Q!==0?Q:z.id-k.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;u.unstable_now=function(){return C.now()}}else{var M=Date,F=M.now();u.unstable_now=function(){return M.now()-F}}var U=[],v=[],q=1,X=null,W=3,se=!1,$=!1,ee=!1,te=!1,_=typeof setTimeout=="function"?setTimeout:null,K=typeof clearTimeout=="function"?clearTimeout:null,P=typeof setImmediate<"u"?setImmediate:null;function B(z){for(var k=f(v);k!==null;){if(k.callback===null)c(v);else if(k.startTime<=z)c(v),k.sortIndex=k.expirationTime,r(U,k);else break;k=f(v)}}function le(z){if(ee=!1,B(z),!$)if(f(U)!==null)$=!0,ne||(ne=!0,he());else{var k=f(v);k!==null&&Pe(le,k.startTime-z)}}var ne=!1,A=-1,D=5,ae=-1;function ye(){return te?!0:!(u.unstable_now()-aez&&ye());){var Ae=X.callback;if(typeof Ae=="function"){X.callback=null,W=X.priorityLevel;var xe=Ae(X.expirationTime<=z);if(z=u.unstable_now(),typeof xe=="function"){X.callback=xe,B(z),k=!0;break t}X===f(U)&&c(U),B(z)}else c(U);X=f(U)}if(X!==null)k=!0;else{var g=f(v);g!==null&&Pe(le,g.startTime-z),k=!1}}break e}finally{X=null,W=Q,se=!1}k=void 0}}finally{k?he():ne=!1}}}var he;if(typeof P=="function")he=function(){P(ze)};else if(typeof MessageChannel<"u"){var Re=new MessageChannel,et=Re.port2;Re.port1.onmessage=ze,he=function(){et.postMessage(null)}}else he=function(){_(ze,0)};function Pe(z,k){A=_(function(){z(u.unstable_now())},k)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(z){z.callback=null},u.unstable_forceFrameRate=function(z){0>z||125Ae?(z.sortIndex=Q,r(v,z),f(U)===null&&z===f(v)&&(ee?(K(A),A=-1):ee=!0,Pe(le,Q-Ae))):(z.sortIndex=xe,r(U,z),$||se||($=!0,ne||(ne=!0,he()))),z},u.unstable_shouldYield=ye,u.unstable_wrapCallback=function(z){var k=W;return function(){var Q=W;W=k;try{return z.apply(this,arguments)}finally{W=Q}}}})(Yo)),Yo}var hg;function i0(){return hg||(hg=1,qo.exports=n0()),qo.exports}var Po={exports:{}},mt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var pg;function s0(){if(pg)return mt;pg=1;var u=Jo();function r(U){var v="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Po.exports=s0(),Po.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var bg;function o0(){if(bg)return Zn;bg=1;var u=i0(),r=Jo(),f=u0();function c(e){var t="https://react.dev/errors/"+e;if(1xe||(e.current=Ae[xe],Ae[xe]=null,xe--)}function J(e,t){xe++,Ae[xe]=e.current,e.current=t}var I=g(null),re=g(null),pe=g(null),ge=g(null);function Je(e,t){switch(J(pe,t),J(re,e),J(I,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Sd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Sd(t),e=_d(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}N(I),J(I,e)}function Ge(){N(I),N(re),N(pe)}function pt(e){e.memoizedState!==null&&J(ge,e);var t=I.current,l=_d(t,e.type);t!==l&&(J(re,e),J(I,l))}function j(e){re.current===e&&(N(I),N(re)),ge.current===e&&(N(ge),qn._currentValue=Q)}var h,p;function Y(e){if(h===void 0)try{throw Error()}catch(l){var t=l.stack.trim().match(/\n( *(at )?)/);h=t&&t[1]||"",p=-1)":-1n||d[a]!==x[n]){var w=` -`+d[a].replace(" at new "," at ");return e.displayName&&w.includes("")&&(w=w.replace("",e.displayName)),w}while(1<=a&&0<=n);break}}}finally{R=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?Y(l):""}function Z(e,t){switch(e.tag){case 26:case 27:case 5:return Y(e.type);case 16:return Y("Lazy");case 13:return e.child!==t&&t!==null?Y("Suspense Fallback"):Y("Suspense");case 19:return Y("SuspenseList");case 0:case 15:return H(e.type,!1);case 11:return H(e.type.render,!1);case 1:return H(e.type,!0);case 31:return Y("Activity");default:return""}}function fe(e){try{var t="",l=null;do t+=Z(e,l),l=e,e=e.return;while(e);return t}catch(a){return` -Error generating stack: `+a.message+` -`+a.stack}}var _e=Object.prototype.hasOwnProperty,ve=u.unstable_scheduleCallback,Ce=u.unstable_cancelCallback,Xe=u.unstable_shouldYield,Pl=u.unstable_requestPaint,Tt=u.unstable_now,Hg=u.unstable_getCurrentPriorityLevel,Wo=u.unstable_ImmediatePriority,Fo=u.unstable_UserBlockingPriority,kn=u.unstable_NormalPriority,Gg=u.unstable_LowPriority,Io=u.unstable_IdlePriority,Kg=u.log,Bg=u.unstable_setDisableYieldValue,Fa=null,Ct=null;function pl(e){if(typeof Kg=="function"&&Bg(e),Ct&&typeof Ct.setStrictMode=="function")try{Ct.setStrictMode(Fa,e)}catch{}}var zt=Math.clz32?Math.clz32:Yg,Lg=Math.log,qg=Math.LN2;function Yg(e){return e>>>=0,e===0?32:31-(Lg(e)/qg|0)|0}var Jn=256,$n=262144,Wn=4194304;function Xl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Fn(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var n=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var o=a&134217727;return o!==0?(a=o&~i,a!==0?n=Xl(a):(s&=o,s!==0?n=Xl(s):l||(l=o&~e,l!==0&&(n=Xl(l))))):(o=a&~i,o!==0?n=Xl(o):s!==0?n=Xl(s):l||(l=a&~e,l!==0&&(n=Xl(l)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:n}function Ia(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Pg(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ec(){var e=Wn;return Wn<<=1,(Wn&62914560)===0&&(Wn=4194304),e}function ps(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function en(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Xg(e,t,l,a,n,i){var s=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,d=e.expirationTimes,x=e.hiddenUpdates;for(l=s&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var $g=/[\n"\\]/g;function Ht(e){return e.replace($g,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function xs(e,t,l,a,n,i,s,o){e.name="",s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.type=s:e.removeAttribute("type"),t!=null?s==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Rt(t)):e.value!==""+Rt(t)&&(e.value=""+Rt(t)):s!=="submit"&&s!=="reset"||e.removeAttribute("value"),t!=null?As(e,s,Rt(t)):l!=null?As(e,s,Rt(l)):a!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.name=""+Rt(o):e.removeAttribute("name")}function gc(e,t,l,a,n,i,s,o){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){Es(e);return}l=l!=null?""+Rt(l):"",t=t!=null?""+Rt(t):l,o||t===e.value||(e.value=t),e.defaultValue=t}a=a??n,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=o?e.checked:!!a,e.defaultChecked=!!a,s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(e.name=s),Es(e)}function As(e,t,l){t==="number"&&ti(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function ma(e,t,l,a){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Os=!1;if(el)try{var nn={};Object.defineProperty(nn,"passive",{get:function(){Os=!0}}),window.addEventListener("test",nn,nn),window.removeEventListener("test",nn,nn)}catch{Os=!1}var bl=null,Ds=null,ai=null;function Sc(){if(ai)return ai;var e,t=Ds,l=t.length,a,n="value"in bl?bl.value:bl.textContent,i=n.length;for(e=0;e=on),Cc=" ",zc=!1;function Mc(e,t){switch(e){case"keyup":return Am.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Oc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var va=!1;function Cm(e,t){switch(e){case"compositionend":return Oc(t);case"keypress":return t.which!==32?null:(zc=!0,Cc);case"textInput":return e=t.data,e===Cc&&zc?null:e;default:return null}}function zm(e,t){if(va)return e==="compositionend"||!Rs&&Mc(e,t)?(e=Sc(),ai=Ds=bl=null,va=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Gc(l)}}function Bc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Bc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Lc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=ti(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=ti(e.document)}return t}function Ks(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var Rm=el&&"documentMode"in document&&11>=document.documentMode,ba=null,Bs=null,dn=null,Ls=!1;function qc(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Ls||ba==null||ba!==ti(a)||(a=ba,"selectionStart"in a&&Ks(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),dn&&fn(dn,a)||(dn=a,a=$i(Bs,"onSelect"),0>=s,n-=s,kt=1<<32-zt(t)+n|l<Ee?(Ue=ce,ce=null):Ue=ce.sibling;var Ne=T(b,ce,E[Ee],G);if(Ne===null){ce===null&&(ce=Ue);break}e&&ce&&Ne.alternate===null&&t(b,ce),m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne,ce=Ue}if(Ee===E.length)return l(b,ce),je&&ll(b,Ee),de;if(ce===null){for(;EeEe?(Ue=ce,ce=null):Ue=ce.sibling;var ql=T(b,ce,Ne.value,G);if(ql===null){ce===null&&(ce=Ue);break}e&&ce&&ql.alternate===null&&t(b,ce),m=i(ql,m,Ee),we===null?de=ql:we.sibling=ql,we=ql,ce=Ue}if(Ne.done)return l(b,ce),je&&ll(b,Ee),de;if(ce===null){for(;!Ne.done;Ee++,Ne=E.next())Ne=L(b,Ne.value,G),Ne!==null&&(m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne);return je&&ll(b,Ee),de}for(ce=a(ce);!Ne.done;Ee++,Ne=E.next())Ne=O(ce,b,Ee,Ne.value,G),Ne!==null&&(e&&Ne.alternate!==null&&ce.delete(Ne.key===null?Ee:Ne.key),m=i(Ne,m,Ee),we===null?de=Ne:we.sibling=Ne,we=Ne);return e&&ce.forEach(function(th){return t(b,th)}),je&&ll(b,Ee),de}function Ye(b,m,E,G){if(typeof E=="object"&&E!==null&&E.type===ee&&E.key===null&&(E=E.props.children),typeof E=="object"&&E!==null){switch(E.$$typeof){case se:e:{for(var de=E.key;m!==null;){if(m.key===de){if(de=E.type,de===ee){if(m.tag===7){l(b,m.sibling),G=n(m,E.props.children),G.return=b,b=G;break e}}else if(m.elementType===de||typeof de=="object"&&de!==null&&de.$$typeof===D&&ta(de)===m.type){l(b,m.sibling),G=n(m,E.props),vn(G,E),G.return=b,b=G;break e}l(b,m);break}else t(b,m);m=m.sibling}E.type===ee?(G=$l(E.props.children,b.mode,G,E.key),G.return=b,b=G):(G=gi(E.type,E.key,E.props,null,b.mode,G),vn(G,E),G.return=b,b=G)}return s(b);case $:e:{for(de=E.key;m!==null;){if(m.key===de)if(m.tag===4&&m.stateNode.containerInfo===E.containerInfo&&m.stateNode.implementation===E.implementation){l(b,m.sibling),G=n(m,E.children||[]),G.return=b,b=G;break e}else{l(b,m);break}else t(b,m);m=m.sibling}G=Zs(E,b.mode,G),G.return=b,b=G}return s(b);case D:return E=ta(E),Ye(b,m,E,G)}if(Pe(E))return ie(b,m,E,G);if(he(E)){if(de=he(E),typeof de!="function")throw Error(c(150));return E=de.call(E),me(b,m,E,G)}if(typeof E.then=="function")return Ye(b,m,Si(E),G);if(E.$$typeof===P)return Ye(b,m,hi(b,E),G);_i(b,E)}return typeof E=="string"&&E!==""||typeof E=="number"||typeof E=="bigint"?(E=""+E,m!==null&&m.tag===6?(l(b,m.sibling),G=n(m,E),G.return=b,b=G):(l(b,m),G=Vs(E,b.mode,G),G.return=b,b=G),s(b)):l(b,m)}return function(b,m,E,G){try{pn=0;var de=Ye(b,m,E,G);return Da=null,de}catch(ce){if(ce===Oa||ce===vi)throw ce;var we=Ot(29,ce,null,b.mode);return we.lanes=G,we.return=b,we}finally{}}}var aa=rr(!0),fr=rr(!1),Al=!1;function iu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function su(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Tl(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Cl(e,t,l){var a=e.updateQueue;if(a===null)return null;if(a=a.shared,(He&2)!==0){var n=a.pending;return n===null?t.next=t:(t.next=n.next,n.next=t),a.pending=t,t=di(e),kc(e,null,l),t}return fi(e,a,t,l),di(e)}function bn(e,t,l){if(t=t.updateQueue,t!==null&&(t=t.shared,(l&4194048)!==0)){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,lc(e,l)}}function uu(e,t){var l=e.updateQueue,a=e.alternate;if(a!==null&&(a=a.updateQueue,l===a)){var n=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var s={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};i===null?n=i=s:i=i.next=s,l=l.next}while(l!==null);i===null?n=i=t:i=i.next=t}else n=i=t;l={baseState:a.baseState,firstBaseUpdate:n,lastBaseUpdate:i,shared:a.shared,callbacks:a.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=t:e.next=t,l.lastBaseUpdate=t}var ou=!1;function Sn(){if(ou){var e=Ma;if(e!==null)throw e}}function _n(e,t,l,a){ou=!1;var n=e.updateQueue;Al=!1;var i=n.firstBaseUpdate,s=n.lastBaseUpdate,o=n.shared.pending;if(o!==null){n.shared.pending=null;var d=o,x=d.next;d.next=null,s===null?i=x:s.next=x,s=d;var w=e.alternate;w!==null&&(w=w.updateQueue,o=w.lastBaseUpdate,o!==s&&(o===null?w.firstBaseUpdate=x:o.next=x,w.lastBaseUpdate=d))}if(i!==null){var L=n.baseState;s=0,w=x=d=null,o=i;do{var T=o.lane&-536870913,O=T!==o.lane;if(O?(De&T)===T:(a&T)===T){T!==0&&T===za&&(ou=!0),w!==null&&(w=w.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var ie=e,me=o;T=t;var Ye=l;switch(me.tag){case 1:if(ie=me.payload,typeof ie=="function"){L=ie.call(Ye,L,T);break e}L=ie;break e;case 3:ie.flags=ie.flags&-65537|128;case 0:if(ie=me.payload,T=typeof ie=="function"?ie.call(Ye,L,T):ie,T==null)break e;L=X({},L,T);break e;case 2:Al=!0}}T=o.callback,T!==null&&(e.flags|=64,O&&(e.flags|=8192),O=n.callbacks,O===null?n.callbacks=[T]:O.push(T))}else O={lane:T,tag:o.tag,payload:o.payload,callback:o.callback,next:null},w===null?(x=w=O,d=L):w=w.next=O,s|=T;if(o=o.next,o===null){if(o=n.shared.pending,o===null)break;O=o,o=O.next,O.next=null,n.lastBaseUpdate=O,n.shared.pending=null}}while(!0);w===null&&(d=L),n.baseState=d,n.firstBaseUpdate=x,n.lastBaseUpdate=w,i===null&&(n.shared.lanes=0),Ul|=s,e.lanes=s,e.memoizedState=L}}function dr(e,t){if(typeof e!="function")throw Error(c(191,e));e.call(t)}function gr(e,t){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ei?i:8;var s=z.T,o={};z.T=o,zu(e,!1,t,l);try{var d=n(),x=z.S;if(x!==null&&x(o,d),d!==null&&typeof d=="object"&&typeof d.then=="function"){var w=Xm(d,a);An(e,t,w,Nt(e))}else An(e,t,a,Nt(e))}catch(L){An(e,t,{then:function(){},status:"rejected",reason:L},Nt())}finally{k.p=i,s!==null&&o.types!==null&&(s.types=o.types),z.T=s}}function $m(){}function Tu(e,t,l,a){if(e.tag!==5)throw Error(c(476));var n=Qr(e).queue;Xr(e,n,t,Q,l===null?$m:function(){return Vr(e),l(a)})}function Qr(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Q,baseState:Q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:Q},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Vr(e){var t=Qr(e);t.next===null&&(t=e.alternate.memoizedState),An(e,t.next.queue,{},Nt())}function Cu(){return ft(qn)}function Zr(){return Ie().memoizedState}function kr(){return Ie().memoizedState}function Wm(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Nt();e=Tl(l);var a=Cl(t,e,l);a!==null&&(At(a,t,l),bn(a,t,l)),t={cache:tu()},e.payload=t;return}t=t.return}}function Fm(e,t,l){var a=Nt();l={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Ui(e)?$r(t,l):(l=Xs(e,t,l,a),l!==null&&(At(l,e,a),Wr(l,t,a)))}function Jr(e,t,l){var a=Nt();An(e,t,l,a)}function An(e,t,l,a){var n={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(Ui(e))$r(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,o=i(s,l);if(n.hasEagerState=!0,n.eagerState=o,Mt(o,s))return fi(e,t,n,0),Qe===null&&ri(),!1}catch{}finally{}if(l=Xs(e,t,n,a),l!==null)return At(l,e,a),Wr(l,t,a),!0}return!1}function zu(e,t,l,a){if(a={lane:2,revertLane:io(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},Ui(e)){if(t)throw Error(c(479))}else t=Xs(e,l,a,2),t!==null&&At(t,e,2)}function Ui(e){var t=e.alternate;return e===Se||t!==null&&t===Se}function $r(e,t){ja=Ai=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function Wr(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,lc(e,l)}}var Tn={readContext:ft,use:zi,useCallback:$e,useContext:$e,useEffect:$e,useImperativeHandle:$e,useLayoutEffect:$e,useInsertionEffect:$e,useMemo:$e,useReducer:$e,useRef:$e,useState:$e,useDebugValue:$e,useDeferredValue:$e,useTransition:$e,useSyncExternalStore:$e,useId:$e,useHostTransitionStatus:$e,useFormState:$e,useActionState:$e,useOptimistic:$e,useMemoCache:$e,useCacheRefresh:$e};Tn.useEffectEvent=$e;var Fr={readContext:ft,use:zi,useCallback:function(e,t){return ht().memoizedState=[e,t===void 0?null:t],e},useContext:ft,useEffect:Rr,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Oi(4194308,4,Br.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Oi(4194308,4,e,t)},useInsertionEffect:function(e,t){Oi(4,2,e,t)},useMemo:function(e,t){var l=ht();t=t===void 0?null:t;var a=e();if(na){pl(!0);try{e()}finally{pl(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=ht();if(l!==void 0){var n=l(t);if(na){pl(!0);try{l(t)}finally{pl(!1)}}}else n=t;return a.memoizedState=a.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},a.queue=e,e=e.dispatch=Fm.bind(null,Se,e),[a.memoizedState,e]},useRef:function(e){var t=ht();return e={current:e},t.memoizedState=e},useState:function(e){e=Su(e);var t=e.queue,l=Jr.bind(null,Se,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:xu,useDeferredValue:function(e,t){var l=ht();return Au(l,e,t)},useTransition:function(){var e=Su(!1);return e=Xr.bind(null,Se,e.queue,!0,!1),ht().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=Se,n=ht();if(je){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Qe===null)throw Error(c(349));(De&127)!==0||br(a,t,l)}n.memoizedState=l;var i={value:l,getSnapshot:t};return n.queue=i,Rr(_r.bind(null,a,i,e),[e]),a.flags|=2048,Na(9,{destroy:void 0},Sr.bind(null,a,i,l,t),null),l},useId:function(){var e=ht(),t=Qe.identifierPrefix;if(je){var l=Jt,a=kt;l=(a&~(1<<32-zt(a)-1)).toString(32)+l,t="_"+t+"R_"+l,l=Ti++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof a.is=="string"?s.createElement("select",{is:a.is}):s.createElement("select"),a.multiple?i.multiple=!0:a.size&&(i.size=a.size);break;default:i=typeof a.is=="string"?s.createElement(n,{is:a.is}):s.createElement(n)}}i[ct]=t,i[vt]=a;e:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)i.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break e;for(;s.sibling===null;){if(s.return===null||s.return===t)break e;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=i;e:switch(gt(i,n,a),n){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break e;case"img":a=!0;break e;default:a=!1}a&&ol(t)}}return Ze(t),qu(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,l),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==a&&ol(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(c(166));if(e=pe.current,Ta(t)){if(e=t.stateNode,l=t.memoizedProps,a=null,n=rt,n!==null)switch(n.tag){case 27:case 5:a=n.memoizedProps}e[ct]=t,e=!!(e.nodeValue===l||a!==null&&a.suppressHydrationWarning===!0||vd(e.nodeValue,l)),e||El(t,!0)}else e=Wi(e).createTextNode(a),e[ct]=t,t.stateNode=e}return Ze(t),null;case 31:if(l=t.memoizedState,e===null||e.memoizedState!==null){if(a=Ta(t),l!==null){if(e===null){if(!a)throw Error(c(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(c(557));e[ct]=t}else Wl(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ze(t),e=!1}else l=Ws(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return t.flags&256?(Ut(t),t):(Ut(t),null);if((t.flags&128)!==0)throw Error(c(558))}return Ze(t),null;case 13:if(a=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(n=Ta(t),a!==null&&a.dehydrated!==null){if(e===null){if(!n)throw Error(c(318));if(n=t.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(c(317));n[ct]=t}else Wl(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ze(t),n=!1}else n=Ws(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),n=!0;if(!n)return t.flags&256?(Ut(t),t):(Ut(t),null)}return Ut(t),(t.flags&128)!==0?(t.lanes=l,t):(l=a!==null,e=e!==null&&e.memoizedState!==null,l&&(a=t.child,n=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(n=a.alternate.memoizedState.cachePool.pool),i=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(i=a.memoizedState.cachePool.pool),i!==n&&(a.flags|=2048)),l!==e&&l&&(t.child.flags|=8192),Hi(t,t.updateQueue),Ze(t),null);case 4:return Ge(),e===null&&co(t.stateNode.containerInfo),Ze(t),null;case 10:return nl(t.type),Ze(t),null;case 19:if(N(Fe),a=t.memoizedState,a===null)return Ze(t),null;if(n=(t.flags&128)!==0,i=a.rendering,i===null)if(n)zn(a,!1);else{if(We!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=xi(e),i!==null){for(t.flags|=128,zn(a,!1),e=i.updateQueue,t.updateQueue=e,Hi(t,e),t.subtreeFlags=0,e=l,l=t.child;l!==null;)Jc(l,e),l=l.sibling;return J(Fe,Fe.current&1|2),je&&ll(t,a.treeForkCount),t.child}e=e.sibling}a.tail!==null&&Tt()>qi&&(t.flags|=128,n=!0,zn(a,!1),t.lanes=4194304)}else{if(!n)if(e=xi(i),e!==null){if(t.flags|=128,n=!0,e=e.updateQueue,t.updateQueue=e,Hi(t,e),zn(a,!0),a.tail===null&&a.tailMode==="hidden"&&!i.alternate&&!je)return Ze(t),null}else 2*Tt()-a.renderingStartTime>qi&&l!==536870912&&(t.flags|=128,n=!0,zn(a,!1),t.lanes=4194304);a.isBackwards?(i.sibling=t.child,t.child=i):(e=a.last,e!==null?e.sibling=i:t.child=i,a.last=i)}return a.tail!==null?(e=a.tail,a.rendering=e,a.tail=e.sibling,a.renderingStartTime=Tt(),e.sibling=null,l=Fe.current,J(Fe,n?l&1|2:l&1),je&&ll(t,a.treeForkCount),e):(Ze(t),null);case 22:case 23:return Ut(t),ru(),a=t.memoizedState!==null,e!==null?e.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(l&536870912)!==0&&(t.flags&128)===0&&(Ze(t),t.subtreeFlags&6&&(t.flags|=8192)):Ze(t),l=t.updateQueue,l!==null&&Hi(t,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==l&&(t.flags|=2048),e!==null&&N(ea),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),nl(tt),Ze(t),null;case 25:return null;case 30:return null}throw Error(c(156,t.tag))}function ay(e,t){switch(Js(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return nl(tt),Ge(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return j(t),null;case 31:if(t.memoizedState!==null){if(Ut(t),t.alternate===null)throw Error(c(340));Wl()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Ut(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(c(340));Wl()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return N(Fe),null;case 4:return Ge(),null;case 10:return nl(t.type),null;case 22:case 23:return Ut(t),ru(),e!==null&&N(ea),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return nl(tt),null;case 25:return null;default:return null}}function xf(e,t){switch(Js(t),t.tag){case 3:nl(tt),Ge();break;case 26:case 27:case 5:j(t);break;case 4:Ge();break;case 31:t.memoizedState!==null&&Ut(t);break;case 13:Ut(t);break;case 19:N(Fe);break;case 10:nl(t.type);break;case 22:case 23:Ut(t),ru(),e!==null&&N(ea);break;case 24:nl(tt)}}function Mn(e,t){try{var l=t.updateQueue,a=l!==null?l.lastEffect:null;if(a!==null){var n=a.next;l=n;do{if((l.tag&e)===e){a=void 0;var i=l.create,s=l.inst;a=i(),s.destroy=a}l=l.next}while(l!==n)}}catch(o){Be(t,t.return,o)}}function Ol(e,t,l){try{var a=t.updateQueue,n=a!==null?a.lastEffect:null;if(n!==null){var i=n.next;a=i;do{if((a.tag&e)===e){var s=a.inst,o=s.destroy;if(o!==void 0){s.destroy=void 0,n=t;var d=l,x=o;try{x()}catch(w){Be(n,d,w)}}}a=a.next}while(a!==i)}}catch(w){Be(t,t.return,w)}}function Af(e){var t=e.updateQueue;if(t!==null){var l=e.stateNode;try{gr(t,l)}catch(a){Be(e,e.return,a)}}}function Tf(e,t,l){l.props=ia(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(a){Be(e,t,a)}}function On(e,t){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var a=e.stateNode;break;case 30:a=e.stateNode;break;default:a=e.stateNode}typeof l=="function"?e.refCleanup=l(a):l.current=a}}catch(n){Be(e,t,n)}}function $t(e,t){var l=e.ref,a=e.refCleanup;if(l!==null)if(typeof a=="function")try{a()}catch(n){Be(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(n){Be(e,t,n)}else l.current=null}function Cf(e){var t=e.type,l=e.memoizedProps,a=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":l.autoFocus&&a.focus();break e;case"img":l.src?a.src=l.src:l.srcSet&&(a.srcset=l.srcSet)}}catch(n){Be(e,e.return,n)}}function Yu(e,t,l){try{var a=e.stateNode;Ty(a,e.type,l,t),a[vt]=t}catch(n){Be(e,e.return,n)}}function zf(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Hl(e.type)||e.tag===4}function Pu(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||zf(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Hl(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Xu(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,t):(t=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,t.appendChild(e),l=l._reactRootContainer,l!=null||t.onclick!==null||(t.onclick=It));else if(a!==4&&(a===27&&Hl(e.type)&&(l=e.stateNode,t=null),e=e.child,e!==null))for(Xu(e,t,l),e=e.sibling;e!==null;)Xu(e,t,l),e=e.sibling}function Gi(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?l.insertBefore(e,t):l.appendChild(e);else if(a!==4&&(a===27&&Hl(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Gi(e,t,l),e=e.sibling;e!==null;)Gi(e,t,l),e=e.sibling}function Mf(e){var t=e.stateNode,l=e.memoizedProps;try{for(var a=e.type,n=t.attributes;n.length;)t.removeAttributeNode(n[0]);gt(t,a,l),t[ct]=e,t[vt]=l}catch(i){Be(e,e.return,i)}}var cl=!1,nt=!1,Qu=!1,Of=typeof WeakSet=="function"?WeakSet:Set,ot=null;function ny(e,t){if(e=e.containerInfo,go=ns,e=Lc(e),Ks(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else e:{l=(l=e.ownerDocument)&&l.defaultView||window;var a=l.getSelection&&l.getSelection();if(a&&a.rangeCount!==0){l=a.anchorNode;var n=a.anchorOffset,i=a.focusNode;a=a.focusOffset;try{l.nodeType,i.nodeType}catch{l=null;break e}var s=0,o=-1,d=-1,x=0,w=0,L=e,T=null;t:for(;;){for(var O;L!==l||n!==0&&L.nodeType!==3||(o=s+n),L!==i||a!==0&&L.nodeType!==3||(d=s+a),L.nodeType===3&&(s+=L.nodeValue.length),(O=L.firstChild)!==null;)T=L,L=O;for(;;){if(L===e)break t;if(T===l&&++x===n&&(o=s),T===i&&++w===a&&(d=s),(O=L.nextSibling)!==null)break;L=T,T=L.parentNode}L=O}l=o===-1||d===-1?null:{start:o,end:d}}else l=null}l=l||{start:0,end:0}}else l=null;for(mo={focusedElem:e,selectionRange:l},ns=!1,ot=t;ot!==null;)if(t=ot,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,ot=e;else for(;ot!==null;){switch(t=ot,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),gt(i,a,l),i[ct]=e,ut(i),a=i;break e;case"link":var s=Rd("link","href",n).get(a+(l.href||""));if(s){for(var o=0;oYe&&(s=Ye,Ye=me,me=s);var b=Kc(o,me),m=Kc(o,Ye);if(b&&m&&(O.rangeCount!==1||O.anchorNode!==b.node||O.anchorOffset!==b.offset||O.focusNode!==m.node||O.focusOffset!==m.offset)){var E=L.createRange();E.setStart(b.node,b.offset),O.removeAllRanges(),me>Ye?(O.addRange(E),O.extend(m.node,m.offset)):(E.setEnd(m.node,m.offset),O.addRange(E))}}}}for(L=[],O=o;O=O.parentNode;)O.nodeType===1&&L.push({element:O,left:O.scrollLeft,top:O.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;ol?32:l,z.T=null,l=Fu,Fu=null;var i=wl,s=ml;if(it=0,Ba=wl=null,ml=0,(He&6)!==0)throw Error(c(331));var o=He;if(He|=4,Lf(i.current),Gf(i,i.current,s,l),He=o,Rn(0,!1),Ct&&typeof Ct.onPostCommitFiberRoot=="function")try{Ct.onPostCommitFiberRoot(Fa,i)}catch{}return!0}finally{k.p=n,z.T=a,nd(e,t)}}function sd(e,t,l){t=Kt(l,t),t=Uu(e.stateNode,t,2),e=Cl(e,t,2),e!==null&&(en(e,2),Wt(e))}function Be(e,t,l){if(e.tag===3)sd(e,e,l);else for(;t!==null;){if(t.tag===3){sd(t,e,l);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(jl===null||!jl.has(a))){e=Kt(l,e),l=uf(2),a=Cl(t,l,2),a!==null&&(of(l,a,t,e),en(a,2),Wt(a));break}}t=t.return}}function lo(e,t,l){var a=e.pingCache;if(a===null){a=e.pingCache=new uy;var n=new Set;a.set(t,n)}else n=a.get(t),n===void 0&&(n=new Set,a.set(t,n));n.has(l)||(ku=!0,n.add(l),e=dy.bind(null,e,t,l),t.then(e,e))}function dy(e,t,l){var a=e.pingCache;a!==null&&a.delete(t),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,Qe===e&&(De&l)===l&&(We===4||We===3&&(De&62914560)===De&&300>Tt()-Li?(He&2)===0&&La(e,0):Ju|=l,Ka===De&&(Ka=0)),Wt(e)}function ud(e,t){t===0&&(t=ec()),e=Jl(e,t),e!==null&&(en(e,t),Wt(e))}function gy(e){var t=e.memoizedState,l=0;t!==null&&(l=t.retryLane),ud(e,l)}function my(e,t){var l=0;switch(e.tag){case 31:case 13:var a=e.stateNode,n=e.memoizedState;n!==null&&(l=n.retryLane);break;case 19:a=e.stateNode;break;case 22:a=e.stateNode._retryCache;break;default:throw Error(c(314))}a!==null&&a.delete(t),ud(e,l)}function yy(e,t){return ve(e,t)}var Zi=null,Ya=null,ao=!1,ki=!1,no=!1,Rl=0;function Wt(e){e!==Ya&&e.next===null&&(Ya===null?Zi=Ya=e:Ya=Ya.next=e),ki=!0,ao||(ao=!0,py())}function Rn(e,t){if(!no&&ki){no=!0;do for(var l=!1,a=Zi;a!==null;){if(e!==0){var n=a.pendingLanes;if(n===0)var i=0;else{var s=a.suspendedLanes,o=a.pingedLanes;i=(1<<31-zt(42|e)+1)-1,i&=n&~(s&~o),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(l=!0,fd(a,i))}else i=De,i=Fn(a,a===Qe?i:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(i&3)===0||Ia(a,i)||(l=!0,fd(a,i));a=a.next}while(l);no=!1}}function hy(){od()}function od(){ki=ao=!1;var e=0;Rl!==0&&zy()&&(e=Rl);for(var t=Tt(),l=null,a=Zi;a!==null;){var n=a.next,i=cd(a,t);i===0?(a.next=null,l===null?Zi=n:l.next=n,n===null&&(Ya=l)):(l=a,(e!==0||(i&3)!==0)&&(ki=!0)),a=n}it!==0&&it!==5||Rn(e),Rl!==0&&(Rl=0)}function cd(e,t){for(var l=e.suspendedLanes,a=e.pingedLanes,n=e.expirationTimes,i=e.pendingLanes&-62914561;0o)break;var w=d.transferSize,L=d.initiatorType;w&&bd(L)&&(d=d.responseEnd,s+=w*(d"u"?null:document;function Ud(e,t,l){var a=Pa;if(a&&typeof t=="string"&&t){var n=Ht(t);n='link[rel="'+e+'"][href="'+n+'"]',typeof l=="string"&&(n+='[crossorigin="'+l+'"]'),Dd.has(n)||(Dd.add(n),e={rel:e,crossOrigin:l,href:t},a.querySelector(n)===null&&(t=a.createElement("link"),gt(t,"link",e),ut(t),a.head.appendChild(t)))}}function Hy(e){yl.D(e),Ud("dns-prefetch",e,null)}function Gy(e,t){yl.C(e,t),Ud("preconnect",e,t)}function Ky(e,t,l){yl.L(e,t,l);var a=Pa;if(a&&e&&t){var n='link[rel="preload"][as="'+Ht(t)+'"]';t==="image"&&l&&l.imageSrcSet?(n+='[imagesrcset="'+Ht(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(n+='[imagesizes="'+Ht(l.imageSizes)+'"]')):n+='[href="'+Ht(e)+'"]';var i=n;switch(t){case"style":i=Xa(e);break;case"script":i=Qa(e)}Xt.has(i)||(e=X({rel:"preload",href:t==="image"&&l&&l.imageSrcSet?void 0:e,as:t},l),Xt.set(i,e),a.querySelector(n)!==null||t==="style"&&a.querySelector(Bn(i))||t==="script"&&a.querySelector(Ln(i))||(t=a.createElement("link"),gt(t,"link",e),ut(t),a.head.appendChild(t)))}}function By(e,t){yl.m(e,t);var l=Pa;if(l&&e){var a=t&&typeof t.as=="string"?t.as:"script",n='link[rel="modulepreload"][as="'+Ht(a)+'"][href="'+Ht(e)+'"]',i=n;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=Qa(e)}if(!Xt.has(i)&&(e=X({rel:"modulepreload",href:e},t),Xt.set(i,e),l.querySelector(n)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(Ln(i)))return}a=l.createElement("link"),gt(a,"link",e),ut(a),l.head.appendChild(a)}}}function Ly(e,t,l){yl.S(e,t,l);var a=Pa;if(a&&e){var n=da(a).hoistableStyles,i=Xa(e);t=t||"default";var s=n.get(i);if(!s){var o={loading:0,preload:null};if(s=a.querySelector(Bn(i)))o.loading=5;else{e=X({rel:"stylesheet",href:e,"data-precedence":t},l),(l=Xt.get(i))&&_o(e,l);var d=s=a.createElement("link");ut(d),gt(d,"link",e),d._p=new Promise(function(x,w){d.onload=x,d.onerror=w}),d.addEventListener("load",function(){o.loading|=1}),d.addEventListener("error",function(){o.loading|=2}),o.loading|=4,Ii(s,t,a)}s={type:"stylesheet",instance:s,count:1,state:o},n.set(i,s)}}}function qy(e,t){yl.X(e,t);var l=Pa;if(l&&e){var a=da(l).hoistableScripts,n=Qa(e),i=a.get(n);i||(i=l.querySelector(Ln(n)),i||(e=X({src:e,async:!0},t),(t=Xt.get(n))&&Eo(e,t),i=l.createElement("script"),ut(i),gt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(n,i))}}function Yy(e,t){yl.M(e,t);var l=Pa;if(l&&e){var a=da(l).hoistableScripts,n=Qa(e),i=a.get(n);i||(i=l.querySelector(Ln(n)),i||(e=X({src:e,async:!0,type:"module"},t),(t=Xt.get(n))&&Eo(e,t),i=l.createElement("script"),ut(i),gt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(n,i))}}function jd(e,t,l,a){var n=(n=pe.current)?Fi(n):null;if(!n)throw Error(c(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(t=Xa(l.href),l=da(n).hoistableStyles,a=l.get(t),a||(a={type:"style",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=Xa(l.href);var i=da(n).hoistableStyles,s=i.get(e);if(s||(n=n.ownerDocument||n,s={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,s),(i=n.querySelector(Bn(e)))&&!i._p&&(s.instance=i,s.state.loading=5),Xt.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},Xt.set(e,l),i||Py(n,e,l,s.state))),t&&a===null)throw Error(c(528,""));return s}if(t&&a!==null)throw Error(c(529,""));return null;case"script":return t=l.async,l=l.src,typeof l=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=Qa(l),l=da(n).hoistableScripts,a=l.get(t),a||(a={type:"script",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(c(444,e))}}function Xa(e){return'href="'+Ht(e)+'"'}function Bn(e){return'link[rel="stylesheet"]['+e+"]"}function wd(e){return X({},e,{"data-precedence":e.precedence,precedence:null})}function Py(e,t,l,a){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=e.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),gt(t,"link",l),ut(t),e.head.appendChild(t))}function Qa(e){return'[src="'+Ht(e)+'"]'}function Ln(e){return"script[async]"+e}function Nd(e,t,l){if(t.count++,t.instance===null)switch(t.type){case"style":var a=e.querySelector('style[data-href~="'+Ht(l.href)+'"]');if(a)return t.instance=a,ut(a),a;var n=X({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return a=(e.ownerDocument||e).createElement("style"),ut(a),gt(a,"style",n),Ii(a,l.precedence,e),t.instance=a;case"stylesheet":n=Xa(l.href);var i=e.querySelector(Bn(n));if(i)return t.state.loading|=4,t.instance=i,ut(i),i;a=wd(l),(n=Xt.get(n))&&_o(a,n),i=(e.ownerDocument||e).createElement("link"),ut(i);var s=i;return s._p=new Promise(function(o,d){s.onload=o,s.onerror=d}),gt(i,"link",a),t.state.loading|=4,Ii(i,l.precedence,e),t.instance=i;case"script":return i=Qa(l.src),(n=e.querySelector(Ln(i)))?(t.instance=n,ut(n),n):(a=l,(n=Xt.get(i))&&(a=X({},l),Eo(a,n)),e=e.ownerDocument||e,n=e.createElement("script"),ut(n),gt(n,"link",a),e.head.appendChild(n),t.instance=n);case"void":return null;default:throw Error(c(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,Ii(a,l.precedence,e));return t.instance}function Ii(e,t,l){for(var a=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),n=a.length?a[a.length-1]:null,i=n,s=0;s title"):null)}function Xy(e,t,l){if(l===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return e=t.disabled,typeof t.precedence=="string"&&e==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Gd(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function Qy(e,t,l,a){if(l.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var n=Xa(a.href),i=t.querySelector(Bn(n));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=ts.bind(e),t.then(e,e)),l.state.loading|=4,l.instance=i,ut(i);return}i=t.ownerDocument||t,a=wd(a),(n=Xt.get(n))&&_o(a,n),i=i.createElement("link"),ut(i);var s=i;s._p=new Promise(function(o,d){s.onload=o,s.onerror=d}),gt(i,"link",a),l.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,t),(t=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=ts.bind(e),t.addEventListener("load",l),t.addEventListener("error",l))}}var xo=0;function Vy(e,t){return e.stylesheets&&e.count===0&&as(e,e.stylesheets),0xo?50:800)+t);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(a),clearTimeout(n)}}:null}function ts(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)as(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var ls=null;function as(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,ls=new Map,t.forEach(Zy,e),ls=null,ts.call(e))}function Zy(e,t){if(!(t.state.loading&4)){var l=ls.get(e);if(l)var a=l.get(null);else{l=new Map,ls.set(e,l);for(var n=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(r){console.error(r)}}return u(),Lo.exports=o0(),Lo.exports}var r0=c0();const f0=({error:u,resetError:r})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),u&&y.jsx("pre",{children:u.message}),r&&y.jsx("button",{onClick:r,children:"Сбросить"})]}),d0=({children:u})=>{const[r,f]=hs.useState(null),c=hs.useCallback(()=>f(null),[]);if(r)return y.jsx(f0,{error:r,resetError:c});try{return y.jsx(y.Fragment,{children:u})}catch(S){return f(S),null}},g0=({plugin:u,selected:r,onClick:f,isLight:c})=>{const S=u.settings?.enabled??!0;return y.jsxs("div",{className:`plugin-card${r?" selected":""}${c?"":" dark"}`,onClick:f,style:{border:S?"2px solid aqua":"2px solid #ccc"},children:[y.jsx("div",{className:"plugin-card-name",children:u.name}),y.jsxs("div",{className:"plugin-card-version",children:["v",u.version]}),y.jsx("div",{className:"plugin-card-description",children:u.description||"Описание не указано"}),y.jsx("div",{className:`plugin-card-status${S?" enabled":" disabled"}`,children:S?"Активен":"Неактивен"})]})},Xo=u=>{try{const r=new URL(u);return`${r.protocol}//${r.hostname}${r.pathname}`}catch{return u}},m0=()=>{const[u,r]=V.useState([]),[f,c]=V.useState(null),[S,C]=V.useState(!1),[M,F]=V.useState("chat"),[U,v]=V.useState(null),[q,X]=V.useState(null),[W,se]=V.useState([]),[$,ee]=V.useState(null),[te,_]=V.useState("system"),[K,P]=V.useState(!0);V.useEffect(()=>{const h=async()=>{const Y=await Ro.get();_(Y.theme),P(Y.isLight)};return h(),Ro.subscribe(()=>{h()})},[]);const B=V.useRef(null),le=V.useRef(!1),ne=V.useRef([]),A=V.useRef(null),[D,ae]=V.useState("connecting"),ye=V.useCallback(()=>le.current&&B.current!==null,[]),ze=V.useCallback(async(h,p=3)=>{if(!ye()){console.log("[SidePanel] Port not ready, queuing message:",h),ne.current.push(h);return}for(let Y=0;YsetTimeout(H,500*(Y+1)))}throw new Error("Failed to send message after all retries")},[ye]),he=V.useCallback(()=>{for(;ne.current.length>0&&ye();){const h=ne.current.shift();h&&ze(h).catch(p=>{console.error("[SidePanel] Failed to process queued message:",p)})}},[ye,ze]),Re=async(h,p)=>{const Y=`sidepanel_state_${h}`,R={selectedPluginId:p,showControlPanel:!0,timestamp:Date.now()};console.log("[SidePanel] Сохраняем состояние panel для страницы:",h,R),await chrome.storage.local.set({[Y]:R})},et=async h=>{const p=`sidepanel_state_${h}`;console.log("[SidePanel] Очищаем состояние panel для страницы:",h),await chrome.storage.local.remove(p)},Pe=async(h,p)=>{const Y=`sidepanel_state_${h}`,H=(await chrome.storage.local.get(Y))[Y];if(console.log("[SidePanel] Проверяем сохраненное состояние для страницы:",h,H),H&&H.selectedPluginId){const Z=p.find(fe=>fe.id===H.selectedPluginId);if(Z&&re(Z))return console.log("[SidePanel] Восстанавливаем состояние чата для страницы:",h,{pluginId:Z.id,pluginName:Z.name}),c(Z),C(!0),!0;console.log("[SidePanel] Плагин из сохраненного состояния не найден или не разрешен:",{pluginId:H.selectedPluginId,pluginFound:!!Z,isAllowed:Z?re(Z):!1})}return!1},z=V.useCallback(h=>{se(p=>p.filter(Y=>Y.id!==h))},[]),k=V.useCallback((h,p="success",Y=3e3)=>{const R=Date.now().toString(),H={id:R,message:h,type:p,duration:Y,timestamp:Date.now()};se(Z=>[...Z,H]),setTimeout(()=>{z(R)},Y)},[z]);V.useEffect(()=>{console.log("[SidePanel] useEffect вызван - загружаем плагины и URL"),J()},[]);const Q=V.useCallback(async(h=3,p=1e3)=>{console.log("[SidePanel][HEARTBEAT] Starting heartbeat ping with retry");for(let Y=0;YsetTimeout(H,p*(Y+1))))}return console.error("[SidePanel][HEARTBEAT] 💥 All heartbeat attempts failed - connection lost"),ae("disconnected"),!1},[ye,ze]),Ae=V.useCallback(async()=>{console.log("[SidePanel] Attempting to reconnect port...");try{B.current&&(B.current.disconnect(),B.current=null),le.current=!1;const h=chrome.runtime.connect();B.current=h,console.log("[SidePanel] New port created:",h.name),le.current=!0;const p=R=>{console.log("[SidePanel] Received message from background via port:",R),R.type==="GET_PLUGINS_RESPONSE"&&R.plugins&&Array.isArray(R.plugins)?(console.log("[SidePanel] Setting plugins from port message:",R.plugins),r(R.plugins),console.log("[SidePanel] ✅ Plugins loaded successfully")):R.type==="GET_PLUGINS_RESPONSE"&&R.error&&(console.error("[SidePanel] Error from background script:",R.error),k("Ошибка загрузки плагинов","error"))},Y=()=>{console.log("[SidePanel] Port disconnected, will attempt reconnection"),le.current=!1,B.current=null,ae("disconnected"),setTimeout(()=>{Ae()},1e3)};h.onMessage.addListener(p),h.onDisconnect.addListener(Y),he()}catch(h){console.error("[SidePanel] Failed to reconnect port:",h),k("Не удалось переподключить порт","error")}},[he,k]),xe=V.useCallback(async(h,p=3)=>{for(let Y=0;YsetTimeout(H,500*(Y+1)))}throw new Error("Failed to send message after all retries")},[]),g=V.useCallback(()=>{A.current&&clearInterval(A.current),console.log("[SidePanel][HEARTBEAT] 🚀 Starting heartbeat with 10s interval"),A.current=setInterval(async()=>{if(!await Q()){console.warn("[SidePanel][HEARTBEAT] ⚠️ Heartbeat failed, attempting to reconnect port...");try{await Ae()}catch(p){console.error("[SidePanel][HEARTBEAT] ❌ Port reconnection failed:",p)}}},1e4)},[Q,Ae]),N=V.useCallback(()=>{A.current&&(clearInterval(A.current),A.current=null)},[]);V.useEffect(()=>(console.log("[SidePanel] Запуск heartbeat механизма и подключения к порту"),g(),()=>{console.log("[SidePanel] Остановка heartbeat механизма и отключение порта"),N(),B.current&&B.current.disconnect()}),[g,N]),V.useEffect(()=>{console.log("[SidePanel] useEffect: Starting port-based plugin loading"),(async()=>{try{console.log("[SidePanel] Connecting to background script via port..."),await Ae();const p=5e3,Y=100;let R=0;for(;!ye()&&RsetTimeout(H,Y)),R+=Y;if(ye())await ze({type:"GET_PLUGINS"}),console.log("[SidePanel] Sent GET_PLUGINS message via port");else throw new Error("Port not ready after waiting")}catch(p){console.error("[SidePanel] Error in port-based plugin loading:",p),k("Ошибка связи с background script","error")}})()},[Ae,ye,ze,k]),V.useEffect(()=>{const h=()=>J();return chrome.tabs.onActivated.addListener(h),chrome.tabs.onUpdated.addListener(h),()=>{chrome.tabs.onActivated.removeListener(h),chrome.tabs.onUpdated.removeListener(h)}},[]);const J=async()=>{try{console.log("[SidePanel] Получение URL активной вкладки...");let h=null;try{const p=await chrome.tabs.query({active:!0,currentWindow:!0});console.log("[SidePanel] Способ 1 - найденные вкладки:",p),p[0]?.url&&(h=p[0])}catch(p){console.log("[SidePanel] Способ 1 не сработал:",p)}if(!h)try{const p=await chrome.tabs.query({windowId:chrome.windows.WINDOW_ID_CURRENT});console.log("[SidePanel] Способ 2 - все вкладки в окне:",p),h=p.find(Y=>Y.active)}catch(p){console.log("[SidePanel] Способ 2 не сработал:",p)}if(!h)try{const p=await xe({type:"GET_ACTIVE_TAB_URL"});if(console.log("[SidePanel] Способ 3 - ответ от background:",p),p?.url){ee(p.url);return}}catch(p){console.log("[SidePanel] Способ 3 не сработал:",p)}h?.url?(console.log("[SidePanel] Устанавливаем URL:",h.url),ee(h.url)):(console.log("[SidePanel] URL не найден, activeTab:",h),ee(null))}catch(h){console.error("[SidePanel] Ошибка получения URL активной вкладки:",h),ee(null)}},I=h=>{if(h==="")return/^https?:\/\/.+/;const p=h.match(/^(\*|http|https):\/\/([^/]+)\/(.*)$/);if(!p)return null;const[,Y,R,H]=p,Z=Y==="*"?"https?":Y;if(R.startsWith("*.")){const _e="(?:[\\w-]+\\.)*"+R.slice(2).replace(/\./g,"\\."),ve=H.replace(/\*/g,".*");return new RegExp(`^${Z}://${_e}/${ve}$`)}else{const fe=R.replace(/\./g,"\\."),_e=H.replace(/\*/g,".*");return new RegExp(`^${Z}://${fe}/${_e}$`)}},re=h=>{const p=Array.isArray(h.manifest?.host_permissions)?h.manifest?.host_permissions:h.host_permissions||[],Y=$||window.location.href;let R=!1;const H=[];console.log(`[SidePanel] Проверка плагина '${h.name}' для URL: ${Y}`),console.log("[SidePanel] host_permissions:",p);for(const Z of p){const fe=I(Z);if(!fe){H.push(`[${h.name}] Pattern '${Z}' не преобразован в RegExp`);continue}const _e=fe.test(Y);H.push(`[${h.name}] Pattern: '${Z}' → ${fe} => ${_e}`),_e&&(R=!0)}return R?console.log(`[SidePanel][DEBUG] Плагин '${h.name}' отображается для URL: ${Y}`):console.info(`[SidePanel] Плагин '${h.name}' не отображается для URL: ${Y}`),R},pe=async h=>{if(c(h),C(!0),F("chat"),$){const p=Xo($);await Re(p,h.id)}},ge=async()=>{if(f){v(f.id),X(null);try{const h=f.name||f.manifest?.name||f.id;await xe({type:"RUN_WORKFLOW",pluginId:f.id}),k(`Плагин ${h} запущен`,"success")}catch(h){console.error("Failed to run workflow:",h),k(`Ошибка запуска плагина ${f.name}`,"error")}finally{v(null)}}},Je=async()=>{f&&(q===f.id?(X(null),k(`Плагин ${f.name} возобновлен`,"success")):(X(f.id),k(`Плагин ${f.name} приостановлен`,"warning")))},Ge=async()=>{if(f)try{await xe({type:"STOP_WORKFLOW",pluginId:f.id}),v(null),X(null),k(`Плагин ${f.name} остановлен`,"success")}catch(h){console.error("Failed to stop workflow:",h),k(`Ошибка остановки плагина ${f.name}`,"error")}},pt=()=>{if(C(!1),c(null),F("chat"),$){const h=Xo($);et(h)}};V.useEffect(()=>{const h=(p,Y,R)=>{if(console.log("[SidePanel] Принято сообщение от Pyodide:",p),p.type==="PYODIDE_MESSAGE"){if(!f)return console.warn("[SidePanel][PYODIDE_MESSAGE] Игнорируем: selectedPlugin не установлен"),!0;if(console.log("[SidePanel] PYODIDE_MESSAGE получено:",p.message),!p.message||!p.message.content)return console.warn("[SidePanel][PYODIDE_MESSAGE] Игнорируем: message.content отсутствует или пустой",{hasMessage:!!p.message,hasContent:!!(p.message&&p.message.content)}),!0;console.log("[SidePanel] Отправляем PYODIDE_MESSAGE_UPDATE в PluginControlPanel");const H=new CustomEvent("PYODIDE_MESSAGE_UPDATE",{detail:{type:"PYODIDE_MESSAGE_UPDATE",message:p.message,timestamp:p.timestamp}});return window.dispatchEvent(H),console.log("[SidePanel] Событие PYODIDE_MESSAGE_UPDATE отправлено"),!0}return!1};return chrome.runtime.onMessage.addListener(h),console.log("[SidePanel] Handler для PYODIDE_MESSAGE зарегистрирован"),()=>{chrome.runtime.onMessage.removeListener(h),console.log("[SidePanel] Handler для PYODIDE_MESSAGE удален")}},[f]),V.useEffect(()=>{console.log("[SidePanel] currentTabUrl изменился:",$),(async()=>{if(!$||u.length===0)return;const p=Xo($);await Pe(p,u)||(console.log("[SidePanel] Состояние не восстановлено, проверяем сброс плагина"),f&&!re(f)&&(console.log("[SidePanel] Плагин не разрешен для новой страницы, сбрасываем состояние"),c(null),C(!1),v(null),X(null)))})()},[$,u,f]);const j=te==="dark"||te==="system"&&!K;return y.jsx(d0,{children:y.jsxs("div",{className:gg("App",j?"bg-gray-800":"bg-slate-50"),children:[y.jsx("header",{className:gg("App-header",j?"text-gray-100":"text-gray-900"),children:y.jsxs("div",{className:"header-controls",children:[y.jsx(_p,{theme:te,isLight:K,onToggle:Ro.toggle,isInSidebar:!0}),y.jsx("button",{onClick:()=>chrome.runtime.openOptionsPage(),className:"settings-btn",title:"Открыть настройки",children:y.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"3"}),y.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"})]})})]})}),y.jsx("main",{className:"side-panel-main",children:y.jsxs("section",{className:"plugins-section",children:[y.jsx("h3",{children:"Доступные плагины"}),y.jsx("div",{className:"plugins-grid",children:(()=>{console.log("[SidePanel] === РЕНДЕР ==="),console.log("[SidePanel] Состояние plugins:",u),console.log("[SidePanel] Состояние currentTabUrl:",$);const h=u.filter(re);return console.log("[SidePanel] Всего плагинов:",u.length),console.log("[SidePanel] Отфильтрованных плагинов:",h.length),console.log("[SidePanel] Отфильтрованные плагины:",h),h.map(p=>y.jsx(g0,{plugin:p,selected:f?.id===p.id,onClick:()=>pe(p),isLight:!j},p.id))})()})]})}),S&&f&&y.jsx(vp,{plugin:f,currentView:M,isRunning:U===f.id,isPaused:q===f.id,currentTabUrl:$,onStart:ge,onPause:Je,onStop:Ge,onClose:pt}),y.jsx(bp,{toasts:W,onRemove:z})]})})},y0=()=>{const u=document.querySelector("#app-container");if(!u)throw new Error("Can not find #app-container");r0.createRoot(u).render(y.jsx(m0,{}))};y0(); -//# sourceMappingURL=index-Da7zCQoy.js.map diff --git a/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js.map b/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js.map deleted file mode 100644 index c1b8a0c6..00000000 --- a/chrome-extension/public/side-panel/assets/index-Da7zCQoy.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index-Da7zCQoy.js","sources":["../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react-jsx-runtime.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js","../../../../pages/side-panel/src/components/DraftStatus.tsx","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/cjs/react.production.js","../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/index.js","../../../../pages/options/src/hooks/useTranslations.ts","../../../../pages/options/src/components/ToggleButton.tsx","../../../../pages/options/src/components/ErrorDisplay.tsx","../../../../pages/options/src/components/LocalErrorBoundary.tsx","../../../../pages/options/src/utils/encryption.ts","../../../../pages/options/src/hooks/usePluginSettings.ts","../../../../pages/options/src/components/LLMSelector.tsx","../../../../pages/options/src/hooks/useAIKeys.ts","../../../../pages/options/src/components/PluginDetails.tsx","../../../../pages/side-panel/src/components/PluginDetails.tsx","../../../../packages/shared/lib/utils/helpers.ts","../../../../pages/side-panel/src/hooks/useLazyChatSync.ts","../../../../node_modules/.pnpm/file-saver@2.0.5/node_modules/file-saver/dist/FileSaver.min.js","../../../../packages/storage/dist/lib/base/enums.js","../../../../packages/storage/dist/lib/base/base.js","../../../../packages/storage/dist/lib/impl/example-theme-storage.js","../../../../packages/storage/dist/lib/impl/example-chat-alignment-storage.js","../../../../pages/side-panel/src/components/PluginControlPanel.tsx","../../../../pages/side-panel/src/components/ToastNotifications.tsx","../../../../pages/options/src/components/ThemeSwitcher.tsx","../../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../../node_modules/.pnpm/tailwind-merge@3.3.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs","../../../../packages/ui/dist/lib/utils.js","../../../../node_modules/.pnpm/deepmerge@4.3.1/node_modules/deepmerge/dist/cjs.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.production.js","../../../../node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/index.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/cjs/react-dom-client.production.js","../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/client.js","../../../../pages/side-panel/src/components/ErrorDisplay.tsx","../../../../pages/side-panel/src/components/LocalErrorBoundary.tsx","../../../../pages/side-panel/src/components/PluginCard.tsx","../../../../pages/side-panel/src/SidePanel.tsx","../../../../pages/side-panel/src/index.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","import './DraftStatus.css';\n\ninterface DraftStatusProps {\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n messageLength: number;\n minLength: number;\n maxLength: number;\n}\n\nexport const DraftStatus: React.FC = ({\n isDraftSaved,\n isDraftLoading,\n draftError,\n messageLength,\n minLength,\n maxLength,\n}) => {\n const getStatusIcon = () => {\n if (isDraftLoading) {\n // CSS-анимированный круг для лоадера\n return
;\n }\n\n if (draftError) {\n return (\n \n \n \n \n \n );\n }\n\n if (isDraftSaved) {\n return (\n \n \n \n );\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return (\n \n \n \n \n );\n }\n\n return null;\n };\n\n const getStatusText = () => {\n if (isDraftLoading) {\n return 'Загрузка черновика...';\n }\n\n if (draftError) {\n return draftError;\n }\n\n if (isDraftSaved) {\n return 'Черновик сохранен';\n }\n\n if (messageLength > 0 && messageLength < minLength) {\n return `Еще ${minLength - messageLength} символов для сохранения`;\n }\n\n if (messageLength >= minLength && messageLength <= maxLength) {\n return 'Черновик будет сохранен автоматически';\n }\n\n if (messageLength > maxLength) {\n return 'Превышен лимит символов';\n }\n\n return '';\n };\n\n const getStatusClass = () => {\n if (isDraftLoading) return 'draft-status loading';\n if (draftError) return 'draft-status error';\n if (isDraftSaved) return 'draft-status saved';\n if (messageLength > 0 && messageLength < minLength) return 'draft-status pending';\n if (messageLength >= minLength && messageLength <= maxLength) return 'draft-status ready';\n if (messageLength > maxLength) return 'draft-status error';\n return 'draft-status';\n };\n\n if (messageLength === 0 && !isDraftLoading && !draftError) {\n return null; // Не показываем статус, если нет текста\n }\n\n return (\n
\n {getStatusIcon()}\n {getStatusText()}\n
\n );\n};\n","/**\n * @license React\n * react.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\"),\n REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\"),\n MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar ReactNoopUpdateQueue = {\n isMounted: function () {\n return !1;\n },\n enqueueForceUpdate: function () {},\n enqueueReplaceState: function () {},\n enqueueSetState: function () {}\n },\n assign = Object.assign,\n emptyObject = {};\nfunction Component(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nComponent.prototype.isReactComponent = {};\nComponent.prototype.setState = function (partialState, callback) {\n if (\n \"object\" !== typeof partialState &&\n \"function\" !== typeof partialState &&\n null != partialState\n )\n throw Error(\n \"takes an object of state variables to update or a function which returns an object of state variables.\"\n );\n this.updater.enqueueSetState(this, partialState, callback, \"setState\");\n};\nComponent.prototype.forceUpdate = function (callback) {\n this.updater.enqueueForceUpdate(this, callback, \"forceUpdate\");\n};\nfunction ComponentDummy() {}\nComponentDummy.prototype = Component.prototype;\nfunction PureComponent(props, context, updater) {\n this.props = props;\n this.context = context;\n this.refs = emptyObject;\n this.updater = updater || ReactNoopUpdateQueue;\n}\nvar pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());\npureComponentPrototype.constructor = PureComponent;\nassign(pureComponentPrototype, Component.prototype);\npureComponentPrototype.isPureReactComponent = !0;\nvar isArrayImpl = Array.isArray;\nfunction noop() {}\nvar ReactSharedInternals = { H: null, A: null, T: null, S: null },\n hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction ReactElement(type, key, props) {\n var refProp = props.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== refProp ? refProp : null,\n props: props\n };\n}\nfunction cloneAndReplaceKey(oldElement, newKey) {\n return ReactElement(oldElement.type, newKey, oldElement.props);\n}\nfunction isValidElement(object) {\n return (\n \"object\" === typeof object &&\n null !== object &&\n object.$$typeof === REACT_ELEMENT_TYPE\n );\n}\nfunction escape(key) {\n var escaperLookup = { \"=\": \"=0\", \":\": \"=2\" };\n return (\n \"$\" +\n key.replace(/[=:]/g, function (match) {\n return escaperLookup[match];\n })\n );\n}\nvar userProvidedKeyEscapeRegex = /\\/+/g;\nfunction getElementKey(element, index) {\n return \"object\" === typeof element && null !== element && null != element.key\n ? escape(\"\" + element.key)\n : index.toString(36);\n}\nfunction resolveThenable(thenable) {\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n default:\n switch (\n (\"string\" === typeof thenable.status\n ? thenable.then(noop, noop)\n : ((thenable.status = \"pending\"),\n thenable.then(\n function (fulfilledValue) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"fulfilled\"),\n (thenable.value = fulfilledValue));\n },\n function (error) {\n \"pending\" === thenable.status &&\n ((thenable.status = \"rejected\"), (thenable.reason = error));\n }\n )),\n thenable.status)\n ) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw thenable.reason;\n }\n }\n throw thenable;\n}\nfunction mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {\n var type = typeof children;\n if (\"undefined\" === type || \"boolean\" === type) children = null;\n var invokeCallback = !1;\n if (null === children) invokeCallback = !0;\n else\n switch (type) {\n case \"bigint\":\n case \"string\":\n case \"number\":\n invokeCallback = !0;\n break;\n case \"object\":\n switch (children.$$typeof) {\n case REACT_ELEMENT_TYPE:\n case REACT_PORTAL_TYPE:\n invokeCallback = !0;\n break;\n case REACT_LAZY_TYPE:\n return (\n (invokeCallback = children._init),\n mapIntoArray(\n invokeCallback(children._payload),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n )\n );\n }\n }\n if (invokeCallback)\n return (\n (callback = callback(children)),\n (invokeCallback =\n \"\" === nameSoFar ? \".\" + getElementKey(children, 0) : nameSoFar),\n isArrayImpl(callback)\n ? ((escapedPrefix = \"\"),\n null != invokeCallback &&\n (escapedPrefix =\n invokeCallback.replace(userProvidedKeyEscapeRegex, \"$&/\") + \"/\"),\n mapIntoArray(callback, array, escapedPrefix, \"\", function (c) {\n return c;\n }))\n : null != callback &&\n (isValidElement(callback) &&\n (callback = cloneAndReplaceKey(\n callback,\n escapedPrefix +\n (null == callback.key ||\n (children && children.key === callback.key)\n ? \"\"\n : (\"\" + callback.key).replace(\n userProvidedKeyEscapeRegex,\n \"$&/\"\n ) + \"/\") +\n invokeCallback\n )),\n array.push(callback)),\n 1\n );\n invokeCallback = 0;\n var nextNamePrefix = \"\" === nameSoFar ? \".\" : nameSoFar + \":\";\n if (isArrayImpl(children))\n for (var i = 0; i < children.length; i++)\n (nameSoFar = children[i]),\n (type = nextNamePrefix + getElementKey(nameSoFar, i)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (((i = getIteratorFn(children)), \"function\" === typeof i))\n for (\n children = i.call(children), i = 0;\n !(nameSoFar = children.next()).done;\n\n )\n (nameSoFar = nameSoFar.value),\n (type = nextNamePrefix + getElementKey(nameSoFar, i++)),\n (invokeCallback += mapIntoArray(\n nameSoFar,\n array,\n escapedPrefix,\n type,\n callback\n ));\n else if (\"object\" === type) {\n if (\"function\" === typeof children.then)\n return mapIntoArray(\n resolveThenable(children),\n array,\n escapedPrefix,\n nameSoFar,\n callback\n );\n array = String(children);\n throw Error(\n \"Objects are not valid as a React child (found: \" +\n (\"[object Object]\" === array\n ? \"object with keys {\" + Object.keys(children).join(\", \") + \"}\"\n : array) +\n \"). If you meant to render a collection of children, use an array instead.\"\n );\n }\n return invokeCallback;\n}\nfunction mapChildren(children, func, context) {\n if (null == children) return children;\n var result = [],\n count = 0;\n mapIntoArray(children, result, \"\", \"\", function (child) {\n return func.call(context, child, count++);\n });\n return result;\n}\nfunction lazyInitializer(payload) {\n if (-1 === payload._status) {\n var ctor = payload._result;\n ctor = ctor();\n ctor.then(\n function (moduleObject) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 1), (payload._result = moduleObject);\n },\n function (error) {\n if (0 === payload._status || -1 === payload._status)\n (payload._status = 2), (payload._result = error);\n }\n );\n -1 === payload._status && ((payload._status = 0), (payload._result = ctor));\n }\n if (1 === payload._status) return payload._result.default;\n throw payload._result;\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n Children = {\n map: mapChildren,\n forEach: function (children, forEachFunc, forEachContext) {\n mapChildren(\n children,\n function () {\n forEachFunc.apply(this, arguments);\n },\n forEachContext\n );\n },\n count: function (children) {\n var n = 0;\n mapChildren(children, function () {\n n++;\n });\n return n;\n },\n toArray: function (children) {\n return (\n mapChildren(children, function (child) {\n return child;\n }) || []\n );\n },\n only: function (children) {\n if (!isValidElement(children))\n throw Error(\n \"React.Children.only expected to receive a single React element child.\"\n );\n return children;\n }\n };\nexports.Activity = REACT_ACTIVITY_TYPE;\nexports.Children = Children;\nexports.Component = Component;\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.Profiler = REACT_PROFILER_TYPE;\nexports.PureComponent = PureComponent;\nexports.StrictMode = REACT_STRICT_MODE_TYPE;\nexports.Suspense = REACT_SUSPENSE_TYPE;\nexports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n ReactSharedInternals;\nexports.__COMPILER_RUNTIME = {\n __proto__: null,\n c: function (size) {\n return ReactSharedInternals.H.useMemoCache(size);\n }\n};\nexports.cache = function (fn) {\n return function () {\n return fn.apply(null, arguments);\n };\n};\nexports.cacheSignal = function () {\n return null;\n};\nexports.cloneElement = function (element, config, children) {\n if (null === element || void 0 === element)\n throw Error(\n \"The argument must be a React element, but you passed \" + element + \".\"\n );\n var props = assign({}, element.props),\n key = element.key;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n !hasOwnProperty.call(config, propName) ||\n \"key\" === propName ||\n \"__self\" === propName ||\n \"__source\" === propName ||\n (\"ref\" === propName && void 0 === config.ref) ||\n (props[propName] = config[propName]);\n var propName = arguments.length - 2;\n if (1 === propName) props.children = children;\n else if (1 < propName) {\n for (var childArray = Array(propName), i = 0; i < propName; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n return ReactElement(element.type, key, props);\n};\nexports.createContext = function (defaultValue) {\n defaultValue = {\n $$typeof: REACT_CONTEXT_TYPE,\n _currentValue: defaultValue,\n _currentValue2: defaultValue,\n _threadCount: 0,\n Provider: null,\n Consumer: null\n };\n defaultValue.Provider = defaultValue;\n defaultValue.Consumer = {\n $$typeof: REACT_CONSUMER_TYPE,\n _context: defaultValue\n };\n return defaultValue;\n};\nexports.createElement = function (type, config, children) {\n var propName,\n props = {},\n key = null;\n if (null != config)\n for (propName in (void 0 !== config.key && (key = \"\" + config.key), config))\n hasOwnProperty.call(config, propName) &&\n \"key\" !== propName &&\n \"__self\" !== propName &&\n \"__source\" !== propName &&\n (props[propName] = config[propName]);\n var childrenLength = arguments.length - 2;\n if (1 === childrenLength) props.children = children;\n else if (1 < childrenLength) {\n for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++)\n childArray[i] = arguments[i + 2];\n props.children = childArray;\n }\n if (type && type.defaultProps)\n for (propName in ((childrenLength = type.defaultProps), childrenLength))\n void 0 === props[propName] &&\n (props[propName] = childrenLength[propName]);\n return ReactElement(type, key, props);\n};\nexports.createRef = function () {\n return { current: null };\n};\nexports.forwardRef = function (render) {\n return { $$typeof: REACT_FORWARD_REF_TYPE, render: render };\n};\nexports.isValidElement = isValidElement;\nexports.lazy = function (ctor) {\n return {\n $$typeof: REACT_LAZY_TYPE,\n _payload: { _status: -1, _result: ctor },\n _init: lazyInitializer\n };\n};\nexports.memo = function (type, compare) {\n return {\n $$typeof: REACT_MEMO_TYPE,\n type: type,\n compare: void 0 === compare ? null : compare\n };\n};\nexports.startTransition = function (scope) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = scope(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n returnValue.then(noop, reportGlobalError);\n } catch (error) {\n reportGlobalError(error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n};\nexports.unstable_useCacheRefresh = function () {\n return ReactSharedInternals.H.useCacheRefresh();\n};\nexports.use = function (usable) {\n return ReactSharedInternals.H.use(usable);\n};\nexports.useActionState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useActionState(action, initialState, permalink);\n};\nexports.useCallback = function (callback, deps) {\n return ReactSharedInternals.H.useCallback(callback, deps);\n};\nexports.useContext = function (Context) {\n return ReactSharedInternals.H.useContext(Context);\n};\nexports.useDebugValue = function () {};\nexports.useDeferredValue = function (value, initialValue) {\n return ReactSharedInternals.H.useDeferredValue(value, initialValue);\n};\nexports.useEffect = function (create, deps) {\n return ReactSharedInternals.H.useEffect(create, deps);\n};\nexports.useEffectEvent = function (callback) {\n return ReactSharedInternals.H.useEffectEvent(callback);\n};\nexports.useId = function () {\n return ReactSharedInternals.H.useId();\n};\nexports.useImperativeHandle = function (ref, create, deps) {\n return ReactSharedInternals.H.useImperativeHandle(ref, create, deps);\n};\nexports.useInsertionEffect = function (create, deps) {\n return ReactSharedInternals.H.useInsertionEffect(create, deps);\n};\nexports.useLayoutEffect = function (create, deps) {\n return ReactSharedInternals.H.useLayoutEffect(create, deps);\n};\nexports.useMemo = function (create, deps) {\n return ReactSharedInternals.H.useMemo(create, deps);\n};\nexports.useOptimistic = function (passthrough, reducer) {\n return ReactSharedInternals.H.useOptimistic(passthrough, reducer);\n};\nexports.useReducer = function (reducer, initialArg, init) {\n return ReactSharedInternals.H.useReducer(reducer, initialArg, init);\n};\nexports.useRef = function (initialValue) {\n return ReactSharedInternals.H.useRef(initialValue);\n};\nexports.useState = function (initialState) {\n return ReactSharedInternals.H.useState(initialState);\n};\nexports.useSyncExternalStore = function (\n subscribe,\n getSnapshot,\n getServerSnapshot\n) {\n return ReactSharedInternals.H.useSyncExternalStore(\n subscribe,\n getSnapshot,\n getServerSnapshot\n );\n};\nexports.useTransition = function () {\n return ReactSharedInternals.H.useTransition();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react.production.js');\n} else {\n module.exports = require('./cjs/react.development.js');\n}\n","import * as React from 'react';\n\n// Импортируем переводы\nimport enTranslations from '../locales/en.json';\nimport ruTranslations from '../locales/ru.json';\n\nconst translations = {\n en: enTranslations,\n ru: ruTranslations,\n};\n\nexport type Locale = 'en' | 'ru';\n\nexport const useTranslations = (locale: Locale = 'en') => {\n const t = React.useMemo(() => {\n const dict: any = translations[locale] || {};\n\n // Функция для получения значения по пути с точками\n const getNestedValue = (obj: any, path: string): string => {\n return path.split('.').reduce((current, key) => {\n return current && current[key] ? current[key] : undefined;\n }, obj);\n };\n\n return (key: string) => {\n const value = getNestedValue(dict, key);\n return value !== undefined ? value : key;\n };\n }, [locale, translations]);\n\n return { t, locale };\n};\n","import React from 'react';\n\ninterface ToggleButtonProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n label?: React.ReactNode;\n iconOn?: React.ReactNode; // AI-First: иконка для состояния ON\n iconOff?: React.ReactNode; // AI-First: иконка для состояния OFF\n}\n\nconst ToggleButton: React.FC = ({ checked, onChange, disabled, label, iconOn, iconOff }) => (\n \n);\n\nexport default ToggleButton;\n","import React from 'react';\n\nconst ErrorDisplay: React.FC<{ error?: Error; resetError?: () => void }> = ({ error, resetError }) => (\n
\n

Произошла ошибка

\n {error &&
{error.message}
}\n {resetError && }\n
\n);\n\nexport default ErrorDisplay;\n","import React from 'react';\nimport ErrorDisplay from './ErrorDisplay';\n\nconst LocalErrorBoundary: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [error, setError] = React.useState(null);\n const resetError = React.useCallback(() => setError(null), []);\n\n if (error) {\n return ;\n }\n\n try {\n return <>{children};\n } catch (err) {\n setError(err as Error);\n return null;\n }\n};\n\nexport default LocalErrorBoundary;\n","/**\n * Утилиты для шифрования API-ключей\n * Использует Web Crypto API для безопасного хранения в chrome.storage.local\n */\n\nexport class APIKeyEncryption {\n private static readonly ALGORITHM = 'AES-GCM';\n private static readonly KEY_LENGTH = 256;\n private static readonly IV_LENGTH = 12;\n\n /**\n * Получает или создает ключ шифрования из chrome.storage.local\n */\n private static async getEncryptionKey(): Promise {\n try {\n // Проверяем, есть ли уже ключ в хранилище\n const result = await chrome.storage.local.get(['encryptionKey']);\n if (result.encryptionKey) {\n // Восстанавливаем ключ из массива байтов\n const keyData = new Uint8Array(result.encryptionKey);\n return await crypto.subtle.importKey(\n 'raw',\n keyData,\n this.ALGORITHM,\n false,\n ['encrypt', 'decrypt']\n );\n }\n\n // Создаем новый ключ\n const key = await crypto.subtle.generateKey(\n {\n name: this.ALGORITHM,\n length: this.KEY_LENGTH,\n },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Сохраняем ключ в хранилище\n const exportedKey = await crypto.subtle.exportKey('raw', key);\n const keyArray = new Uint8Array(exportedKey);\n await chrome.storage.local.set({\n encryptionKey: Array.from(keyArray)\n });\n\n return key;\n } catch (error) {\n console.error('Failed to get/create encryption key:', error);\n throw new Error('Не удалось инициализировать шифрование');\n }\n }\n\n /**\n * Шифрует текст\n */\n static async encrypt(text: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n const encodedText = new TextEncoder().encode(text);\n\n const encrypted = await crypto.subtle.encrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n encodedText\n );\n\n // Объединяем IV и зашифрованные данные\n const encryptedArray = new Uint8Array(encrypted);\n const resultArray = new Uint8Array(iv.length + encryptedArray.length);\n resultArray.set(iv);\n resultArray.set(encryptedArray, iv.length);\n\n // Кодируем в base64 для хранения в storage\n return btoa(String.fromCharCode(...resultArray));\n } catch (error) {\n console.error('Encryption failed:', error);\n throw new Error('Ошибка шифрования');\n }\n }\n\n /**\n * Расшифровывает текст\n */\n static async decrypt(encryptedText: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n\n // Декодируем из base64\n const encryptedArray = new Uint8Array(\n atob(encryptedText).split('').map(char => char.charCodeAt(0))\n );\n\n // Извлекаем IV и зашифрованные данные\n const iv = encryptedArray.slice(0, this.IV_LENGTH);\n const data = encryptedArray.slice(this.IV_LENGTH);\n\n const decrypted = await crypto.subtle.decrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n data\n );\n\n return new TextDecoder().decode(decrypted);\n } catch (error) {\n console.error('Decryption failed:', error);\n throw new Error('Ошибка расшифрования');\n }\n }\n\n /**\n * Валидация API ключа\n */\n static validateAPIKey(key: string): { isValid: boolean; error?: string } {\n if (!key || typeof key !== 'string') {\n return { isValid: false, error: 'Ключ не может быть пустым' };\n }\n\n if (key.length < 10) {\n return { isValid: false, error: 'Ключ слишком короткий' };\n }\n\n if (key.length > 200) {\n return { isValid: false, error: 'Ключ слишком длинный' };\n }\n\n // Проверяем на наличие потенциально опасных символов\n if (/[<>\\\"'&]/.test(key)) {\n return { isValid: false, error: 'Ключ содержит недопустимые символы' };\n }\n\n return { isValid: true };\n }\n}\n\n/**\n * Утилиты для безопасной работы с API ключами\n */\nexport class APIKeyManager {\n /**\n * Сохраняет API ключ с шифрованием\n */\n static async saveEncryptedKey(keyId: string, apiKey: string): Promise {\n try {\n const validation = APIKeyEncryption.validateAPIKey(apiKey);\n if (!validation.isValid) {\n throw new Error(validation.error);\n }\n\n const encryptedKey = await APIKeyEncryption.encrypt(apiKey);\n const keys = await this.getAllEncryptedKeys();\n\n keys[keyId] = encryptedKey;\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to save encrypted API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает расшифрованный API ключ\n */\n static async getDecryptedKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n const encryptedKey = keys[keyId];\n\n if (!encryptedKey) {\n return null;\n }\n\n return await APIKeyEncryption.decrypt(encryptedKey);\n } catch (error) {\n console.error('Failed to get decrypted API key:', error);\n return null;\n }\n }\n\n /**\n * Удаляет API ключ\n */\n static async removeKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n delete keys[keyId];\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to remove API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает все зашифрованные ключи\n */\n private static async getAllEncryptedKeys(): Promise> {\n try {\n const result = await chrome.storage.local.get(['encryptedApiKeys']);\n return result.encryptedApiKeys || {};\n } catch (error) {\n console.error('Failed to get encrypted keys:', error);\n return {};\n }\n }\n\n /**\n * Получает все ID ключей (без расшифровки)\n */\n static async getAllKeyIds(): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return Object.keys(keys);\n } catch (error) {\n console.error('Failed to get key IDs:', error);\n return [];\n }\n }\n\n /**\n * Проверяет, существует ли ключ\n */\n static async keyExists(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return keyId in keys;\n } catch (error) {\n console.error('Failed to check key existence:', error);\n return false;\n }\n }\n}","import * as React from 'react';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface PluginSettings {\n basic_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n deep_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n api_keys: {\n default: string; // encrypted\n };\n}\n\nexport interface PluginPromptSettings {\n llm: string;\n custom_prompt: string;\n}\n\nconst STORAGE_KEY = 'plugin-ozon-analyzer-settings';\n\n// Function to load default prompts from manifest.json\nconst loadDefaultPromptsFromManifest = async (): Promise<{ basic_analysis: { ru: string; en: string }; deep_analysis: { ru: string; en: string } }> => {\n try {\n // Load manifest.json from the plugin directory\n const manifestResponse = await fetch(chrome.runtime.getURL('plugins/ozon-analyzer/manifest.json'));\n const manifest = await manifestResponse.json();\n\n const prompts = manifest.options?.prompts;\n if (!prompts) {\n throw new Error('Prompts not found in manifest');\n }\n\n return {\n basic_analysis: {\n ru: prompts.basic_analysis?.ru?.default || '',\n en: prompts.basic_analysis?.en?.default || '',\n },\n deep_analysis: {\n ru: prompts.deep_analysis?.ru?.default || '',\n en: prompts.deep_analysis?.en?.default || '',\n },\n };\n } catch (error) {\n console.error('Failed to load prompts from manifest:', error);\n // Fallback to hardcoded defaults if manifest loading fails\n return {\n basic_analysis: {\n ru: 'верни слово basic_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n en: 'верни слово basic_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n },\n deep_analysis: {\n ru: 'верни слово deep_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n en: 'верни слово deep_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n },\n };\n }\n};\n\nconst DEFAULT_SETTINGS: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: '',\n },\n en: {\n llm: '',\n custom_prompt: '',\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: '',\n },\n en: {\n llm: '',\n custom_prompt: '',\n },\n },\n api_keys: {\n default: '',\n },\n};\n\nexport const usePluginSettings = () => {\n const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);\n const [isLoading, setIsLoading] = React.useState(true);\n\n React.useEffect(() => {\n initializeSettings();\n }, []);\n\n const initializeSettings = async () => {\n try {\n // Load default prompts from manifest first\n const defaultPrompts = await loadDefaultPromptsFromManifest();\n\n // Create initial settings with manifest defaults\n const initialSettings: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: defaultPrompts.basic_analysis.ru,\n },\n en: {\n llm: '',\n custom_prompt: defaultPrompts.basic_analysis.en,\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: defaultPrompts.deep_analysis.ru,\n },\n en: {\n llm: '',\n custom_prompt: defaultPrompts.deep_analysis.en,\n },\n },\n api_keys: {\n default: '',\n },\n };\n\n // Then load user settings and merge with defaults\n await loadSettings(initialSettings);\n } catch (error) {\n console.error('Failed to initialize settings:', error);\n // Fallback to loading with empty defaults\n await loadSettings(DEFAULT_SETTINGS);\n }\n };\n\n const loadSettings = async (defaultSettings: PluginSettings = DEFAULT_SETTINGS) => {\n try {\n setIsLoading(true);\n const result = await chrome.storage.local.get([STORAGE_KEY]);\n const storedSettings = result[STORAGE_KEY];\n\n if (storedSettings) {\n // Расшифровываем API ключи\n const decryptedApiKeys = { ...storedSettings.api_keys };\n if (storedSettings.api_keys?.default) {\n decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || '';\n }\n\n setSettings({\n ...defaultSettings,\n ...storedSettings,\n basic_analysis: {\n ru: {\n ...defaultSettings.basic_analysis.ru,\n ...storedSettings.basic_analysis?.ru,\n },\n en: {\n ...defaultSettings.basic_analysis.en,\n ...storedSettings.basic_analysis?.en,\n },\n },\n deep_analysis: {\n ru: {\n ...defaultSettings.deep_analysis.ru,\n ...storedSettings.deep_analysis?.ru,\n },\n en: {\n ...defaultSettings.deep_analysis.en,\n ...storedSettings.deep_analysis?.en,\n },\n },\n api_keys: decryptedApiKeys,\n });\n } else {\n setSettings(defaultSettings);\n }\n } catch (error) {\n console.error('Failed to load plugin settings:', error);\n setSettings(defaultSettings);\n } finally {\n setIsLoading(false);\n }\n };\n\n const saveSettings = async (newSettings: PluginSettings) => {\n try {\n // Шифруем API ключи перед сохранением\n const settingsToSave = { ...newSettings };\n if (newSettings.api_keys?.default) {\n await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default);\n } else {\n await APIKeyManager.removeKey('ozon-analyzer-default');\n }\n\n // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON\n settingsToSave.api_keys = { default: '' };\n\n await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave });\n setSettings(newSettings);\n } catch (error) {\n console.error('Failed to save plugin settings:', error);\n throw error;\n }\n };\n\n const updateBasicAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n basic_analysis: {\n ...settings.basic_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateDeepAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n deep_analysis: {\n ...settings.deep_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateAPIKey = async (key: string) => {\n const newSettings = {\n ...settings,\n api_keys: {\n default: key,\n },\n };\n await saveSettings(newSettings);\n };\n\n const getBasicAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.basic_analysis[language];\n };\n\n const getDeepAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.deep_analysis[language];\n };\n\n const getAPIKey = (): string => {\n return settings.api_keys?.default || '';\n };\n\n const resetToDefaults = async () => {\n await saveSettings(DEFAULT_SETTINGS);\n };\n\n return {\n settings,\n isLoading,\n updateBasicAnalysisSettings,\n updateDeepAnalysisSettings,\n updateAPIKey,\n getBasicAnalysisSettings,\n getDeepAnalysisSettings,\n getAPIKey,\n resetToDefaults,\n saveSettings,\n loadSettings,\n };\n};","import React, { useState, useEffect, useRef } from 'react';\nimport { usePluginSettings } from '../hooks/usePluginSettings';\nimport { AIKey } from '../hooks/useAIKeys';\nimport { APIKeyManager } from '../utils/encryption';\n\ninterface LLMSelectorProps {\n promptType: 'basic_analysis' | 'deep_analysis';\n language: 'ru' | 'en';\n globalAIKeys: AIKey[];\n defaultLLMCurl: string;\n hasDefaultLLM: boolean;\n onLLMChange: (llm: string, apiKey?: string) => void;\n}\n\nconst LLMSelector: React.FC = ({\n promptType,\n language,\n globalAIKeys,\n defaultLLMCurl,\n hasDefaultLLM,\n onLLMChange,\n}) => {\n const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings();\n const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl);\n const [apiKey, setApiKey] = useState('');\n const saveTimeoutRef = useRef(null);\n\n // Загружаем API-ключ при монтировании компонента\n useEffect(() => {\n const loadApiKey = async () => {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n const key = await APIKeyManager.getDecryptedKey(keyId) || '';\n setApiKey(key);\n };\n loadApiKey();\n }, [promptType, language]);\n\n // Загружаем LLM при изменении promptType/language\n useEffect(() => {\n const currentSettings = promptType === 'basic_analysis'\n ? settings.basic_analysis[language]\n : settings.deep_analysis[language];\n\n let initialLLM = '';\n\n if (currentSettings) {\n const savedLLM = currentSettings.llm;\n initialLLM = savedLLM || (hasDefaultLLM ? 'default' : '');\n } else {\n initialLLM = hasDefaultLLM ? 'default' : '';\n }\n\n setSelectedLLM(initialLLM);\n }, [promptType, language, settings, hasDefaultLLM]);\n\n // Сохраняем выбор LLM\n const handleLLMChange = async (newLLM: string) => {\n setSelectedLLM(newLLM);\n\n const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings;\n await updateFn(language, {\n llm: newLLM,\n custom_prompt: promptType === 'basic_analysis'\n ? settings.basic_analysis[language].custom_prompt\n : settings.deep_analysis[language].custom_prompt,\n });\n\n\n // API-ключ передаем только для Default LLM\n onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined);\n };\n\n // Сохраняем API ключ\n const handleApiKeyChange = (newApiKey: string) => {\n setApiKey(newApiKey);\n\n if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);\n\n saveTimeoutRef.current = setTimeout(async () => {\n try {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n await APIKeyManager.saveEncryptedKey(keyId, newApiKey);\n onLLMChange(selectedLLM, newApiKey);\n } catch (error) {\n console.error('Failed to save API key:', error);\n }\n }, 500);\n };\n\n // Получаем список опций для селекта\n const getLLMOptions = () => {\n const options = [\n { value: 'default', label: 'Default LLM' },\n ...globalAIKeys.map(key => ({\n value: key.id,\n label: key.name,\n })),\n ];\n return options;\n };\n\n return (\n
\n \n\n handleLLMChange(e.target.value)}\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px',\n marginBottom: '8px'\n }}\n >\n {getLLMOptions().map(option => (\n \n ))}\n \n\n {selectedLLM === 'default' && (\n
\n \n handleApiKeyChange(e.target.value)}\n placeholder=\"Введите API ключ\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n />\n
\n )}\n
\n );\n};\n\nexport default LLMSelector;","import * as React from 'react';\nimport { useTranslations } from './useTranslations';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface AIKey {\n id: string;\n name: string;\n key: string;\n status: 'configured' | 'not_configured' | 'testing';\n isFixed?: boolean;\n isFree?: boolean;\n}\n\nexport const useAIKeys = () => {\n const { t } = useTranslations('ru');\n const [aiKeys, setAiKeys] = React.useState([\n {\n id: 'gemini-flash-lite',\n name: 'Google Gemini Flash Lite - Базовый анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n {\n id: 'gemini-pro',\n name: 'Gemini 2.5 Pro - Глубокий анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n ]);\n const [customKeys, setCustomKeys] = React.useState([]);\n\n // Рефы для отложенного сохранения ключей\n const saveTimeoutsRef = React.useRef>({});\n\n // Load AI keys on mount and cleanup timeouts on unmount\n React.useEffect(() => {\n loadAIKeys();\n\n // Cleanup function\n return () => {\n // Очищаем все активные таймауты\n Object.values(saveTimeoutsRef.current).forEach(timeout => {\n clearTimeout(timeout);\n });\n saveTimeoutsRef.current = {};\n };\n }, []);\n\n const loadAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to load AI keys...');\n\n // Загружаем зашифрованные ключи\n const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro'];\n const fixedKeysPromises = fixedKeyIds.map(async (keyId) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(keyId);\n console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty');\n return { keyId, decryptedKey };\n });\n\n const fixedKeysResults = await Promise.all(fixedKeysPromises);\n console.log('[useAIKeys] Fixed keys results:', fixedKeysResults);\n\n setAiKeys(prev =>\n prev.map(key => {\n const result = fixedKeysResults.find(r => r.keyId === key.id);\n console.log(`[useAIKeys] Setting key ${key.id}:`, result?.decryptedKey ? 'configured' : 'not_configured');\n return {\n ...key,\n key: result?.decryptedKey || '',\n status: (result?.decryptedKey ? 'configured' : 'not_configured') as AIKey['status'],\n };\n }),\n );\n\n // Загружаем пользовательские ключи (метаданные)\n const result = await chrome.storage.local.get(['customKeys']);\n console.log('[useAIKeys] Custom keys metadata:', result.customKeys);\n if (result.customKeys) {\n // Для пользовательских ключей также используем шифрование\n const customKeysWithDecryption = await Promise.all(\n (result.customKeys as AIKey[]).map(async (key: AIKey) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(key.id);\n console.log(`[useAIKeys] Custom key ${key.id}:`, decryptedKey ? 'present' : 'empty');\n return {\n ...key,\n key: decryptedKey || '',\n status: (decryptedKey ? 'configured' : 'not_configured') as AIKey['status']\n };\n })\n );\n console.log('[useAIKeys] Setting custom keys:', customKeysWithDecryption);\n setCustomKeys(customKeysWithDecryption);\n } else {\n console.log('[useAIKeys] No custom keys found');\n setCustomKeys([]);\n }\n\n console.log('[useAIKeys] AI keys loaded successfully');\n } catch (error) {\n console.error('Failed to load AI keys:', error);\n // В случае ошибки шифрования показываем пустые ключи\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n key: '',\n status: 'not_configured' as AIKey['status'],\n })),\n );\n setCustomKeys([]);\n }\n };\n\n const saveAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to save AI keys...');\n console.log('[useAIKeys] Current aiKeys:', aiKeys);\n console.log('[useAIKeys] Current customKeys:', customKeys);\n\n // Сохраняем фиксированные ключи с шифрованием\n const saveFixedKeysPromises = aiKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving fixed key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing fixed key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveFixedKeysPromises);\n\n // Сохраняем пользовательские ключи с шифрованием\n const saveCustomKeysPromises = customKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving custom key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing custom key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveCustomKeysPromises);\n\n // Сохраняем метаданные пользовательских ключей (без самих ключей)\n const customKeysMetadata = customKeys.map(key => ({\n id: key.id,\n name: key.name,\n isFixed: false,\n isFree: false,\n // key и status не сохраняем в метаданных для безопасности\n }));\n\n console.log('[useAIKeys] Saving custom keys metadata:', customKeysMetadata);\n await chrome.storage.local.set({\n customKeys: customKeysMetadata,\n });\n\n // Обновляем статусы в состоянии\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status'],\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status']\n }))\n );\n\n console.log('[useAIKeys] AI keys saved successfully');\n alert(t('options.settings.aiKeys.messages.saved'));\n } catch (error) {\n console.error('Failed to save AI keys:', error);\n alert(t('options.settings.aiKeys.messages.saveError'));\n }\n };\n\n const testAIKeys = async () => {\n // Set testing status\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n // Simulate API testing with timeout\n setTimeout(() => {\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n alert(t('options.settings.aiKeys.messages.testComplete'));\n }, 2000);\n };\n\n const addCustomKey = () => {\n const newKey: AIKey = {\n id: `custom-${Date.now()}`,\n name: `Пользовательский ключ ${customKeys.length + 1}`,\n key: '',\n status: 'not_configured' as const,\n };\n setCustomKeys(prev => [...prev, newKey]);\n };\n\n const removeCustomKey = async (id: string) => {\n try {\n // Удаляем зашифрованный ключ\n await APIKeyManager.removeKey(id);\n\n // Удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n } catch (error) {\n console.error('Failed to remove custom key:', error);\n // Даже если удаление зашифрованного ключа не удалось, удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n }\n };\n\n const updateKey = (id: string, value: string, isCustom = false) => {\n console.log(`[useAIKeys] Updating key ${id} with value:`, value ? 'present' : 'empty');\n\n // Обновляем состояние немедленно для лучшего UX\n if (isCustom) {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n } else {\n setAiKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n }\n\n // Отменяем предыдущий таймаут для этого ключа\n if (saveTimeoutsRef.current[id]) {\n clearTimeout(saveTimeoutsRef.current[id]);\n }\n\n // Устанавливаем новый таймаут для отложенного сохранения\n saveTimeoutsRef.current[id] = setTimeout(async () => {\n try {\n console.log(`[useAIKeys] Auto-saving key ${id} after delay`);\n if (value) {\n await APIKeyManager.saveEncryptedKey(id, value);\n } else {\n await APIKeyManager.removeKey(id);\n }\n\n // Для пользовательских ключей также обновляем метаданные\n if (isCustom) {\n const result = await chrome.storage.local.get(['customKeys']);\n const customKeysMetadata = result.customKeys || [];\n const updatedMetadata = customKeysMetadata.map((key: any) =>\n key.id === id ? { ...key, name: key.name } : key\n );\n\n // Если ключ не существует в метаданных, добавляем его\n const existingKeyIndex = updatedMetadata.findIndex((key: any) => key.id === id);\n if (existingKeyIndex === -1 && value) {\n updatedMetadata.push({\n id,\n name: `Пользовательский ключ ${customKeysMetadata.length + 1}`,\n isFixed: false,\n isFree: false,\n });\n }\n\n await chrome.storage.local.set({\n customKeys: updatedMetadata.filter((key: any) => {\n // Убираем из метаданных ключи, которые были удалены\n return value || key.id !== id;\n }),\n });\n }\n\n console.log(`[useAIKeys] Key ${id} auto-saved successfully`);\n } catch (error) {\n console.error(`[useAIKeys] Failed to auto-save key ${id}:`, error);\n } finally {\n // Убираем таймаут из рефа\n delete saveTimeoutsRef.current[id];\n }\n }, 1000); // 1 секунда задержки перед сохранением\n };\n\n const updateCustomKeyName = (id: string, name: string) => {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n name,\n status: key.key ? 'configured' : 'not_configured'\n } : key)));\n };\n\n // Функция для получения текста статуса с поддержкой локализации\n const getStatusText = (status: string) => {\n switch (status) {\n case 'configured':\n return t('options.settings.aiKeys.status.configured');\n case 'not_configured':\n return t('options.settings.aiKeys.status.notConfigured');\n case 'testing':\n return t('options.settings.aiKeys.status.testing');\n default:\n return t('options.settings.aiKeys.status.notConfigured');\n }\n };\n\n const getStatusClass = (status: string) => {\n switch (status) {\n case 'configured':\n return 'status-configured';\n case 'not_configured':\n return 'status-not-configured';\n case 'testing':\n return 'status-testing';\n default:\n return '';\n }\n };\n\n // Функция для получения API ключа для использования в MCP-серверах\n const getAPIKeyForMCP = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.getDecryptedKey(keyId);\n } catch (error) {\n console.error('Failed to get API key for MCP:', error);\n return null;\n }\n };\n\n // Функция для проверки, настроен ли определенный ключ\n const isKeyConfigured = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.keyExists(keyId);\n } catch (error) {\n console.error('Failed to check if key is configured:', error);\n return false;\n }\n };\n\n return {\n aiKeys,\n customKeys,\n saveAIKeys,\n testAIKeys,\n addCustomKey,\n removeCustomKey,\n updateKey,\n updateCustomKeyName,\n getStatusText,\n getStatusClass,\n getAPIKeyForMCP,\n isKeyConfigured,\n };\n};\n","import { useTranslations } from '../hooks/useTranslations';\nimport type { Plugin } from '../hooks/usePlugins';\nimport type { PluginSettings } from '@extension/storage';\nimport ToggleButton from './ToggleButton';\nimport LocalErrorBoundary from './LocalErrorBoundary';\nimport LLMSelector from './LLMSelector';\nimport { useAIKeys, AIKey } from '../hooks/useAIKeys';\nimport { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings';\nimport { useState, useEffect } from 'react';\nimport type { ReactNode } from 'react';\n\n// Типы для структуры промптов\ninterface PromptData {\n [key: string]: any;\n}\n\ninterface LanguagePrompts {\n ru: PromptData;\n en: PromptData;\n}\n\ninterface PromptsStructure {\n basic_analysis: LanguagePrompts;\n deep_analysis: LanguagePrompts;\n}\n\nconst cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' ');\n\ninterface PluginDetailsProps {\n selectedPlugin: Plugin | null;\n locale?: 'en' | 'ru';\n onUpdateSetting?: (pluginId: string, setting: keyof PluginSettings, value: boolean) => Promise;\n}\n\ninterface CustomSetting {\n type: 'boolean' | 'select' | 'text' | 'number' | 'prompts';\n default: boolean | string | number | PromptsStructure;\n label: string | { ru: string; en: string };\n description?: string | { ru: string; en: string };\n values?: string[];\n labels?: Record;\n min?: number;\n max?: number;\n step?: number;\n}\n\n// Компонент для редактирования промптов\ninterface PromptsEditorProps {\n value: PromptsStructure;\n manifest: any; // manifest.json структура\n disabled: boolean;\n onSave: (value: PromptsStructure) => void;\n locale: 'en' | 'ru';\n t: (key: string) => string; // функция перевода\n globalAIKeys: AIKey[];\n pluginSettings: PluginSettingsType;\n}\n\nconst PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => {\n const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis');\n const [language, setLanguage] = useState<'ru' | 'en'>('ru');\n const [customPrompt, setCustomPrompt] = useState('');\n\n // Получить дефолтную LLM для конкретного промпта и языка\n const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return 'default';\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n const llmConfig = langPrompts.LLM?.default;\n\n if (!llmConfig) return 'default';\n\n // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro\n if (type === 'basic_analysis') {\n return 'gemini-flash-lite';\n } else if (type === 'deep_analysis') {\n return 'gemini-pro';\n }\n\n return 'default';\n } catch {\n return 'default';\n }\n };\n\n // Проверить наличие дефолтной LLM для конкретного промпта и языка\n const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return false;\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n return !!langPrompts.LLM?.default;\n } catch {\n return false;\n }\n };\n\n // Получаем оригинальный промпт из manifest\n const getOriginalPrompt = (): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return '';\n\n const typePrompts = promptsConfig[promptType] || {};\n const langPrompts = typePrompts[language] || {};\n const defaultPrompt = langPrompts.default || '';\n\n // Если defaultPrompt - это объект, преобразуем его\n if (typeof defaultPrompt === 'object') {\n return JSON.stringify(defaultPrompt, null, 2);\n }\n\n return defaultPrompt;\n } catch {\n return '';\n }\n };\n\n // Получаем кастомный промпт\n const getCustomPrompt = (): string => {\n try {\n const prompts = value || {};\n const typePrompts = (prompts as any)[promptType] || {};\n const langPrompts = (typePrompts as any)[language];\n\n // If stored as plain text, show as-is. Only stringify objects.\n if (typeof langPrompts === 'string') return langPrompts;\n if (langPrompts == null) return '';\n return JSON.stringify(langPrompts, null, 2);\n } catch {\n return '';\n }\n };\n\n // Загружаем кастомный промпт при изменении типа или языка\n useEffect(() => {\n setCustomPrompt(getCustomPrompt());\n }, [promptType, language, value]);\n\n const handleCopyToCustom = () => {\n setCustomPrompt(getOriginalPrompt());\n };\n\n const handleSave = () => {\n try {\n const newValue: any = { ...value };\n\n // Ensure container objects exist\n if (!newValue[promptType]) newValue[promptType] = { ru: '', en: '' };\n\n // Store verbatim text (plain string), no JSON requirement\n newValue[promptType][language] = customPrompt;\n\n onSave(newValue);\n } catch (error) {\n console.error('Failed to save custom prompt:', error);\n // Можно добавить уведомление об ошибке\n }\n };\n\n return (\n
\n
\n \n\n {/* Переключатели */}\n
\n
\n \n setPromptType(e.target.value as 'basic_analysis' | 'deep_analysis')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n\n
\n \n setLanguage(e.target.value as 'ru' | 'en')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n
\n\n {/* LLM Selector для текущего промпта и языка */}\n {\n console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey);\n // Сохраняем выбранную LLM в pluginSettings для передачи в mcp_server.py\n const updatedPluginSettings = { ...pluginSettings } as any;\n if (!updatedPluginSettings.selected_llms) {\n updatedPluginSettings.selected_llms = {};\n }\n if (!updatedPluginSettings.selected_llms[promptType]) {\n updatedPluginSettings.selected_llms[promptType] = {};\n }\n // Сохраняем только выбранную LLM (без api_key, так как он уже сохранен через APIKeyManager)\n updatedPluginSettings.selected_llms[promptType][language] = llm;\n // Обновляем pluginSettings через глобальный объект\n if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n }\n }}\n />\n\n {/* Textarea */}\n
\n
\n \n \n
\n\n
\n \n {t('options.plugins.prompts.copyToCustom')}\n \n
\n\n
\n \n setCustomPrompt(e.target.value)}\n disabled={disabled}\n style={{\n width: '100%',\n height: '300px',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n backgroundColor: 'white',\n fontSize: '12px',\n fontFamily: 'monospace',\n resize: 'vertical'\n }}\n />\n
\n
\n\n {/* Кнопка сохранения */}\n
\n \n {t('options.plugins.prompts.save')}\n \n
\n
\n
\n );\n};\n\nconst PluginDetails = (props: PluginDetailsProps) => {\n const { selectedPlugin, locale = 'en', onUpdateSetting } = props;\n const { t } = useTranslations(locale);\n const { aiKeys } = useAIKeys();\n const { settings: pluginSettings } = usePluginSettings();\n const [isUpdating, setIsUpdating] = useState(null);\n const [customSettings, setCustomSettings] = useState | null>(null);\n\n // Хелперы для работы с локализацией\n const getLocalizedText = (text: string | { ru: string; en: string } | undefined): string => {\n if (!text) return '';\n if (typeof text === 'string') return text;\n return text[locale] || text.ru || text.en || '';\n };\n\n // Загружаем пользовательские настройки при выборе плагина\n useEffect(() => {\n if (selectedPlugin?.manifest?.options) {\n loadCustomSettings();\n }\n }, [selectedPlugin?.id]);\n\n // Передаем выбранные LLM в mcp_server.py через pyodide.globals\n useEffect(() => {\n if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n // Создаем структуру selected_llms из pluginSettings\n const selected_llms = {\n basic_analysis: {\n ru: pluginSettings.basic_analysis?.ru?.llm || 'default',\n en: pluginSettings.basic_analysis?.en?.llm || 'default',\n },\n deep_analysis: {\n ru: pluginSettings.deep_analysis?.ru?.llm || 'default',\n en: pluginSettings.deep_analysis?.en?.llm || 'default',\n }\n };\n \n // Обновляем pluginSettings в pyodide.globals\n const updatedPluginSettings = {\n ...(window as any).pyodide.globals.pluginSettings || {},\n selected_llms: selected_llms\n };\n \n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings);\n }\n }, [pluginSettings]);\n\n if (!selectedPlugin || typeof selectedPlugin !== 'object') {\n return (\n
\n

{t('options_plugins_details_title')}

\n

{t('options.plugins.details.selectPlugin')}

\n
\n );\n }\n\n const settings = selectedPlugin.settings || { enabled: true, autorun: false };\n const hostPermissions = selectedPlugin.manifest?.host_permissions || [];\n\n // Функции для работы с chrome.storage.local\n const loadCustomSettings = async () => {\n if (!selectedPlugin || !selectedPlugin.manifest?.options || customSettings !== null) return;\n\n try {\n // Проверяем доступность chrome.storage\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const optionKeys = Object.keys(selectedPlugin.manifest.options);\n const keys = optionKeys.map(key => `${selectedPlugin.id}_${key}`);\n\n const result = await chrome.storage.local.get(keys);\n const loadedSettings: Record = {};\n\n // Преобразуем ключи обратно и применяем значения\n Object.entries(result).forEach(([key, value]) => {\n const settingName = key.replace(`${selectedPlugin.id}_`, '');\n if (value !== undefined) {\n loadedSettings[settingName] = value;\n }\n });\n\n setCustomSettings(loadedSettings);\n }\n } catch (error) {\n console.warn('Failed to load custom settings from chrome.storage.local:', error);\n // Fallback: используем дефолтные значения из manifest\n }\n };\n\n const saveCustomSetting = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n try {\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const key = `${selectedPlugin.id}_${setting}`;\n await chrome.storage.local.set({ [key]: value });\n\n // Обновляем локальное состояние\n setCustomSettings(prev => ({\n ...prev,\n [setting]: value\n }));\n\n // Диагностика: проверяем правильность сохранения промптов\n if (setting === 'prompts') {\n await verifyPromptStorage();\n }\n } else {\n console.warn('chrome.storage.local is not available');\n }\n } catch (error) {\n console.error(`Failed to save setting ${setting} to chrome.storage.local:`, error);\n throw error; // Пробрасываем ошибку для обработки в handleSettingChange\n }\n };\n\n // Диагностическая функция для проверки сохранения промптов\n const verifyPromptStorage = async () => {\n if (!selectedPlugin) return;\n\n try {\n const key = `${selectedPlugin.id}_prompts`;\n const stored = await chrome.storage.local.get([key]);\n console.log('🔍 Диагностика промптов:');\n console.log(` Plugin ID: ${selectedPlugin.id}`);\n console.log(` Storage key: ${key}`);\n console.log(' Сохраненные промпты:', stored[key]);\n\n if (stored[key]) {\n const prompts = stored[key] as PromptsStructure;\n console.log(' Структура промптов:');\n console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`);\n console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`);\n console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`);\n console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`);\n }\n } catch (error) {\n console.error('Ошибка диагностики промптов:', error);\n }\n };\n\n\n // Хелпер для получения значения настройки с приоритетом: chrome.storage -> manifest\n const getCustomSettingValue = (settingName: string, defaultValue: boolean | string | number | PromptsStructure): boolean | string | number | PromptsStructure => {\n if (customSettings && customSettings[settingName] !== undefined) {\n return customSettings[settingName];\n }\n\n // Специальная обработка для промптов: преобразуем структуру из manifest в PromptsStructure\n if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) {\n const promptsConfig = defaultValue as any;\n const result: PromptsStructure = {\n basic_analysis: { ru: {}, en: {} },\n deep_analysis: { ru: {}, en: {} }\n };\n\n // Извлекаем default значения из структуры manifest\n if (promptsConfig.basic_analysis?.ru?.default) {\n result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default;\n }\n if (promptsConfig.basic_analysis?.en?.default) {\n result.basic_analysis.en = promptsConfig.basic_analysis.en.default;\n }\n if (promptsConfig.deep_analysis?.ru?.default) {\n result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default;\n }\n if (promptsConfig.deep_analysis?.en?.default) {\n result.deep_analysis.en = promptsConfig.deep_analysis.en.default;\n }\n\n return result;\n }\n\n return defaultValue;\n };\n\n const renderCustomSetting = (key: string, config: CustomSetting): ReactNode | null => {\n const value = getCustomSettingValue(key, config.default);\n const disabled = isUpdating === key || !(settings.enabled ?? true);\n const localizedLabel = getLocalizedText(config.label);\n const localizedDescription = getLocalizedText(config.description);\n\n // Специальная обработка для промптов\n if (key === 'prompts') {\n return (\n
\n handleSettingChange(key, newValue)}\n locale={locale}\n t={t}\n globalAIKeys={aiKeys}\n pluginSettings={pluginSettings}\n />\n
\n );\n }\n\n if (config.type === 'boolean') {\n return (\n
\n handleSettingChange(key, val)}\n label={\n <>\n {localizedLabel}\n {localizedDescription && (\n \n i\n \n )}\n \n }\n />\n
\n );\n }\n\n if (config.type === 'select') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'text') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'number') {\n const handleNumberChange = (e: React.ChangeEvent) => {\n const numValue = parseFloat(e.target.value);\n if (isNaN(numValue)) return;\n\n // Валидация диапазона\n let validatedValue = numValue;\n if (config.min !== undefined && validatedValue < config.min) {\n validatedValue = config.min;\n }\n if (config.max !== undefined && validatedValue > config.max) {\n validatedValue = config.max;\n }\n\n handleSettingChange(key, validatedValue);\n };\n\n return (\n
\n \n
\n );\n }\n\n return null;\n };\n\n const handleSettingChange = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n // Проверяем, является ли настройка пользовательской\n const options = selectedPlugin.manifest?.options;\n if (options && options[setting as keyof typeof options]) {\n // Ленивая загрузка: загружаем настройки только при первом взаимодействии\n if (customSettings === null) {\n await loadCustomSettings();\n }\n\n try {\n setIsUpdating(setting);\n await saveCustomSetting(setting, value);\n } catch (error) {\n console.error(`Failed to update custom setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n } else {\n // Стандартные настройки (enabled/autorun) используем через callback\n if (onUpdateSetting) {\n try {\n setIsUpdating(setting);\n await onUpdateSetting(selectedPlugin.id, setting as keyof PluginSettings, value as boolean);\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n }\n }\n };\n\n // Precompute custom settings elements to satisfy TypeScript\n const optionEntries = Object.entries(selectedPlugin.manifest?.options ?? {}) as [string, CustomSetting][];\n const customSettingElements: ReactNode[] = optionEntries\n .map(([key, config]) => renderCustomSetting(key, config))\n .filter((item): item is ReactNode => item !== null);\n\n // Render helper to avoid union/unknown in JSX for plugin settings section\n const PluginSettingsSection = () => (\n
\n

Настройки плагина

\n
\n handleSettingChange('enabled', val)}\n label={\n <>\n Включен\n \n i\n \n \n }\n />\n
\n
\n handleSettingChange('autorun', val)}\n label={\n <>\n Автоматический запуск\n \n i\n \n \n }\n />\n
\n
\n );\n\n return (\n \n
\n

{selectedPlugin.name}

\n
\n
\n
\n

\n Версия: v{selectedPlugin.version}\n

\n

\n Статус:\n \n {settings.enabled ? 'Активен' : 'Неактивен'}\n \n

\n

\n Автор: {selectedPlugin.manifest?.author || 'Не указан'}\n

\n

\n Последнее обновление: {selectedPlugin.manifest?.last_updated || 'Неизвестно'}\n

\n
\n\n
\n

Описание

\n

{selectedPlugin.description}

\n
\n\n {/* Сайты/домены, на которых работает плагин */}\n {hostPermissions.length > 0 && (\n
\n

Сайты/домены

\n
    \n {hostPermissions.map((host: string, idx: number) => (\n
  • {host}
  • \n ))}\n
\n
\n )}\n\n {Array.isArray(selectedPlugin.manifest?.permissions) ? (\n
\n

Разрешения

\n
    \n {(selectedPlugin.manifest?.permissions ?? []).map((permission: string, idx: number) => (\n
  • {permission}
  • \n ))}\n
\n
\n ) : null}\n\n \n\n {/* Пользовательские настройки */}\n {(selectedPlugin.manifest?.options && Object.keys(selectedPlugin.manifest.options).length > 0) ? (\n
\n

Дополнительные настройки

\n {customSettingElements}\n
\n ) : null}\n\n\n
\n
\n
\n );\n};\n\nexport { PluginDetails };\n\nexport default PluginDetails;\n","import '../../../options/src/Options.css'; // Импорт стилей из options для идентичности\nimport type React from 'react';\nimport OptionsPluginDetails from '../../../options/src/components/PluginDetails';\nimport type { PluginSettings } from '@extension/storage';\n\ninterface PluginDetailsProps {\n plugin: Plugin;\n onUpdateSetting?: (pluginId: string, setting: string, value: boolean) => Promise;\n}\n\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\nexport const PluginDetails: React.FC = ({ plugin, onUpdateSetting }) => {\n // Адаптируем onUpdateSetting для совместимости с OptionsPluginDetails\n const adaptedOnUpdateSetting = onUpdateSetting ? async (pluginId: string, setting: keyof PluginSettings, value: boolean): Promise => {\n try {\n await onUpdateSetting(pluginId, setting as string, value);\n return true; // Успешно\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n return false; // Неудача\n }\n } : undefined;\n\n const adaptedProps = {\n selectedPlugin: {\n ...plugin,\n description: plugin.description || 'Описание не указано',\n icon: plugin.icon || '',\n manifest: plugin.manifest || {} as any\n },\n locale: 'ru' as const,\n onUpdateSetting: adaptedOnUpdateSetting\n };\n\n return ;\n};\n","import type { ExcludeValuesFromBaseArrayType } from './types.js';\n\nexport const excludeValuesFromBaseArray = (\n baseArray: B,\n excludeArray: E,\n) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType;\n\nexport const sleep = async (time: number) => new Promise(r => setTimeout(r, time));\n\n/**\n * Унифицированное получение pageKey из URL страницы.\n * Удаляет search/hash, возвращает нормализованный URL или 'unknown-page'.\n */\nexport const getPageKey = function (currentTabUrl: string | null): string {\n if (!currentTabUrl) return 'unknown-page';\n try {\n const url = new URL(currentTabUrl);\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return currentTabUrl;\n }\n};\n","import { useState, useEffect, useRef, useCallback } from 'react';\n\ninterface UseLazyChatSyncOptions {\n pluginId: string;\n pageKey: string;\n debounceMs?: number; // Задержка перед синхронизацией (по умолчанию 1000ms)\n}\n\ninterface UseLazyChatSyncReturn {\n message: string;\n setMessage: (text: string) => void;\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n loadDraft: () => Promise;\n clearDraft: () => Promise;\n draftText: string;\n}\n\nexport const useLazyChatSync = ({\n pluginId,\n pageKey,\n debounceMs = 1000,\n}: UseLazyChatSyncOptions): UseLazyChatSyncReturn => {\n const [message, setMessageState] = useState('');\n const [isDraftSaved, setIsDraftSaved] = useState(false);\n const [isDraftLoading, setIsDraftLoading] = useState(false);\n const [draftError, setDraftError] = useState(null);\n const [draftText, setDraftText] = useState('');\n\n const debounceRef = useRef(null);\n const lastSavedText = useRef('');\n\n // Функция для сохранения черновика\n const saveDraft = useCallback(\n async (text: string) => {\n if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился\n console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: text,\n });\n lastSavedText.current = text;\n setIsDraftSaved(true);\n setDraftError(null);\n setDraftText(text);\n console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text });\n } catch (error) {\n console.error('[useLazyChatSync] Error saving draft:', error);\n setDraftError('Ошибка сохранения черновика');\n setIsDraftSaved(false);\n }\n },\n [pluginId, pageKey],\n );\n\n // Функция для загрузки черновика\n const loadDraft = useCallback(async () => {\n setIsDraftLoading(true);\n setDraftError(null);\n console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey });\n try {\n const response = await chrome.runtime.sendMessage({\n type: 'GET_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n });\n if (response?.draftText) {\n setMessageState(response.draftText);\n lastSavedText.current = response.draftText;\n setIsDraftSaved(true);\n setDraftText(response.draftText);\n console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText });\n } else {\n setMessageState('');\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftText('');\n console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey });\n }\n } catch (error) {\n console.error('[useLazyChatSync] Error loading draft:', error);\n setDraftError('Ошибка загрузки черновика');\n } finally {\n setIsDraftLoading(false);\n }\n }, [pluginId, pageKey]);\n\n // Функция для очистки черновика\n const clearDraft = useCallback(async () => {\n console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: '',\n });\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftError(null);\n setDraftText('');\n setMessageState('');\n console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey });\n } catch (error) {\n console.error('[useLazyChatSync] Error clearing draft:', error);\n setDraftError('Ошибка очистки черновика');\n }\n }, [pluginId, pageKey]);\n\n // Обновленная функция setMessage с ленивой синхронизацией\n const setMessage = useCallback(\n (text: string) => {\n setMessageState(text);\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (text.length === 0) {\n clearDraft();\n } else {\n debounceRef.current = setTimeout(() => {\n saveDraft(text);\n }, debounceMs);\n }\n console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text });\n },\n [debounceMs, saveDraft, clearDraft, pluginId, pageKey],\n );\n\n // Очистка таймера при размонтировании\n useEffect(\n () => () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n },\n [],\n );\n\n // Автоматическая загрузка черновика при монтировании\n useEffect(() => {\n loadDraft();\n }, [loadDraft]);\n\n // При смене pageKey всегда загружаем draft, не сбрасываем message вручную\n useEffect(() => {\n console.log('[useLazyChatSync] pageKey изменился:', pageKey);\n setIsDraftSaved(false);\n setIsDraftLoading(false);\n setDraftError(null);\n lastSavedText.current = '';\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n loadDraft();\n }, [pageKey, loadDraft]);\n\n return {\n message,\n setMessage,\n isDraftSaved,\n isDraftLoading,\n draftError,\n loadDraft,\n clearDraft,\n draftText,\n };\n};\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","/**\n * Storage area type for persisting and exchanging data.\n * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview\n */\nexport var StorageEnum;\n(function (StorageEnum) {\n /**\n * Persist data locally against browser restarts. Will be deleted by uninstalling the extension.\n * @default\n */\n StorageEnum[\"Local\"] = \"local\";\n /**\n * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply.\n */\n StorageEnum[\"Sync\"] = \"sync\";\n /**\n * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a\n * json schema for company wide config.\n */\n StorageEnum[\"Managed\"] = \"managed\";\n /**\n * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and\n * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access.\n * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session)\n */\n StorageEnum[\"Session\"] = \"session\";\n})(StorageEnum || (StorageEnum = {}));\n/**\n * Global access level requirement for the {@link StorageEnum.Session} Storage Area.\n * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel)\n */\nexport var SessionAccessLevelEnum;\n(function (SessionAccessLevelEnum) {\n /**\n * Storage can only be accessed by Extension pages (not Content scripts).\n * @default\n */\n SessionAccessLevelEnum[\"ExtensionPagesOnly\"] = \"TRUSTED_CONTEXTS\";\n /**\n * Storage can be accessed by both Extension pages and Content scripts.\n */\n SessionAccessLevelEnum[\"ExtensionPagesAndContentScripts\"] = \"TRUSTED_AND_UNTRUSTED_CONTEXTS\";\n})(SessionAccessLevelEnum || (SessionAccessLevelEnum = {}));\n","import { SessionAccessLevelEnum, StorageEnum } from './enums.js';\n/**\n * Chrome reference error while running `processTailwindFeatures` in tailwindcss.\n * To avoid this, we need to check if globalThis.chrome is available and add fallback logic.\n */\nconst chrome = globalThis.chrome;\n/**\n * Sets or updates an arbitrary cache with a new value or the result of an update function.\n */\nconst updateCache = async (valueOrUpdate, cache) => {\n // Type guard to check if our value or update is a function\n const isFunction = (value) => typeof value === 'function';\n // Type guard to check in case of a function if it's a Promise\n const returnsPromise = (func) => \n // Use ReturnType to infer the return type of the function and check if it's a Promise\n func instanceof Promise;\n if (isFunction(valueOrUpdate)) {\n // Check if the function returns a Promise\n if (returnsPromise(valueOrUpdate)) {\n return valueOrUpdate(cache);\n }\n else {\n return valueOrUpdate(cache);\n }\n }\n else {\n return valueOrUpdate;\n }\n};\n/**\n * If one session storage needs access from content scripts, we need to enable it globally.\n * @default false\n */\nlet globalSessionAccessLevelFlag = false;\n/**\n * Checks if the storage permission is granted in the manifest.json.\n */\nconst checkStoragePermission = (storageEnum) => {\n if (!chrome) {\n return;\n }\n if (!chrome.storage[storageEnum]) {\n throw new Error(`\"storage\" permission in manifest.ts: \"storage ${storageEnum}\" isn't defined`);\n }\n};\n/**\n * Creates a storage area for persisting and exchanging data.\n */\nexport const createStorage = (key, fallback, config) => {\n let cache = null;\n let initialCache = false;\n let listeners = [];\n const storageEnum = config?.storageEnum ?? StorageEnum.Local;\n const liveUpdate = config?.liveUpdate ?? false;\n const serialize = config?.serialization?.serialize ?? ((v) => v);\n const deserialize = config?.serialization?.deserialize ?? (v => v);\n // Set global session storage access level for StoryType.Session, only when not already done but needed.\n if (globalSessionAccessLevelFlag === false &&\n storageEnum === StorageEnum.Session &&\n config?.sessionAccessForContentScripts === true) {\n checkStoragePermission(storageEnum);\n chrome?.storage[storageEnum]\n .setAccessLevel({\n accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts,\n })\n .catch(error => {\n console.error(error);\n console.error('Please call .setAccessLevel() into different context, like a background script.');\n });\n globalSessionAccessLevelFlag = true;\n }\n // Register life cycle methods\n const get = async () => {\n checkStoragePermission(storageEnum);\n const value = await chrome?.storage[storageEnum].get([key]);\n if (!value) {\n return fallback;\n }\n return deserialize(value[key]) ?? fallback;\n };\n const set = async (valueOrUpdate) => {\n if (!initialCache) {\n cache = await get();\n }\n cache = await updateCache(valueOrUpdate, cache);\n await chrome?.storage[storageEnum].set({ [key]: serialize(cache) });\n _emitChange();\n };\n const subscribe = (listener) => {\n listeners = [...listeners, listener];\n return () => {\n listeners = listeners.filter(l => l !== listener);\n };\n };\n const getSnapshot = () => cache;\n const _emitChange = () => {\n listeners.forEach(listener => listener());\n };\n // Listener for live updates from the browser\n const _updateFromStorageOnChanged = async (changes) => {\n // Check if the key we are listening for is in the changes object\n if (changes[key] === undefined)\n return;\n const valueOrUpdate = deserialize(changes[key].newValue);\n if (cache === valueOrUpdate)\n return;\n cache = await updateCache(valueOrUpdate, cache);\n _emitChange();\n };\n get().then(data => {\n cache = data;\n initialCache = true;\n _emitChange();\n });\n // Register listener for live updates for our storage area\n if (liveUpdate) {\n chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);\n }\n return {\n get,\n set,\n getSnapshot,\n subscribe,\n };\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('theme-storage-key', {\n theme: 'system',\n isLight: getSystemTheme(),\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\n// Функция для определения системной темы\nfunction getSystemTheme() {\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: light)').matches;\n }\n return true; // По умолчанию светлая тема\n}\nexport const exampleThemeStorage = {\n ...storage,\n toggle: async () => {\n await storage.set(currentState => {\n let newTheme;\n switch (currentState.theme) {\n case 'light':\n newTheme = 'dark';\n break;\n case 'dark':\n newTheme = 'system';\n break;\n case 'system':\n default:\n newTheme = 'light';\n break;\n }\n const isLight = newTheme === 'system' ? getSystemTheme() : newTheme === 'light';\n return {\n theme: newTheme,\n isLight,\n };\n });\n },\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('chat-alignment-storage-key', {\n alignment: 'left',\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\nexport const exampleChatAlignmentStorage = {\n ...storage,\n setAlignment: async (alignment) => {\n await storage.set({ alignment });\n },\n getAlignment: async () => {\n const state = await storage.get();\n return state.alignment;\n },\n};\n","/**\n * PluginControlPanel.tsx - Панель управления плагином с чатом\n *\n * ИСПРАВЛЕНИЕ ПРОБЛЕМЫ С ПОЛУЧЕНИЕМ ОТВЕТА GET_PLUGIN_CHAT:\n * ========================================================\n *\n * Проблема: Background отправлял ответ через sendResponse() callback, но компонент\n * ожидал ответ через chrome.runtime.sendMessage() с типом 'GET_PLUGIN_CHAT_RESPONSE'.\n *\n * Решение: Изменен механизм коммуникации на использование Promise-based подхода\n * с помощью sendMessageToBackgroundAsync(), который правильно работает с sendResponse().\n *\n * Теперь:\n * 1. Компонент отправляет GET_PLUGIN_CHAT через chrome.runtime.sendMessage()\n * 2. Background получает сообщение и отвечает через sendResponse()\n * 3. Компонент получает ответ через Promise и обрабатывает его\n *\n * Диагностика:\n * - Логи sendMessageToBackgroundAsync покажут отправку и получение ответа\n * - Логи loadChat покажут обработку ответа\n * - Логи processChatResponse покажут разбор данных чата\n */\n\nimport { DraftStatus } from './DraftStatus';\nimport { PluginDetails } from './PluginDetails';\nimport { getPageKey } from '../../../../packages/shared/lib/utils/helpers';\nimport { useLazyChatSync } from '../hooks/useLazyChatSync';\nimport { saveAs } from 'file-saver';\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport './PluginControlPanel.css';\nimport type React from 'react';\nimport { exampleChatAlignmentStorage, type ChatAlignment } from '@extension/storage';\n\n// Определение типа Plugin для PluginControlPanel\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Новый тип для сообщений чата\ninterface ChatMessage {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: number;\n}\n\ninterface PluginControlPanelProps {\n plugin: Plugin;\n currentView: PanelView;\n isRunning: boolean;\n isPaused: boolean;\n currentTabUrl: string | null;\n onStart: () => void;\n onPause: () => void;\n onStop: () => void;\n onClose: () => void;\n}\n\nexport type PanelView = 'chat' | 'details';\n\nexport const PluginControlPanel: React.FC = ({\n plugin,\n currentView,\n isRunning,\n isPaused,\n currentTabUrl,\n onStart,\n onPause,\n onStop,\n onClose,\n}) => {\n // Состояние для активной вкладки в панели управления\n const [activeTab, setActiveTab] = useState('chat');\n // Состояние для текущего pageKey с динамическим обновлением\n const [currentPageKey, setCurrentPageKey] = useState(getPageKey(currentTabUrl));\n\n const [chatTextAlign, setChatTextAlign] = useState('left');\n\n // useEffect для обновления pageKey при изменении currentTabUrl\n useEffect(() => {\n const newPageKey = getPageKey(currentTabUrl);\n console.log('[PluginControlPanel] currentTabUrl изменился:', {\n oldPageKey: currentPageKey,\n newPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n setCurrentPageKey(newPageKey);\n }, [currentTabUrl]);\n\n useEffect(() => {\n const loadAlignment = async () => {\n const alignment = await exampleChatAlignmentStorage.getAlignment();\n setChatTextAlign(alignment);\n };\n\n loadAlignment();\n\n const unsubscribe = exampleChatAlignmentStorage.subscribe(() => {\n loadAlignment();\n });\n\n return unsubscribe;\n }, []);\n // Используем хук для ленивой синхронизации\n const { message, setMessage, isDraftSaved, isDraftLoading, draftError, loadDraft, clearDraft, draftText } =\n useLazyChatSync({\n pluginId: plugin.id,\n pageKey: currentPageKey, // <-- Теперь динамический pageKey\n debounceMs: 1000, // 1 секунда задержки\n });\n\n const [messages, setMessages] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n const [inputHeight, setInputHeight] = useState(60); // Начальная высота поля ввода\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef(null);\n const textareaRef = useRef(null);\n\n useEffect(() => {\n if (!isRunning) {\n // Удалить все вызовы setStopped(...)\n }\n }, [isRunning]);\n\n const handleStart = () => {\n // Удалить все вызовы setStopped(...)\n onStart();\n };\n\n const pluginName =\n plugin.name || (typeof plugin.manifest?.name === 'string' ? plugin.manifest.name : '') || plugin.id;\n\n // Получаем ключ чата для текущего плагина и страницы\n const pluginId = plugin.id;\n\n // Вспомогательная функция для отправки сообщений в background с ожиданием ответа\n const sendMessageToBackgroundAsync = useCallback(async (message: any): Promise => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n try {\n const response = await chrome.runtime.sendMessage(messageWithId);\n return response;\n } catch (error) {\n console.error('[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:', error);\n throw error;\n }\n }, []);\n\n // Вспомогательная функция для отправки сообщений в background без ожидания ответа (для обратной совместимости)\n const sendMessageToBackground = useCallback((message: any): void => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n chrome.runtime.sendMessage(messageWithId);\n }, []);\n\n // Функция для тестирования обработки сообщений с проблемными данными\n const testMessageProcessing = useCallback(() => {\n console.log('[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными');\n\n // Тест 1: Сообщение с объектом вместо строки в text\n const testMessageWithObject = {\n messages: [{\n id: 'test_obj_1',\n text: { content: 'Это объект вместо строки', type: 'object' }, // Объект вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 2: Сообщение с null в text\n const testMessageWithNull = {\n messages: [{\n id: 'test_null_1',\n text: null, // null вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 3: Сообщение с undefined в text\n const testMessageWithUndefined = {\n messages: [{\n id: 'test_undef_1',\n text: undefined, // undefined вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Объявляем processChatResponse локально для избежания проблем с temporal dead zone\n const localProcessChatResponse = (response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(msg.timestamp || Date.now() + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: msg.timestamp || Date.now(),\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n };\n\n try {\n console.log('[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text');\n localProcessChatResponse(testMessageWithObject);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 1 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 2: Обработка сообщения с null в text');\n localProcessChatResponse(testMessageWithNull);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 2 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text');\n localProcessChatResponse(testMessageWithUndefined);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 3 провалился:', error);\n }\n\n console.log('[PluginControlPanel] ✅ Тестирование обработки сообщений завершено');\n }, []); // Убрали processChatResponse из зависимостей\n\n // Вспомогательная функция для обработки ответа чата\n const processChatResponse = useCallback((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n\n console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${index}:`, textContent);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n\n console.log(`[PluginControlPanel] TextContent after JSON parse for message ${index}:`, textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n console.log(`[PluginControlPanel] Final converted text for message ${index}:`, convertedMsg.text);\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n }, []);\n\n // Загрузка истории чата при монтировании или смене плагина/страницы\n const loadChat = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n console.log('[PluginControlPanel] ===== НАЧАЛО loadChat =====', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString(),\n isRunning,\n isPaused\n });\n\n try {\n console.log('[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT');\n const response = await sendMessageToBackgroundAsync({\n type: 'GET_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n console.log('[PluginControlPanel] loadChat - получен ответ от background:', response);\n console.log('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:', {\n loading,\n messagesCount: messages.length,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку при получении ответа\n console.log('[PluginControlPanel] loadChat - loading сброшен в false');\n\n if (response?.error) {\n console.error('[PluginControlPanel] loadChat - ошибка в ответе:', response.error);\n setError(`Ошибка загрузки чата: ${response.error}`);\n setMessages([]);\n } else {\n console.log('[PluginControlPanel] loadChat - обрабатываем успешный ответ');\n // Используем анонимную функцию вместо прямого вызова processChatResponse\n // чтобы избежать проблемы с temporal dead zone\n ((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n \n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n \n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распаршен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n }\n \n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n })(response);\n console.log('[PluginControlPanel] loadChat: чат успешно загружен');\n }\n\n } catch (error) {\n console.error('[PluginControlPanel] loadChat - ошибка при получении ответа:', error);\n console.error('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:', {\n loading,\n messagesCount: messages.length,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString()\n });\n\n // Детальное логирование ошибки с трассировкой стека\n console.error('[PluginControlPanel] loadChat - ERROR DETAILS:', {\n error,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : 'No stack trace',\n errorName: error instanceof Error ? error.name : 'Unknown error type',\n timestamp: new Date().toISOString(),\n pluginId,\n pageKey: currentPageKey\n });\n\n setLoading(false);\n console.log('[PluginControlPanel] loadChat - loading сброшен в false в catch блоке');\n\n // Улучшенная обработка ошибок с проверкой типа\n let errorMessage = 'Ошибка связи с background';\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Специальная обработка для TypeError с substring\n if (error.name === 'TypeError' && error.message.includes('substring')) {\n errorMessage += ' (ошибка обработки текста - проверьте тип данных)';\n console.error('[PluginControlPanel] CRITICAL: substring error detected:', {\n originalError: error,\n stack: error.stack,\n context: { pluginId, pageKey: currentPageKey }\n });\n }\n } else {\n errorMessage += `: ${String(error)}`;\n }\n\n setError(errorMessage);\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====');\n }, [pluginId, currentPageKey, sendMessageToBackgroundAsync, currentTabUrl, isRunning, isPaused]);\n\n // Добавить useEffect для вызова loadChat при монтировании и смене pluginId/pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n\n // Вызываем асинхронную функцию без await, так как useEffect не может быть async\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:', error);\n });\n }, [loadChat]);\n\n // useEffect для перезагрузки чата при смене pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик');\n // Перезагружаем чат для новой страницы\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] Ошибка при перезагрузке чата:', error);\n });\n // Перезагружаем черновик для новой страницы\n loadDraft();\n }, [currentPageKey, loadChat, loadDraft]);\n\n // Событийная синхронизация чата между вкладками и обработка результатов сохранения сообщений\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений', {\n pluginId,\n pageKey: currentPageKey,\n timestamp: new Date().toISOString()\n });\n\n const handleChatUpdate = (event: { type: string; pluginId: string; pageKey: string; messages?: ChatMessage[]; messageId?: string; response?: any; success?: boolean; error?: string; message?: any; timestamp?: number }) => {\n console.log('[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====', {\n type: event?.type,\n pluginId: event?.pluginId,\n pageKey: event?.pageKey,\n messageId: event?.messageId,\n hasResponse: !!event?.response,\n responseType: event?.response ? typeof event.response : 'none',\n timestamp: new Date().toISOString()\n });\n\n // Обработка обновлений чата от других компонентов/вкладок\n if (event?.type === 'PLUGIN_CHAT_UPDATED' && event.pluginId === pluginId && event.pageKey === currentPageKey) {\n console.log('[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные');\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:', {\n loading,\n messagesCount: messages.length,\n currentPageKey,\n pluginId,\n timestamp: new Date().toISOString()\n });\n setLoading(false); // Гарантированный сброс loading перед загрузкой чата\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat');\n // Запрашиваем актуальные данные чата асинхронно\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:', error);\n });\n }\n\n // NOTE: GET_PLUGIN_CHAT_RESPONSE больше не обрабатывается здесь,\n // поскольку ответы на GET_PLUGIN_CHAT теперь обрабатываются через Promise в loadChat()\n\n // Логируем все сообщения, которые приходят, но не обрабатываются\n if (event?.type !== 'PLUGIN_CHAT_UPDATED' && event?.type !== 'GET_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:', {\n type: event?.type,\n fullEvent: event,\n timestamp: new Date().toISOString()\n });\n }\n\n // Обработка результатов сохранения сообщений\n if (event?.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:', event);\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения', event.error);\n setError(`Ошибка сохранения сообщения: ${event.error}`);\n }\n }\n\n // Обработка результатов удаления чата\n if (event?.type === 'DELETE_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат удаления чата:', event);\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:', {\n loading,\n messagesCount: messages.length,\n eventSuccess: event.success,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE');\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: чат успешно удален');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка удаления чата', event.error);\n setError(`Ошибка удаления чата: ${event.error}`);\n }\n }\n\n // === PYODIDE MESSAGE HANDLER ===\n if (event?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', event.message);\n\n if (event.message?.content) {\n try {\n // Строгая проверка типа content для Pyodide сообщений\n let content = event.message.content;\n let messageTimestamp = event.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] PYODIDE content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] PYODIDE content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из PYODIDE content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: event.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:', pyodideError, event);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:', event.message);\n }\n }\n };\n\n // Слушатель для обработки результатов операций с чатом\n const handleChatOperationResult = (message: any) => {\n if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения', message);\n\n if (message.success) {\n console.log('[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено');\n // Не нужно ничего делать дополнительно - обновление придет через PLUGIN_CHAT_UPDATED\n } else {\n console.error('[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения', message.error);\n setError(`Ошибка сохранения сообщения: ${message.error}`);\n }\n }\n };\n\n chrome.runtime.onMessage.addListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы');\n\n return () => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений');\n chrome.runtime.onMessage.removeListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n };\n }, [pluginId, currentPageKey, sendMessageToBackground, loadChat]);\n\n // Восстановление черновика при возврате на вкладку 'Чат'\n useEffect(() => {\n if (currentView === 'chat') {\n loadDraft(); // Явно загружаем черновик при возврате на вкладку чата\n }\n }, [currentView, loadDraft]);\n\n // Логирование каждого рендера и ключевых параметров\n useEffect(() => {\n console.log('[PluginControlPanel] === РЕНДЕР ===', {\n pluginId,\n pageKey: currentPageKey,\n draftText,\n message,\n currentView,\n isRunning,\n isPaused,\n });\n });\n\n // Глобальный обработчик ошибок для ловли проблем с substring\n useEffect(() => {\n const originalConsoleError = console.error;\n console.error = (...args) => {\n // Перехватываем ошибки substring\n const errorMessage = args.join(' ');\n if (errorMessage.includes('substring') && errorMessage.includes('is not a function')) {\n console.error('[PluginControlPanel] 🔴 CRITICAL: substring error detected!');\n console.error('[PluginControlPanel] Error details:', args);\n console.error('[PluginControlPanel] Stack trace:', new Error().stack);\n // Не блокируем оригинальную обработку ошибки\n }\n originalConsoleError.apply(console, args);\n };\n\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring активирован');\n\n return () => {\n console.error = originalConsoleError;\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован');\n };\n }, []);\n\n // Слушатель для Pyodide сообщений через custom events\n useEffect(() => {\n console.log('[PluginControlPanel] Настройка слушателя для pyodide messages');\n\n const handlePyodideCustomEvent = (event: any) => {\n const data = event.detail;\n console.log('[PluginControlPanel] Получен Pyodide custom event:', data);\n\n if (data?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', data.message);\n\n if (data.message?.content) {\n try {\n // Строгая проверка типа content\n let content = data.message.content;\n let messageTimestamp = data.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] Pyodide content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] Pyodide content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из Pyodide content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Pyodide content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: data.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки Pyodide сообщения:', pyodideError, data);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] Pyodide сообщение без content:', data.message);\n }\n }\n };\n\n window.addEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован');\n\n return () => {\n window.removeEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events удален');\n };\n }, []);\n\n // --- Синхронизация message с draftText после загрузки черновика ---\n useEffect(() => {\n if (typeof draftText === 'string') {\n setMessage(draftText);\n console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText);\n }\n }, [draftText, setMessage]);\n\n const handleSendMessage = (): void => {\n console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message });\n if (!message.trim()) return;\n\n const newMessage: ChatMessage = {\n id: Date.now().toString(),\n text: message.trim(),\n isUser: true,\n timestamp: Date.now(),\n };\n\n // Очищаем сообщение через хук\n setMessage('');\n setError(null); // Сбрасываем предыдущие ошибки\n\n console.log('[PluginControlPanel] handleSendMessage: отправка сообщения в background');\n\n sendMessageToBackground({\n type: 'SAVE_PLUGIN_CHAT_MESSAGE',\n pluginId,\n pageKey: currentPageKey,\n message: {\n role: 'user',\n content: newMessage.text,\n timestamp: newMessage.timestamp,\n },\n });\n\n // Очищаем черновик сразу после отправки\n clearDraft();\n };\n\n // Обработка изменения размера разделителя\n useEffect(() => {\n const handleResizeMove = (event: MouseEvent): void => {\n if (!isResizing) return;\n\n const container = document.querySelector('.chat-view') as HTMLElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n const newHeight = containerRect.bottom - event.clientY;\n const minHeight = 100; // Минимальная высота чата\n const maxHeight = containerRect.height - 80; // Максимальная высота чата\n\n if (newHeight >= minHeight && newHeight <= maxHeight) {\n setInputHeight(containerRect.height - newHeight);\n }\n };\n\n const handleResizeEnd = (): void => {\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n if (isResizing) {\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n }\n\n return () => {\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isResizing]);\n\n // Автоскролл к последнему сообщению\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[messages] - состояние messages обновлено:', {\n messagesCount: messages.length,\n firstMessage: messages[0] ? {\n id: messages[0].id,\n text: typeof messages[0].text === 'string' ? messages[0].text.substring(0, 50) : String(messages[0].text || '').substring(0, 50),\n isUser: messages[0].isUser,\n timestamp: messages[0].timestamp,\n textType: typeof messages[0].text\n } : null,\n // Безопасная обработка всех сообщений с проверкой типов\n allMessages: messages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 30) : String(m.text || '').substring(0, 30);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (msgError) {\n console.warn('[PluginControlPanel] Error in message logging:', msgError, m);\n return { id: m.id, text: '[LOGGING ERROR]', isUser: m.isUser, textType: typeof m.text };\n }\n }),\n timestamp: new Date().toISOString()\n });\n }, [messages]);\n\n // Фокус на поле ввода при открытии чата\n useEffect(() => {\n if (currentView === 'chat') {\n setTimeout(() => textareaRef.current?.focus(), 100);\n }\n }, [currentView]);\n\n const handleTextareaChange = (event: React.ChangeEvent): void => {\n setMessage(event.target.value); // Используем хук вместо setMessage\n // Автоматическое изменение высоты\n const textarea = event.target;\n textarea.style.height = 'auto';\n const newHeight = Math.min(Math.max(textarea.scrollHeight, 60), 200); // Минимум 60px, максимум 200px\n textarea.style.height = `${newHeight}px`;\n setInputHeight(newHeight);\n };\n\n const handleKeyPress = (event: React.KeyboardEvent): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n // Очистка чата (удаление всей истории)\n const handleClearChat = (): void => {\n setLoading(true);\n setError(null);\n\n sendMessageToBackground({\n type: 'DELETE_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n // Очищаем локальное состояние сразу\n setMessages([]);\n clearDraft(); // Очищаем черновик\n };\n\n // Экспорт чата в JSON\n const handleExportChat = (): void => {\n const data = JSON.stringify(messages, null, 2);\n const blob = new Blob([data], { type: 'application/json' });\n saveAs(blob, `plugin-chat-${pluginId}.json`);\n };\n\n return (\n
\n
\n
\n {\n const firstChar = typeof pluginName === 'string' && pluginName.length > 0 ? pluginName.charAt(0) : 'P';\n (event.currentTarget as HTMLImageElement).src =\n `data:image/svg+xml;utf8,${firstChar}`;\n }}\n />\n

{pluginName}

\n
\n
\n \n {isRunning ? '⏹️' : '▶️'}\n \n \n ⏸️\n \n \n ⏹️\n \n \n ✕\n \n
\n
\n
\n setActiveTab('chat')}\n >\n Чат\n \n setActiveTab('details')}\n >\n Детали\n \n
\n
\n {activeTab === 'chat' && (\n
\n
\n

Чат

\n
\n \n \n \n 🧪 Тест\n \n
\n
\n
\n {loading &&
Загрузка сообщений...
}\n {error &&
{error}
}\n {!loading && !error && messages.length === 0 && (\n
\n

Нет сообщений

\n

Напишите первое сообщение!

\n
\n )}\n {/* Отображение сообщений чата */}\n
\n {messages.map((msg, idx) => {\n console.log('[PluginControlPanel] render message:', idx, msg);\n\n // Парсинг JSON в тексте сообщения перед рендерингом\n let displayText = msg.text;\n let displayTimestamp = msg.timestamp;\n\n console.log('[PluginControlPanel] Raw message text before parsing for message', idx, ':', msg.text);\n\n try {\n const parsed = JSON.parse(displayText);\n if (typeof parsed === 'object' && parsed !== null && 'content' in parsed) {\n console.log('[PluginControlPanel] Парсинг JSON в рендере:', parsed);\n let content = parsed.content;\n if (typeof content === 'object') {\n displayText = JSON.stringify(content);\n } else {\n displayText = String(content || '');\n }\n if (parsed.timestamp && typeof parsed.timestamp === 'number') {\n displayTimestamp = parsed.timestamp;\n }\n } else if (typeof parsed === 'string') {\n // Если JSON содержит просто строку\n displayText = parsed;\n } else if (typeof parsed === 'object' && parsed !== null) {\n // Если JSON содержит объект без поля content, берем первое строковое поле\n const stringFields = Object.values(parsed).filter(val => typeof val === 'string');\n if (stringFields.length > 0) {\n displayText = String(stringFields[0]);\n } else {\n displayText = JSON.stringify(parsed);\n }\n }\n } catch (parseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Текст не является JSON, рендерим как есть');\n }\n\n console.log('[PluginControlPanel] Display text after parsing for message', idx, ':', displayText);\n\n return (\n \n
\n {displayText}\n \n {new Date(displayTimestamp).toLocaleTimeString()}\n \n
\n
\n );\n })}\n
\n
\n
\n
\n \n \n 📤\n \n \n
\n
\n )}\n {activeTab === 'details' && (\n \n )}\n
\n
\n );\n};","import React, { useState, useEffect, useCallback } from 'react';\nimport './ToastNotifications.css';\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n message: string;\n type: ToastType;\n duration: number;\n timestamp: number;\n}\n\ninterface ToastNotificationsProps {\n toasts: Toast[];\n onRemove: (id: string) => void;\n}\n\nexport const ToastNotifications: React.FC = ({ toasts, onRemove }) => {\n return (\n
\n {toasts.map((toast) => (\n \n ))}\n
\n );\n};\n\ninterface ToastItemProps {\n toast: Toast;\n onRemove: (id: string) => void;\n}\n\nconst ToastItem: React.FC = ({ toast, onRemove }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [isHiding, setIsHiding] = useState(false);\n\n useEffect(() => {\n // Animation in\n requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n // Auto remove\n if (toast.duration > 0) {\n const timer = setTimeout(() => {\n handleRemove();\n }, toast.duration);\n\n return () => clearTimeout(timer);\n }\n\n return undefined; // Explicitly return undefined when no timer is set\n }, [toast.duration]);\n\n const handleRemove = useCallback(() => {\n setIsHiding(true);\n setTimeout(() => {\n onRemove(toast.id);\n }, 300); // Animation duration\n }, [toast.id, onRemove]);\n\n return (\n
\n
\n {toast.message}\n \n
\n
\n );\n};\n\n// Toast manager hook\nexport function useToastManager() {\n const [toasts, setToasts] = useState([]);\n\n const addToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {\n const id = `toast-${Date.now()}-${Math.random()}`;\n const newToast: Toast = {\n id,\n message,\n type,\n duration,\n timestamp: Date.now()\n };\n\n setToasts(prev => {\n const updated = [...prev, newToast];\n // Limit to 3 toasts\n return updated.slice(-3);\n });\n\n return id;\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts(prev => prev.filter(toast => toast.id !== id));\n }, []);\n\n const clearAll = useCallback(() => {\n setToasts([]);\n }, []);\n\n return {\n toasts,\n addToast,\n removeToast,\n clearAll\n };\n}\n\n// Convenience functions\nexport const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {\n // This will be used with the toast manager\n console.log(`[Toast] ${type}: ${message}`);\n};\n\nexport const showSuccessToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'success', duration);\n};\n\nexport const showErrorToast = (message: string, duration: number = 5000) => {\n return showToast(message, 'error', duration);\n};\n\nexport const showWarningToast = (message: string, duration: number = 4000) => {\n return showToast(message, 'warning', duration);\n};\n\nexport const showInfoToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'info', duration);\n}; ","import React from 'react';\n\ninterface ThemeSwitcherProps {\n theme: 'light' | 'dark' | 'system';\n isLight: boolean;\n onToggle: () => void;\n isInSidebar?: boolean; // Контекст использования: sidebar или options\n}\n\nconst ThemeSwitcher: React.FC = ({ theme, isLight, onToggle, isInSidebar = false }) => {\n const getIcon = () => {\n switch (theme) {\n case 'light':\n return '🌙'; // Moon - to switch to dark\n case 'dark':\n return '💻'; // System icon - to switch to system\n case 'system':\n return '☀️'; // Sun - to switch to light\n default:\n return '🌙';\n }\n };\n\n const getTitle = () => {\n switch (theme) {\n case 'light':\n return 'Переключить на темную тему';\n case 'dark':\n return 'Переключить на системную тему';\n case 'system':\n return 'Переключить на светлую тему';\n default:\n return 'Переключить тему';\n }\n };\n\n const buttonStyle: React.CSSProperties = {\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: '50%',\n width: '40px',\n height: '40px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '20px',\n // marginTop только для Options (не в sidebar)\n ...(isInSidebar ? {} : { marginTop: '20px' })\n };\n\n return (\n \n );\n};\n\nexport default ThemeSwitcher;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t {\n const classMap = createClassMap(config);\n const {\n conflictingClassGroups,\n conflictingClassGroupModifiers\n } = config;\n const getClassGroupId = className => {\n const classParts = className.split(CLASS_PART_SEPARATOR);\n // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.\n if (classParts[0] === '' && classParts.length !== 1) {\n classParts.shift();\n }\n return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);\n };\n const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {\n const conflicts = conflictingClassGroups[classGroupId] || [];\n if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {\n return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];\n }\n return conflicts;\n };\n return {\n getClassGroupId,\n getConflictingClassGroupIds\n };\n};\nconst getGroupRecursive = (classParts, classPartObject) => {\n if (classParts.length === 0) {\n return classPartObject.classGroupId;\n }\n const currentClassPart = classParts[0];\n const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);\n const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;\n if (classGroupFromNextClassPart) {\n return classGroupFromNextClassPart;\n }\n if (classPartObject.validators.length === 0) {\n return undefined;\n }\n const classRest = classParts.join(CLASS_PART_SEPARATOR);\n return classPartObject.validators.find(({\n validator\n }) => validator(classRest))?.classGroupId;\n};\nconst arbitraryPropertyRegex = /^\\[(.+)\\]$/;\nconst getGroupIdForArbitraryProperty = className => {\n if (arbitraryPropertyRegex.test(className)) {\n const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];\n const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(':'));\n if (property) {\n // I use two dots here because one dot is used as prefix for class groups in plugins\n return 'arbitrary..' + property;\n }\n }\n};\n/**\n * Exported for testing only\n */\nconst createClassMap = config => {\n const {\n theme,\n classGroups\n } = config;\n const classMap = {\n nextPart: new Map(),\n validators: []\n };\n for (const classGroupId in classGroups) {\n processClassesRecursively(classGroups[classGroupId], classMap, classGroupId, theme);\n }\n return classMap;\n};\nconst processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {\n classGroup.forEach(classDefinition => {\n if (typeof classDefinition === 'string') {\n const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);\n classPartObjectToEdit.classGroupId = classGroupId;\n return;\n }\n if (typeof classDefinition === 'function') {\n if (isThemeGetter(classDefinition)) {\n processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);\n return;\n }\n classPartObject.validators.push({\n validator: classDefinition,\n classGroupId\n });\n return;\n }\n Object.entries(classDefinition).forEach(([key, classGroup]) => {\n processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);\n });\n });\n};\nconst getPart = (classPartObject, path) => {\n let currentClassPartObject = classPartObject;\n path.split(CLASS_PART_SEPARATOR).forEach(pathPart => {\n if (!currentClassPartObject.nextPart.has(pathPart)) {\n currentClassPartObject.nextPart.set(pathPart, {\n nextPart: new Map(),\n validators: []\n });\n }\n currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);\n });\n return currentClassPartObject;\n};\nconst isThemeGetter = func => func.isThemeGetter;\n\n// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance\nconst createLruCache = maxCacheSize => {\n if (maxCacheSize < 1) {\n return {\n get: () => undefined,\n set: () => {}\n };\n }\n let cacheSize = 0;\n let cache = new Map();\n let previousCache = new Map();\n const update = (key, value) => {\n cache.set(key, value);\n cacheSize++;\n if (cacheSize > maxCacheSize) {\n cacheSize = 0;\n previousCache = cache;\n cache = new Map();\n }\n };\n return {\n get(key) {\n let value = cache.get(key);\n if (value !== undefined) {\n return value;\n }\n if ((value = previousCache.get(key)) !== undefined) {\n update(key, value);\n return value;\n }\n },\n set(key, value) {\n if (cache.has(key)) {\n cache.set(key, value);\n } else {\n update(key, value);\n }\n }\n };\n};\nconst IMPORTANT_MODIFIER = '!';\nconst MODIFIER_SEPARATOR = ':';\nconst MODIFIER_SEPARATOR_LENGTH = MODIFIER_SEPARATOR.length;\nconst createParseClassName = config => {\n const {\n prefix,\n experimentalParseClassName\n } = config;\n /**\n * Parse class name into parts.\n *\n * Inspired by `splitAtTopLevelOnly` used in Tailwind CSS\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js\n */\n let parseClassName = className => {\n const modifiers = [];\n let bracketDepth = 0;\n let parenDepth = 0;\n let modifierStart = 0;\n let postfixModifierPosition;\n for (let index = 0; index < className.length; index++) {\n let currentCharacter = className[index];\n if (bracketDepth === 0 && parenDepth === 0) {\n if (currentCharacter === MODIFIER_SEPARATOR) {\n modifiers.push(className.slice(modifierStart, index));\n modifierStart = index + MODIFIER_SEPARATOR_LENGTH;\n continue;\n }\n if (currentCharacter === '/') {\n postfixModifierPosition = index;\n continue;\n }\n }\n if (currentCharacter === '[') {\n bracketDepth++;\n } else if (currentCharacter === ']') {\n bracketDepth--;\n } else if (currentCharacter === '(') {\n parenDepth++;\n } else if (currentCharacter === ')') {\n parenDepth--;\n }\n }\n const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);\n const baseClassName = stripImportantModifier(baseClassNameWithImportantModifier);\n const hasImportantModifier = baseClassName !== baseClassNameWithImportantModifier;\n const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;\n return {\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n };\n };\n if (prefix) {\n const fullPrefix = prefix + MODIFIER_SEPARATOR;\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.substring(fullPrefix.length)) : {\n isExternal: true,\n modifiers: [],\n hasImportantModifier: false,\n baseClassName: className,\n maybePostfixModifierPosition: undefined\n };\n }\n if (experimentalParseClassName) {\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => experimentalParseClassName({\n className,\n parseClassName: parseClassNameOriginal\n });\n }\n return parseClassName;\n};\nconst stripImportantModifier = baseClassName => {\n if (baseClassName.endsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(0, baseClassName.length - 1);\n }\n /**\n * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.\n * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864\n */\n if (baseClassName.startsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(1);\n }\n return baseClassName;\n};\n\n/**\n * Sorts modifiers according to following schema:\n * - Predefined modifiers are sorted alphabetically\n * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it\n */\nconst createSortModifiers = config => {\n const orderSensitiveModifiers = Object.fromEntries(config.orderSensitiveModifiers.map(modifier => [modifier, true]));\n const sortModifiers = modifiers => {\n if (modifiers.length <= 1) {\n return modifiers;\n }\n const sortedModifiers = [];\n let unsortedModifiers = [];\n modifiers.forEach(modifier => {\n const isPositionSensitive = modifier[0] === '[' || orderSensitiveModifiers[modifier];\n if (isPositionSensitive) {\n sortedModifiers.push(...unsortedModifiers.sort(), modifier);\n unsortedModifiers = [];\n } else {\n unsortedModifiers.push(modifier);\n }\n });\n sortedModifiers.push(...unsortedModifiers.sort());\n return sortedModifiers;\n };\n return sortModifiers;\n};\nconst createConfigUtils = config => ({\n cache: createLruCache(config.cacheSize),\n parseClassName: createParseClassName(config),\n sortModifiers: createSortModifiers(config),\n ...createClassGroupUtils(config)\n});\nconst SPLIT_CLASSES_REGEX = /\\s+/;\nconst mergeClassList = (classList, configUtils) => {\n const {\n parseClassName,\n getClassGroupId,\n getConflictingClassGroupIds,\n sortModifiers\n } = configUtils;\n /**\n * Set of classGroupIds in following format:\n * `{importantModifier}{variantModifiers}{classGroupId}`\n * @example 'float'\n * @example 'hover:focus:bg-color'\n * @example 'md:!pr'\n */\n const classGroupsInConflict = [];\n const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);\n let result = '';\n for (let index = classNames.length - 1; index >= 0; index -= 1) {\n const originalClassName = classNames[index];\n const {\n isExternal,\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n } = parseClassName(originalClassName);\n if (isExternal) {\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n let hasPostfixModifier = !!maybePostfixModifierPosition;\n let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);\n if (!classGroupId) {\n if (!hasPostfixModifier) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n classGroupId = getClassGroupId(baseClassName);\n if (!classGroupId) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n hasPostfixModifier = false;\n }\n const variantModifier = sortModifiers(modifiers).join(':');\n const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;\n const classId = modifierId + classGroupId;\n if (classGroupsInConflict.includes(classId)) {\n // Tailwind class omitted due to conflict\n continue;\n }\n classGroupsInConflict.push(classId);\n const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);\n for (let i = 0; i < conflictGroups.length; ++i) {\n const group = conflictGroups[i];\n classGroupsInConflict.push(modifierId + group);\n }\n // Tailwind class not in conflict\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n }\n return result;\n};\n\n/**\n * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.\n *\n * Specifically:\n * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js\n * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts\n *\n * Original code has MIT license: Copyright (c) Luke Edwards (lukeed.com)\n */\nfunction twJoin() {\n let index = 0;\n let argument;\n let resolvedValue;\n let string = '';\n while (index < arguments.length) {\n if (argument = arguments[index++]) {\n if (resolvedValue = toValue(argument)) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n}\nconst toValue = mix => {\n if (typeof mix === 'string') {\n return mix;\n }\n let resolvedValue;\n let string = '';\n for (let k = 0; k < mix.length; k++) {\n if (mix[k]) {\n if (resolvedValue = toValue(mix[k])) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n};\nfunction createTailwindMerge(createConfigFirst, ...createConfigRest) {\n let configUtils;\n let cacheGet;\n let cacheSet;\n let functionToCall = initTailwindMerge;\n function initTailwindMerge(classList) {\n const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());\n configUtils = createConfigUtils(config);\n cacheGet = configUtils.cache.get;\n cacheSet = configUtils.cache.set;\n functionToCall = tailwindMerge;\n return tailwindMerge(classList);\n }\n function tailwindMerge(classList) {\n const cachedResult = cacheGet(classList);\n if (cachedResult) {\n return cachedResult;\n }\n const result = mergeClassList(classList, configUtils);\n cacheSet(classList, result);\n return result;\n }\n return function callTailwindMerge() {\n return functionToCall(twJoin.apply(null, arguments));\n };\n}\nconst fromTheme = key => {\n const themeGetter = theme => theme[key] || [];\n themeGetter.isThemeGetter = true;\n return themeGetter;\n};\nconst arbitraryValueRegex = /^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i;\nconst arbitraryVariableRegex = /^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i;\nconst fractionRegex = /^\\d+\\/\\d+$/;\nconst tshirtUnitRegex = /^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/;\nconst lengthUnitRegex = /\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/;\nconst colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/;\n// Shadow always begins with x and y offset separated by underscore optionally prepended by inset\nconst shadowRegex = /^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/;\nconst imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/;\nconst isFraction = value => fractionRegex.test(value);\nconst isNumber = value => !!value && !Number.isNaN(Number(value));\nconst isInteger = value => !!value && Number.isInteger(Number(value));\nconst isPercent = value => value.endsWith('%') && isNumber(value.slice(0, -1));\nconst isTshirtSize = value => tshirtUnitRegex.test(value);\nconst isAny = () => true;\nconst isLengthOnly = value =>\n// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.\n// For example, `hsl(0 0% 0%)` would be classified as a length without this check.\n// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.\nlengthUnitRegex.test(value) && !colorFunctionRegex.test(value);\nconst isNever = () => false;\nconst isShadow = value => shadowRegex.test(value);\nconst isImage = value => imageRegex.test(value);\nconst isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);\nconst isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);\nconst isArbitraryValue = value => arbitraryValueRegex.test(value);\nconst isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);\nconst isArbitraryNumber = value => getIsArbitraryValue(value, isLabelNumber, isNumber);\nconst isArbitraryPosition = value => getIsArbitraryValue(value, isLabelPosition, isNever);\nconst isArbitraryImage = value => getIsArbitraryValue(value, isLabelImage, isImage);\nconst isArbitraryShadow = value => getIsArbitraryValue(value, isLabelShadow, isShadow);\nconst isArbitraryVariable = value => arbitraryVariableRegex.test(value);\nconst isArbitraryVariableLength = value => getIsArbitraryVariable(value, isLabelLength);\nconst isArbitraryVariableFamilyName = value => getIsArbitraryVariable(value, isLabelFamilyName);\nconst isArbitraryVariablePosition = value => getIsArbitraryVariable(value, isLabelPosition);\nconst isArbitraryVariableSize = value => getIsArbitraryVariable(value, isLabelSize);\nconst isArbitraryVariableImage = value => getIsArbitraryVariable(value, isLabelImage);\nconst isArbitraryVariableShadow = value => getIsArbitraryVariable(value, isLabelShadow, true);\n// Helpers\nconst getIsArbitraryValue = (value, testLabel, testValue) => {\n const result = arbitraryValueRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return testValue(result[2]);\n }\n return false;\n};\nconst getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {\n const result = arbitraryVariableRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return shouldMatchNoLabel;\n }\n return false;\n};\n// Labels\nconst isLabelPosition = label => label === 'position' || label === 'percentage';\nconst isLabelImage = label => label === 'image' || label === 'url';\nconst isLabelSize = label => label === 'length' || label === 'size' || label === 'bg-size';\nconst isLabelLength = label => label === 'length';\nconst isLabelNumber = label => label === 'number';\nconst isLabelFamilyName = label => label === 'family-name';\nconst isLabelShadow = label => label === 'shadow';\nconst validators = /*#__PURE__*/Object.defineProperty({\n __proto__: null,\n isAny,\n isAnyNonArbitrary,\n isArbitraryImage,\n isArbitraryLength,\n isArbitraryNumber,\n isArbitraryPosition,\n isArbitraryShadow,\n isArbitrarySize,\n isArbitraryValue,\n isArbitraryVariable,\n isArbitraryVariableFamilyName,\n isArbitraryVariableImage,\n isArbitraryVariableLength,\n isArbitraryVariablePosition,\n isArbitraryVariableShadow,\n isArbitraryVariableSize,\n isFraction,\n isInteger,\n isNumber,\n isPercent,\n isTshirtSize\n}, Symbol.toStringTag, {\n value: 'Module'\n});\nconst getDefaultConfig = () => {\n /**\n * Theme getters for theme variable namespaces\n * @see https://tailwindcss.com/docs/theme#theme-variable-namespaces\n */\n /***/\n const themeColor = fromTheme('color');\n const themeFont = fromTheme('font');\n const themeText = fromTheme('text');\n const themeFontWeight = fromTheme('font-weight');\n const themeTracking = fromTheme('tracking');\n const themeLeading = fromTheme('leading');\n const themeBreakpoint = fromTheme('breakpoint');\n const themeContainer = fromTheme('container');\n const themeSpacing = fromTheme('spacing');\n const themeRadius = fromTheme('radius');\n const themeShadow = fromTheme('shadow');\n const themeInsetShadow = fromTheme('inset-shadow');\n const themeTextShadow = fromTheme('text-shadow');\n const themeDropShadow = fromTheme('drop-shadow');\n const themeBlur = fromTheme('blur');\n const themePerspective = fromTheme('perspective');\n const themeAspect = fromTheme('aspect');\n const themeEase = fromTheme('ease');\n const themeAnimate = fromTheme('animate');\n /**\n * Helpers to avoid repeating the same scales\n *\n * We use functions that create a new array every time they're called instead of static arrays.\n * This ensures that users who modify any scale by mutating the array (e.g. with `array.push(element)`) don't accidentally mutate arrays in other parts of the config.\n */\n /***/\n const scaleBreak = () => ['auto', 'avoid', 'all', 'avoid-page', 'page', 'left', 'right', 'column'];\n const scalePosition = () => ['center', 'top', 'bottom', 'left', 'right', 'top-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-top', 'top-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-top', 'bottom-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-bottom', 'bottom-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-bottom'];\n const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];\n const scaleOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'];\n const scaleOverscroll = () => ['auto', 'contain', 'none'];\n const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];\n const scaleInset = () => [isFraction, 'full', 'auto', ...scaleUnambiguousSpacing()];\n const scaleGridTemplateColsRows = () => [isInteger, 'none', 'subgrid', isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartAndEnd = () => ['auto', {\n span: ['full', isInteger, isArbitraryVariable, isArbitraryValue]\n }, isInteger, isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartOrEnd = () => [isInteger, 'auto', isArbitraryVariable, isArbitraryValue];\n const scaleGridAutoColsRows = () => ['auto', 'min', 'max', 'fr', isArbitraryVariable, isArbitraryValue];\n const scaleAlignPrimaryAxis = () => ['start', 'end', 'center', 'between', 'around', 'evenly', 'stretch', 'baseline', 'center-safe', 'end-safe'];\n const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];\n const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];\n const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];\n const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];\n const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {\n position: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleBgRepeat = () => ['no-repeat', {\n repeat: ['', 'x', 'y', 'space', 'round']\n }];\n const scaleBgSize = () => ['auto', 'cover', 'contain', isArbitraryVariableSize, isArbitrarySize, {\n size: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];\n const scaleRadius = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', 'full', themeRadius, isArbitraryVariable, isArbitraryValue];\n const scaleBorderWidth = () => ['', isNumber, isArbitraryVariableLength, isArbitraryLength];\n const scaleLineStyle = () => ['solid', 'dashed', 'dotted', 'double'];\n const scaleBlendMode = () => ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];\n const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];\n const scaleBlur = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeBlur, isArbitraryVariable, isArbitraryValue];\n const scaleRotate = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleScale = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleTranslate = () => [isFraction, 'full', ...scaleUnambiguousSpacing()];\n return {\n cacheSize: 500,\n theme: {\n animate: ['spin', 'ping', 'pulse', 'bounce'],\n aspect: ['video'],\n blur: [isTshirtSize],\n breakpoint: [isTshirtSize],\n color: [isAny],\n container: [isTshirtSize],\n 'drop-shadow': [isTshirtSize],\n ease: ['in', 'out', 'in-out'],\n font: [isAnyNonArbitrary],\n 'font-weight': ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black'],\n 'inset-shadow': [isTshirtSize],\n leading: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'],\n perspective: ['dramatic', 'near', 'normal', 'midrange', 'distant', 'none'],\n radius: [isTshirtSize],\n shadow: [isTshirtSize],\n spacing: ['px', isNumber],\n text: [isTshirtSize],\n 'text-shadow': [isTshirtSize],\n tracking: ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest']\n },\n classGroups: {\n // --------------\n // --- Layout ---\n // --------------\n /**\n * Aspect Ratio\n * @see https://tailwindcss.com/docs/aspect-ratio\n */\n aspect: [{\n aspect: ['auto', 'square', isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]\n }],\n /**\n * Container\n * @see https://tailwindcss.com/docs/container\n * @deprecated since Tailwind CSS v4.0.0\n */\n container: ['container'],\n /**\n * Columns\n * @see https://tailwindcss.com/docs/columns\n */\n columns: [{\n columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]\n }],\n /**\n * Break After\n * @see https://tailwindcss.com/docs/break-after\n */\n 'break-after': [{\n 'break-after': scaleBreak()\n }],\n /**\n * Break Before\n * @see https://tailwindcss.com/docs/break-before\n */\n 'break-before': [{\n 'break-before': scaleBreak()\n }],\n /**\n * Break Inside\n * @see https://tailwindcss.com/docs/break-inside\n */\n 'break-inside': [{\n 'break-inside': ['auto', 'avoid', 'avoid-page', 'avoid-column']\n }],\n /**\n * Box Decoration Break\n * @see https://tailwindcss.com/docs/box-decoration-break\n */\n 'box-decoration': [{\n 'box-decoration': ['slice', 'clone']\n }],\n /**\n * Box Sizing\n * @see https://tailwindcss.com/docs/box-sizing\n */\n box: [{\n box: ['border', 'content']\n }],\n /**\n * Display\n * @see https://tailwindcss.com/docs/display\n */\n display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],\n /**\n * Screen Reader Only\n * @see https://tailwindcss.com/docs/display#screen-reader-only\n */\n sr: ['sr-only', 'not-sr-only'],\n /**\n * Floats\n * @see https://tailwindcss.com/docs/float\n */\n float: [{\n float: ['right', 'left', 'none', 'start', 'end']\n }],\n /**\n * Clear\n * @see https://tailwindcss.com/docs/clear\n */\n clear: [{\n clear: ['left', 'right', 'both', 'none', 'start', 'end']\n }],\n /**\n * Isolation\n * @see https://tailwindcss.com/docs/isolation\n */\n isolation: ['isolate', 'isolation-auto'],\n /**\n * Object Fit\n * @see https://tailwindcss.com/docs/object-fit\n */\n 'object-fit': [{\n object: ['contain', 'cover', 'fill', 'none', 'scale-down']\n }],\n /**\n * Object Position\n * @see https://tailwindcss.com/docs/object-position\n */\n 'object-position': [{\n object: scalePositionWithArbitrary()\n }],\n /**\n * Overflow\n * @see https://tailwindcss.com/docs/overflow\n */\n overflow: [{\n overflow: scaleOverflow()\n }],\n /**\n * Overflow X\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-x': [{\n 'overflow-x': scaleOverflow()\n }],\n /**\n * Overflow Y\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-y': [{\n 'overflow-y': scaleOverflow()\n }],\n /**\n * Overscroll Behavior\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n overscroll: [{\n overscroll: scaleOverscroll()\n }],\n /**\n * Overscroll Behavior X\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-x': [{\n 'overscroll-x': scaleOverscroll()\n }],\n /**\n * Overscroll Behavior Y\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-y': [{\n 'overscroll-y': scaleOverscroll()\n }],\n /**\n * Position\n * @see https://tailwindcss.com/docs/position\n */\n position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],\n /**\n * Top / Right / Bottom / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n inset: [{\n inset: scaleInset()\n }],\n /**\n * Right / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-x': [{\n 'inset-x': scaleInset()\n }],\n /**\n * Top / Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-y': [{\n 'inset-y': scaleInset()\n }],\n /**\n * Start\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n start: [{\n start: scaleInset()\n }],\n /**\n * End\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n end: [{\n end: scaleInset()\n }],\n /**\n * Top\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n top: [{\n top: scaleInset()\n }],\n /**\n * Right\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n right: [{\n right: scaleInset()\n }],\n /**\n * Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n bottom: [{\n bottom: scaleInset()\n }],\n /**\n * Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n left: [{\n left: scaleInset()\n }],\n /**\n * Visibility\n * @see https://tailwindcss.com/docs/visibility\n */\n visibility: ['visible', 'invisible', 'collapse'],\n /**\n * Z-Index\n * @see https://tailwindcss.com/docs/z-index\n */\n z: [{\n z: [isInteger, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------------\n // --- Flexbox and Grid ---\n // ------------------------\n /**\n * Flex Basis\n * @see https://tailwindcss.com/docs/flex-basis\n */\n basis: [{\n basis: [isFraction, 'full', 'auto', themeContainer, ...scaleUnambiguousSpacing()]\n }],\n /**\n * Flex Direction\n * @see https://tailwindcss.com/docs/flex-direction\n */\n 'flex-direction': [{\n flex: ['row', 'row-reverse', 'col', 'col-reverse']\n }],\n /**\n * Flex Wrap\n * @see https://tailwindcss.com/docs/flex-wrap\n */\n 'flex-wrap': [{\n flex: ['nowrap', 'wrap', 'wrap-reverse']\n }],\n /**\n * Flex\n * @see https://tailwindcss.com/docs/flex\n */\n flex: [{\n flex: [isNumber, isFraction, 'auto', 'initial', 'none', isArbitraryValue]\n }],\n /**\n * Flex Grow\n * @see https://tailwindcss.com/docs/flex-grow\n */\n grow: [{\n grow: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Flex Shrink\n * @see https://tailwindcss.com/docs/flex-shrink\n */\n shrink: [{\n shrink: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Order\n * @see https://tailwindcss.com/docs/order\n */\n order: [{\n order: [isInteger, 'first', 'last', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Grid Template Columns\n * @see https://tailwindcss.com/docs/grid-template-columns\n */\n 'grid-cols': [{\n 'grid-cols': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Column Start / End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start-end': [{\n col: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Column Start\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start': [{\n 'col-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Column End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-end': [{\n 'col-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Template Rows\n * @see https://tailwindcss.com/docs/grid-template-rows\n */\n 'grid-rows': [{\n 'grid-rows': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Row Start / End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start-end': [{\n row: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Row Start\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start': [{\n 'row-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Row End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-end': [{\n 'row-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Auto Flow\n * @see https://tailwindcss.com/docs/grid-auto-flow\n */\n 'grid-flow': [{\n 'grid-flow': ['row', 'col', 'dense', 'row-dense', 'col-dense']\n }],\n /**\n * Grid Auto Columns\n * @see https://tailwindcss.com/docs/grid-auto-columns\n */\n 'auto-cols': [{\n 'auto-cols': scaleGridAutoColsRows()\n }],\n /**\n * Grid Auto Rows\n * @see https://tailwindcss.com/docs/grid-auto-rows\n */\n 'auto-rows': [{\n 'auto-rows': scaleGridAutoColsRows()\n }],\n /**\n * Gap\n * @see https://tailwindcss.com/docs/gap\n */\n gap: [{\n gap: scaleUnambiguousSpacing()\n }],\n /**\n * Gap X\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-x': [{\n 'gap-x': scaleUnambiguousSpacing()\n }],\n /**\n * Gap Y\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-y': [{\n 'gap-y': scaleUnambiguousSpacing()\n }],\n /**\n * Justify Content\n * @see https://tailwindcss.com/docs/justify-content\n */\n 'justify-content': [{\n justify: [...scaleAlignPrimaryAxis(), 'normal']\n }],\n /**\n * Justify Items\n * @see https://tailwindcss.com/docs/justify-items\n */\n 'justify-items': [{\n 'justify-items': [...scaleAlignSecondaryAxis(), 'normal']\n }],\n /**\n * Justify Self\n * @see https://tailwindcss.com/docs/justify-self\n */\n 'justify-self': [{\n 'justify-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n /**\n * Align Content\n * @see https://tailwindcss.com/docs/align-content\n */\n 'align-content': [{\n content: ['normal', ...scaleAlignPrimaryAxis()]\n }],\n /**\n * Align Items\n * @see https://tailwindcss.com/docs/align-items\n */\n 'align-items': [{\n items: [...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Align Self\n * @see https://tailwindcss.com/docs/align-self\n */\n 'align-self': [{\n self: ['auto', ...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Place Content\n * @see https://tailwindcss.com/docs/place-content\n */\n 'place-content': [{\n 'place-content': scaleAlignPrimaryAxis()\n }],\n /**\n * Place Items\n * @see https://tailwindcss.com/docs/place-items\n */\n 'place-items': [{\n 'place-items': [...scaleAlignSecondaryAxis(), 'baseline']\n }],\n /**\n * Place Self\n * @see https://tailwindcss.com/docs/place-self\n */\n 'place-self': [{\n 'place-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n // Spacing\n /**\n * Padding\n * @see https://tailwindcss.com/docs/padding\n */\n p: [{\n p: scaleUnambiguousSpacing()\n }],\n /**\n * Padding X\n * @see https://tailwindcss.com/docs/padding\n */\n px: [{\n px: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Y\n * @see https://tailwindcss.com/docs/padding\n */\n py: [{\n py: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Start\n * @see https://tailwindcss.com/docs/padding\n */\n ps: [{\n ps: scaleUnambiguousSpacing()\n }],\n /**\n * Padding End\n * @see https://tailwindcss.com/docs/padding\n */\n pe: [{\n pe: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Top\n * @see https://tailwindcss.com/docs/padding\n */\n pt: [{\n pt: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Right\n * @see https://tailwindcss.com/docs/padding\n */\n pr: [{\n pr: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Bottom\n * @see https://tailwindcss.com/docs/padding\n */\n pb: [{\n pb: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Left\n * @see https://tailwindcss.com/docs/padding\n */\n pl: [{\n pl: scaleUnambiguousSpacing()\n }],\n /**\n * Margin\n * @see https://tailwindcss.com/docs/margin\n */\n m: [{\n m: scaleMargin()\n }],\n /**\n * Margin X\n * @see https://tailwindcss.com/docs/margin\n */\n mx: [{\n mx: scaleMargin()\n }],\n /**\n * Margin Y\n * @see https://tailwindcss.com/docs/margin\n */\n my: [{\n my: scaleMargin()\n }],\n /**\n * Margin Start\n * @see https://tailwindcss.com/docs/margin\n */\n ms: [{\n ms: scaleMargin()\n }],\n /**\n * Margin End\n * @see https://tailwindcss.com/docs/margin\n */\n me: [{\n me: scaleMargin()\n }],\n /**\n * Margin Top\n * @see https://tailwindcss.com/docs/margin\n */\n mt: [{\n mt: scaleMargin()\n }],\n /**\n * Margin Right\n * @see https://tailwindcss.com/docs/margin\n */\n mr: [{\n mr: scaleMargin()\n }],\n /**\n * Margin Bottom\n * @see https://tailwindcss.com/docs/margin\n */\n mb: [{\n mb: scaleMargin()\n }],\n /**\n * Margin Left\n * @see https://tailwindcss.com/docs/margin\n */\n ml: [{\n ml: scaleMargin()\n }],\n /**\n * Space Between X\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x': [{\n 'space-x': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between X Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x-reverse': ['space-x-reverse'],\n /**\n * Space Between Y\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y': [{\n 'space-y': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between Y Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y-reverse': ['space-y-reverse'],\n // --------------\n // --- Sizing ---\n // --------------\n /**\n * Size\n * @see https://tailwindcss.com/docs/width#setting-both-width-and-height\n */\n size: [{\n size: scaleSizing()\n }],\n /**\n * Width\n * @see https://tailwindcss.com/docs/width\n */\n w: [{\n w: [themeContainer, 'screen', ...scaleSizing()]\n }],\n /**\n * Min-Width\n * @see https://tailwindcss.com/docs/min-width\n */\n 'min-w': [{\n 'min-w': [themeContainer, 'screen', /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'none', ...scaleSizing()]\n }],\n /**\n * Max-Width\n * @see https://tailwindcss.com/docs/max-width\n */\n 'max-w': [{\n 'max-w': [themeContainer, 'screen', 'none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'prose', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n {\n screen: [themeBreakpoint]\n }, ...scaleSizing()]\n }],\n /**\n * Height\n * @see https://tailwindcss.com/docs/height\n */\n h: [{\n h: ['screen', 'lh', ...scaleSizing()]\n }],\n /**\n * Min-Height\n * @see https://tailwindcss.com/docs/min-height\n */\n 'min-h': [{\n 'min-h': ['screen', 'lh', 'none', ...scaleSizing()]\n }],\n /**\n * Max-Height\n * @see https://tailwindcss.com/docs/max-height\n */\n 'max-h': [{\n 'max-h': ['screen', 'lh', ...scaleSizing()]\n }],\n // ------------------\n // --- Typography ---\n // ------------------\n /**\n * Font Size\n * @see https://tailwindcss.com/docs/font-size\n */\n 'font-size': [{\n text: ['base', themeText, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Font Smoothing\n * @see https://tailwindcss.com/docs/font-smoothing\n */\n 'font-smoothing': ['antialiased', 'subpixel-antialiased'],\n /**\n * Font Style\n * @see https://tailwindcss.com/docs/font-style\n */\n 'font-style': ['italic', 'not-italic'],\n /**\n * Font Weight\n * @see https://tailwindcss.com/docs/font-weight\n */\n 'font-weight': [{\n font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Font Stretch\n * @see https://tailwindcss.com/docs/font-stretch\n */\n 'font-stretch': [{\n 'font-stretch': ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', isPercent, isArbitraryValue]\n }],\n /**\n * Font Family\n * @see https://tailwindcss.com/docs/font-family\n */\n 'font-family': [{\n font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont]\n }],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-normal': ['normal-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-ordinal': ['ordinal'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-slashed-zero': ['slashed-zero'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-figure': ['lining-nums', 'oldstyle-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-spacing': ['proportional-nums', 'tabular-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-fraction': ['diagonal-fractions', 'stacked-fractions'],\n /**\n * Letter Spacing\n * @see https://tailwindcss.com/docs/letter-spacing\n */\n tracking: [{\n tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Line Clamp\n * @see https://tailwindcss.com/docs/line-clamp\n */\n 'line-clamp': [{\n 'line-clamp': [isNumber, 'none', isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Line Height\n * @see https://tailwindcss.com/docs/line-height\n */\n leading: [{\n leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n themeLeading, ...scaleUnambiguousSpacing()]\n }],\n /**\n * List Style Image\n * @see https://tailwindcss.com/docs/list-style-image\n */\n 'list-image': [{\n 'list-image': ['none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * List Style Position\n * @see https://tailwindcss.com/docs/list-style-position\n */\n 'list-style-position': [{\n list: ['inside', 'outside']\n }],\n /**\n * List Style Type\n * @see https://tailwindcss.com/docs/list-style-type\n */\n 'list-style-type': [{\n list: ['disc', 'decimal', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Alignment\n * @see https://tailwindcss.com/docs/text-align\n */\n 'text-alignment': [{\n text: ['left', 'center', 'right', 'justify', 'start', 'end']\n }],\n /**\n * Placeholder Color\n * @deprecated since Tailwind CSS v3.0.0\n * @see https://v3.tailwindcss.com/docs/placeholder-color\n */\n 'placeholder-color': [{\n placeholder: scaleColor()\n }],\n /**\n * Text Color\n * @see https://tailwindcss.com/docs/text-color\n */\n 'text-color': [{\n text: scaleColor()\n }],\n /**\n * Text Decoration\n * @see https://tailwindcss.com/docs/text-decoration\n */\n 'text-decoration': ['underline', 'overline', 'line-through', 'no-underline'],\n /**\n * Text Decoration Style\n * @see https://tailwindcss.com/docs/text-decoration-style\n */\n 'text-decoration-style': [{\n decoration: [...scaleLineStyle(), 'wavy']\n }],\n /**\n * Text Decoration Thickness\n * @see https://tailwindcss.com/docs/text-decoration-thickness\n */\n 'text-decoration-thickness': [{\n decoration: [isNumber, 'from-font', 'auto', isArbitraryVariable, isArbitraryLength]\n }],\n /**\n * Text Decoration Color\n * @see https://tailwindcss.com/docs/text-decoration-color\n */\n 'text-decoration-color': [{\n decoration: scaleColor()\n }],\n /**\n * Text Underline Offset\n * @see https://tailwindcss.com/docs/text-underline-offset\n */\n 'underline-offset': [{\n 'underline-offset': [isNumber, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Transform\n * @see https://tailwindcss.com/docs/text-transform\n */\n 'text-transform': ['uppercase', 'lowercase', 'capitalize', 'normal-case'],\n /**\n * Text Overflow\n * @see https://tailwindcss.com/docs/text-overflow\n */\n 'text-overflow': ['truncate', 'text-ellipsis', 'text-clip'],\n /**\n * Text Wrap\n * @see https://tailwindcss.com/docs/text-wrap\n */\n 'text-wrap': [{\n text: ['wrap', 'nowrap', 'balance', 'pretty']\n }],\n /**\n * Text Indent\n * @see https://tailwindcss.com/docs/text-indent\n */\n indent: [{\n indent: scaleUnambiguousSpacing()\n }],\n /**\n * Vertical Alignment\n * @see https://tailwindcss.com/docs/vertical-align\n */\n 'vertical-align': [{\n align: ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Whitespace\n * @see https://tailwindcss.com/docs/whitespace\n */\n whitespace: [{\n whitespace: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces']\n }],\n /**\n * Word Break\n * @see https://tailwindcss.com/docs/word-break\n */\n break: [{\n break: ['normal', 'words', 'all', 'keep']\n }],\n /**\n * Overflow Wrap\n * @see https://tailwindcss.com/docs/overflow-wrap\n */\n wrap: [{\n wrap: ['break-word', 'anywhere', 'normal']\n }],\n /**\n * Hyphens\n * @see https://tailwindcss.com/docs/hyphens\n */\n hyphens: [{\n hyphens: ['none', 'manual', 'auto']\n }],\n /**\n * Content\n * @see https://tailwindcss.com/docs/content\n */\n content: [{\n content: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // -------------------\n // --- Backgrounds ---\n // -------------------\n /**\n * Background Attachment\n * @see https://tailwindcss.com/docs/background-attachment\n */\n 'bg-attachment': [{\n bg: ['fixed', 'local', 'scroll']\n }],\n /**\n * Background Clip\n * @see https://tailwindcss.com/docs/background-clip\n */\n 'bg-clip': [{\n 'bg-clip': ['border', 'padding', 'content', 'text']\n }],\n /**\n * Background Origin\n * @see https://tailwindcss.com/docs/background-origin\n */\n 'bg-origin': [{\n 'bg-origin': ['border', 'padding', 'content']\n }],\n /**\n * Background Position\n * @see https://tailwindcss.com/docs/background-position\n */\n 'bg-position': [{\n bg: scaleBgPosition()\n }],\n /**\n * Background Repeat\n * @see https://tailwindcss.com/docs/background-repeat\n */\n 'bg-repeat': [{\n bg: scaleBgRepeat()\n }],\n /**\n * Background Size\n * @see https://tailwindcss.com/docs/background-size\n */\n 'bg-size': [{\n bg: scaleBgSize()\n }],\n /**\n * Background Image\n * @see https://tailwindcss.com/docs/background-image\n */\n 'bg-image': [{\n bg: ['none', {\n linear: [{\n to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']\n }, isInteger, isArbitraryVariable, isArbitraryValue],\n radial: ['', isArbitraryVariable, isArbitraryValue],\n conic: [isInteger, isArbitraryVariable, isArbitraryValue]\n }, isArbitraryVariableImage, isArbitraryImage]\n }],\n /**\n * Background Color\n * @see https://tailwindcss.com/docs/background-color\n */\n 'bg-color': [{\n bg: scaleColor()\n }],\n /**\n * Gradient Color Stops From Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from-pos': [{\n from: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops Via Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via-pos': [{\n via: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops To Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to-pos': [{\n to: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops From\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from': [{\n from: scaleColor()\n }],\n /**\n * Gradient Color Stops Via\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via': [{\n via: scaleColor()\n }],\n /**\n * Gradient Color Stops To\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to': [{\n to: scaleColor()\n }],\n // ---------------\n // --- Borders ---\n // ---------------\n /**\n * Border Radius\n * @see https://tailwindcss.com/docs/border-radius\n */\n rounded: [{\n rounded: scaleRadius()\n }],\n /**\n * Border Radius Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-s': [{\n 'rounded-s': scaleRadius()\n }],\n /**\n * Border Radius End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-e': [{\n 'rounded-e': scaleRadius()\n }],\n /**\n * Border Radius Top\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-t': [{\n 'rounded-t': scaleRadius()\n }],\n /**\n * Border Radius Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-r': [{\n 'rounded-r': scaleRadius()\n }],\n /**\n * Border Radius Bottom\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-b': [{\n 'rounded-b': scaleRadius()\n }],\n /**\n * Border Radius Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-l': [{\n 'rounded-l': scaleRadius()\n }],\n /**\n * Border Radius Start Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ss': [{\n 'rounded-ss': scaleRadius()\n }],\n /**\n * Border Radius Start End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-se': [{\n 'rounded-se': scaleRadius()\n }],\n /**\n * Border Radius End End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ee': [{\n 'rounded-ee': scaleRadius()\n }],\n /**\n * Border Radius End Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-es': [{\n 'rounded-es': scaleRadius()\n }],\n /**\n * Border Radius Top Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tl': [{\n 'rounded-tl': scaleRadius()\n }],\n /**\n * Border Radius Top Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tr': [{\n 'rounded-tr': scaleRadius()\n }],\n /**\n * Border Radius Bottom Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-br': [{\n 'rounded-br': scaleRadius()\n }],\n /**\n * Border Radius Bottom Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-bl': [{\n 'rounded-bl': scaleRadius()\n }],\n /**\n * Border Width\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w': [{\n border: scaleBorderWidth()\n }],\n /**\n * Border Width X\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-x': [{\n 'border-x': scaleBorderWidth()\n }],\n /**\n * Border Width Y\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-y': [{\n 'border-y': scaleBorderWidth()\n }],\n /**\n * Border Width Start\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-s': [{\n 'border-s': scaleBorderWidth()\n }],\n /**\n * Border Width End\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-e': [{\n 'border-e': scaleBorderWidth()\n }],\n /**\n * Border Width Top\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-t': [{\n 'border-t': scaleBorderWidth()\n }],\n /**\n * Border Width Right\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-r': [{\n 'border-r': scaleBorderWidth()\n }],\n /**\n * Border Width Bottom\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-b': [{\n 'border-b': scaleBorderWidth()\n }],\n /**\n * Border Width Left\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-l': [{\n 'border-l': scaleBorderWidth()\n }],\n /**\n * Divide Width X\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x': [{\n 'divide-x': scaleBorderWidth()\n }],\n /**\n * Divide Width X Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x-reverse': ['divide-x-reverse'],\n /**\n * Divide Width Y\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y': [{\n 'divide-y': scaleBorderWidth()\n }],\n /**\n * Divide Width Y Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y-reverse': ['divide-y-reverse'],\n /**\n * Border Style\n * @see https://tailwindcss.com/docs/border-style\n */\n 'border-style': [{\n border: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Divide Style\n * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style\n */\n 'divide-style': [{\n divide: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Border Color\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color': [{\n border: scaleColor()\n }],\n /**\n * Border Color X\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-x': [{\n 'border-x': scaleColor()\n }],\n /**\n * Border Color Y\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-y': [{\n 'border-y': scaleColor()\n }],\n /**\n * Border Color S\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-s': [{\n 'border-s': scaleColor()\n }],\n /**\n * Border Color E\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-e': [{\n 'border-e': scaleColor()\n }],\n /**\n * Border Color Top\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-t': [{\n 'border-t': scaleColor()\n }],\n /**\n * Border Color Right\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-r': [{\n 'border-r': scaleColor()\n }],\n /**\n * Border Color Bottom\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-b': [{\n 'border-b': scaleColor()\n }],\n /**\n * Border Color Left\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-l': [{\n 'border-l': scaleColor()\n }],\n /**\n * Divide Color\n * @see https://tailwindcss.com/docs/divide-color\n */\n 'divide-color': [{\n divide: scaleColor()\n }],\n /**\n * Outline Style\n * @see https://tailwindcss.com/docs/outline-style\n */\n 'outline-style': [{\n outline: [...scaleLineStyle(), 'none', 'hidden']\n }],\n /**\n * Outline Offset\n * @see https://tailwindcss.com/docs/outline-offset\n */\n 'outline-offset': [{\n 'outline-offset': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Outline Width\n * @see https://tailwindcss.com/docs/outline-width\n */\n 'outline-w': [{\n outline: ['', isNumber, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Outline Color\n * @see https://tailwindcss.com/docs/outline-color\n */\n 'outline-color': [{\n outline: scaleColor()\n }],\n // ---------------\n // --- Effects ---\n // ---------------\n /**\n * Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow\n */\n shadow: [{\n shadow: [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color\n */\n 'shadow-color': [{\n shadow: scaleColor()\n }],\n /**\n * Inset Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow\n */\n 'inset-shadow': [{\n 'inset-shadow': ['none', themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Inset Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color\n */\n 'inset-shadow-color': [{\n 'inset-shadow': scaleColor()\n }],\n /**\n * Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring\n */\n 'ring-w': [{\n ring: scaleBorderWidth()\n }],\n /**\n * Ring Width Inset\n * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-w-inset': ['ring-inset'],\n /**\n * Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color\n */\n 'ring-color': [{\n ring: scaleColor()\n }],\n /**\n * Ring Offset Width\n * @see https://v3.tailwindcss.com/docs/ring-offset-width\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-w': [{\n 'ring-offset': [isNumber, isArbitraryLength]\n }],\n /**\n * Ring Offset Color\n * @see https://v3.tailwindcss.com/docs/ring-offset-color\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-color': [{\n 'ring-offset': scaleColor()\n }],\n /**\n * Inset Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring\n */\n 'inset-ring-w': [{\n 'inset-ring': scaleBorderWidth()\n }],\n /**\n * Inset Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color\n */\n 'inset-ring-color': [{\n 'inset-ring': scaleColor()\n }],\n /**\n * Text Shadow\n * @see https://tailwindcss.com/docs/text-shadow\n */\n 'text-shadow': [{\n 'text-shadow': ['none', themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Text Shadow Color\n * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color\n */\n 'text-shadow-color': [{\n 'text-shadow': scaleColor()\n }],\n /**\n * Opacity\n * @see https://tailwindcss.com/docs/opacity\n */\n opacity: [{\n opacity: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Mix Blend Mode\n * @see https://tailwindcss.com/docs/mix-blend-mode\n */\n 'mix-blend': [{\n 'mix-blend': [...scaleBlendMode(), 'plus-darker', 'plus-lighter']\n }],\n /**\n * Background Blend Mode\n * @see https://tailwindcss.com/docs/background-blend-mode\n */\n 'bg-blend': [{\n 'bg-blend': scaleBlendMode()\n }],\n /**\n * Mask Clip\n * @see https://tailwindcss.com/docs/mask-clip\n */\n 'mask-clip': [{\n 'mask-clip': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }, 'mask-no-clip'],\n /**\n * Mask Composite\n * @see https://tailwindcss.com/docs/mask-composite\n */\n 'mask-composite': [{\n mask: ['add', 'subtract', 'intersect', 'exclude']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image-linear-pos': [{\n 'mask-linear': [isNumber]\n }],\n 'mask-image-linear-from-pos': [{\n 'mask-linear-from': scaleMaskImagePosition()\n }],\n 'mask-image-linear-to-pos': [{\n 'mask-linear-to': scaleMaskImagePosition()\n }],\n 'mask-image-linear-from-color': [{\n 'mask-linear-from': scaleColor()\n }],\n 'mask-image-linear-to-color': [{\n 'mask-linear-to': scaleColor()\n }],\n 'mask-image-t-from-pos': [{\n 'mask-t-from': scaleMaskImagePosition()\n }],\n 'mask-image-t-to-pos': [{\n 'mask-t-to': scaleMaskImagePosition()\n }],\n 'mask-image-t-from-color': [{\n 'mask-t-from': scaleColor()\n }],\n 'mask-image-t-to-color': [{\n 'mask-t-to': scaleColor()\n }],\n 'mask-image-r-from-pos': [{\n 'mask-r-from': scaleMaskImagePosition()\n }],\n 'mask-image-r-to-pos': [{\n 'mask-r-to': scaleMaskImagePosition()\n }],\n 'mask-image-r-from-color': [{\n 'mask-r-from': scaleColor()\n }],\n 'mask-image-r-to-color': [{\n 'mask-r-to': scaleColor()\n }],\n 'mask-image-b-from-pos': [{\n 'mask-b-from': scaleMaskImagePosition()\n }],\n 'mask-image-b-to-pos': [{\n 'mask-b-to': scaleMaskImagePosition()\n }],\n 'mask-image-b-from-color': [{\n 'mask-b-from': scaleColor()\n }],\n 'mask-image-b-to-color': [{\n 'mask-b-to': scaleColor()\n }],\n 'mask-image-l-from-pos': [{\n 'mask-l-from': scaleMaskImagePosition()\n }],\n 'mask-image-l-to-pos': [{\n 'mask-l-to': scaleMaskImagePosition()\n }],\n 'mask-image-l-from-color': [{\n 'mask-l-from': scaleColor()\n }],\n 'mask-image-l-to-color': [{\n 'mask-l-to': scaleColor()\n }],\n 'mask-image-x-from-pos': [{\n 'mask-x-from': scaleMaskImagePosition()\n }],\n 'mask-image-x-to-pos': [{\n 'mask-x-to': scaleMaskImagePosition()\n }],\n 'mask-image-x-from-color': [{\n 'mask-x-from': scaleColor()\n }],\n 'mask-image-x-to-color': [{\n 'mask-x-to': scaleColor()\n }],\n 'mask-image-y-from-pos': [{\n 'mask-y-from': scaleMaskImagePosition()\n }],\n 'mask-image-y-to-pos': [{\n 'mask-y-to': scaleMaskImagePosition()\n }],\n 'mask-image-y-from-color': [{\n 'mask-y-from': scaleColor()\n }],\n 'mask-image-y-to-color': [{\n 'mask-y-to': scaleColor()\n }],\n 'mask-image-radial': [{\n 'mask-radial': [isArbitraryVariable, isArbitraryValue]\n }],\n 'mask-image-radial-from-pos': [{\n 'mask-radial-from': scaleMaskImagePosition()\n }],\n 'mask-image-radial-to-pos': [{\n 'mask-radial-to': scaleMaskImagePosition()\n }],\n 'mask-image-radial-from-color': [{\n 'mask-radial-from': scaleColor()\n }],\n 'mask-image-radial-to-color': [{\n 'mask-radial-to': scaleColor()\n }],\n 'mask-image-radial-shape': [{\n 'mask-radial': ['circle', 'ellipse']\n }],\n 'mask-image-radial-size': [{\n 'mask-radial': [{\n closest: ['side', 'corner'],\n farthest: ['side', 'corner']\n }]\n }],\n 'mask-image-radial-pos': [{\n 'mask-radial-at': scalePosition()\n }],\n 'mask-image-conic-pos': [{\n 'mask-conic': [isNumber]\n }],\n 'mask-image-conic-from-pos': [{\n 'mask-conic-from': scaleMaskImagePosition()\n }],\n 'mask-image-conic-to-pos': [{\n 'mask-conic-to': scaleMaskImagePosition()\n }],\n 'mask-image-conic-from-color': [{\n 'mask-conic-from': scaleColor()\n }],\n 'mask-image-conic-to-color': [{\n 'mask-conic-to': scaleColor()\n }],\n /**\n * Mask Mode\n * @see https://tailwindcss.com/docs/mask-mode\n */\n 'mask-mode': [{\n mask: ['alpha', 'luminance', 'match']\n }],\n /**\n * Mask Origin\n * @see https://tailwindcss.com/docs/mask-origin\n */\n 'mask-origin': [{\n 'mask-origin': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }],\n /**\n * Mask Position\n * @see https://tailwindcss.com/docs/mask-position\n */\n 'mask-position': [{\n mask: scaleBgPosition()\n }],\n /**\n * Mask Repeat\n * @see https://tailwindcss.com/docs/mask-repeat\n */\n 'mask-repeat': [{\n mask: scaleBgRepeat()\n }],\n /**\n * Mask Size\n * @see https://tailwindcss.com/docs/mask-size\n */\n 'mask-size': [{\n mask: scaleBgSize()\n }],\n /**\n * Mask Type\n * @see https://tailwindcss.com/docs/mask-type\n */\n 'mask-type': [{\n 'mask-type': ['alpha', 'luminance']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image': [{\n mask: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // ---------------\n // --- Filters ---\n // ---------------\n /**\n * Filter\n * @see https://tailwindcss.com/docs/filter\n */\n filter: [{\n filter: [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Blur\n * @see https://tailwindcss.com/docs/blur\n */\n blur: [{\n blur: scaleBlur()\n }],\n /**\n * Brightness\n * @see https://tailwindcss.com/docs/brightness\n */\n brightness: [{\n brightness: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Contrast\n * @see https://tailwindcss.com/docs/contrast\n */\n contrast: [{\n contrast: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Drop Shadow\n * @see https://tailwindcss.com/docs/drop-shadow\n */\n 'drop-shadow': [{\n 'drop-shadow': [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeDropShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Drop Shadow Color\n * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color\n */\n 'drop-shadow-color': [{\n 'drop-shadow': scaleColor()\n }],\n /**\n * Grayscale\n * @see https://tailwindcss.com/docs/grayscale\n */\n grayscale: [{\n grayscale: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Hue Rotate\n * @see https://tailwindcss.com/docs/hue-rotate\n */\n 'hue-rotate': [{\n 'hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Invert\n * @see https://tailwindcss.com/docs/invert\n */\n invert: [{\n invert: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Saturate\n * @see https://tailwindcss.com/docs/saturate\n */\n saturate: [{\n saturate: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Sepia\n * @see https://tailwindcss.com/docs/sepia\n */\n sepia: [{\n sepia: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Filter\n * @see https://tailwindcss.com/docs/backdrop-filter\n */\n 'backdrop-filter': [{\n 'backdrop-filter': [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Blur\n * @see https://tailwindcss.com/docs/backdrop-blur\n */\n 'backdrop-blur': [{\n 'backdrop-blur': scaleBlur()\n }],\n /**\n * Backdrop Brightness\n * @see https://tailwindcss.com/docs/backdrop-brightness\n */\n 'backdrop-brightness': [{\n 'backdrop-brightness': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Contrast\n * @see https://tailwindcss.com/docs/backdrop-contrast\n */\n 'backdrop-contrast': [{\n 'backdrop-contrast': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Grayscale\n * @see https://tailwindcss.com/docs/backdrop-grayscale\n */\n 'backdrop-grayscale': [{\n 'backdrop-grayscale': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Hue Rotate\n * @see https://tailwindcss.com/docs/backdrop-hue-rotate\n */\n 'backdrop-hue-rotate': [{\n 'backdrop-hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Invert\n * @see https://tailwindcss.com/docs/backdrop-invert\n */\n 'backdrop-invert': [{\n 'backdrop-invert': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Opacity\n * @see https://tailwindcss.com/docs/backdrop-opacity\n */\n 'backdrop-opacity': [{\n 'backdrop-opacity': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Saturate\n * @see https://tailwindcss.com/docs/backdrop-saturate\n */\n 'backdrop-saturate': [{\n 'backdrop-saturate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Sepia\n * @see https://tailwindcss.com/docs/backdrop-sepia\n */\n 'backdrop-sepia': [{\n 'backdrop-sepia': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n // --------------\n // --- Tables ---\n // --------------\n /**\n * Border Collapse\n * @see https://tailwindcss.com/docs/border-collapse\n */\n 'border-collapse': [{\n border: ['collapse', 'separate']\n }],\n /**\n * Border Spacing\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing': [{\n 'border-spacing': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing X\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-x': [{\n 'border-spacing-x': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing Y\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-y': [{\n 'border-spacing-y': scaleUnambiguousSpacing()\n }],\n /**\n * Table Layout\n * @see https://tailwindcss.com/docs/table-layout\n */\n 'table-layout': [{\n table: ['auto', 'fixed']\n }],\n /**\n * Caption Side\n * @see https://tailwindcss.com/docs/caption-side\n */\n caption: [{\n caption: ['top', 'bottom']\n }],\n // ---------------------------------\n // --- Transitions and Animation ---\n // ---------------------------------\n /**\n * Transition Property\n * @see https://tailwindcss.com/docs/transition-property\n */\n transition: [{\n transition: ['', 'all', 'colors', 'opacity', 'shadow', 'transform', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Behavior\n * @see https://tailwindcss.com/docs/transition-behavior\n */\n 'transition-behavior': [{\n transition: ['normal', 'discrete']\n }],\n /**\n * Transition Duration\n * @see https://tailwindcss.com/docs/transition-duration\n */\n duration: [{\n duration: [isNumber, 'initial', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Timing Function\n * @see https://tailwindcss.com/docs/transition-timing-function\n */\n ease: [{\n ease: ['linear', 'initial', themeEase, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Delay\n * @see https://tailwindcss.com/docs/transition-delay\n */\n delay: [{\n delay: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Animation\n * @see https://tailwindcss.com/docs/animation\n */\n animate: [{\n animate: ['none', themeAnimate, isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------\n // --- Transforms ---\n // ------------------\n /**\n * Backface Visibility\n * @see https://tailwindcss.com/docs/backface-visibility\n */\n backface: [{\n backface: ['hidden', 'visible']\n }],\n /**\n * Perspective\n * @see https://tailwindcss.com/docs/perspective\n */\n perspective: [{\n perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Perspective Origin\n * @see https://tailwindcss.com/docs/perspective-origin\n */\n 'perspective-origin': [{\n 'perspective-origin': scalePositionWithArbitrary()\n }],\n /**\n * Rotate\n * @see https://tailwindcss.com/docs/rotate\n */\n rotate: [{\n rotate: scaleRotate()\n }],\n /**\n * Rotate X\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-x': [{\n 'rotate-x': scaleRotate()\n }],\n /**\n * Rotate Y\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-y': [{\n 'rotate-y': scaleRotate()\n }],\n /**\n * Rotate Z\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-z': [{\n 'rotate-z': scaleRotate()\n }],\n /**\n * Scale\n * @see https://tailwindcss.com/docs/scale\n */\n scale: [{\n scale: scaleScale()\n }],\n /**\n * Scale X\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-x': [{\n 'scale-x': scaleScale()\n }],\n /**\n * Scale Y\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-y': [{\n 'scale-y': scaleScale()\n }],\n /**\n * Scale Z\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-z': [{\n 'scale-z': scaleScale()\n }],\n /**\n * Scale 3D\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-3d': ['scale-3d'],\n /**\n * Skew\n * @see https://tailwindcss.com/docs/skew\n */\n skew: [{\n skew: scaleSkew()\n }],\n /**\n * Skew X\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-x': [{\n 'skew-x': scaleSkew()\n }],\n /**\n * Skew Y\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-y': [{\n 'skew-y': scaleSkew()\n }],\n /**\n * Transform\n * @see https://tailwindcss.com/docs/transform\n */\n transform: [{\n transform: [isArbitraryVariable, isArbitraryValue, '', 'none', 'gpu', 'cpu']\n }],\n /**\n * Transform Origin\n * @see https://tailwindcss.com/docs/transform-origin\n */\n 'transform-origin': [{\n origin: scalePositionWithArbitrary()\n }],\n /**\n * Transform Style\n * @see https://tailwindcss.com/docs/transform-style\n */\n 'transform-style': [{\n transform: ['3d', 'flat']\n }],\n /**\n * Translate\n * @see https://tailwindcss.com/docs/translate\n */\n translate: [{\n translate: scaleTranslate()\n }],\n /**\n * Translate X\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-x': [{\n 'translate-x': scaleTranslate()\n }],\n /**\n * Translate Y\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-y': [{\n 'translate-y': scaleTranslate()\n }],\n /**\n * Translate Z\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-z': [{\n 'translate-z': scaleTranslate()\n }],\n /**\n * Translate None\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-none': ['translate-none'],\n // ---------------------\n // --- Interactivity ---\n // ---------------------\n /**\n * Accent Color\n * @see https://tailwindcss.com/docs/accent-color\n */\n accent: [{\n accent: scaleColor()\n }],\n /**\n * Appearance\n * @see https://tailwindcss.com/docs/appearance\n */\n appearance: [{\n appearance: ['none', 'auto']\n }],\n /**\n * Caret Color\n * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities\n */\n 'caret-color': [{\n caret: scaleColor()\n }],\n /**\n * Color Scheme\n * @see https://tailwindcss.com/docs/color-scheme\n */\n 'color-scheme': [{\n scheme: ['normal', 'dark', 'light', 'light-dark', 'only-dark', 'only-light']\n }],\n /**\n * Cursor\n * @see https://tailwindcss.com/docs/cursor\n */\n cursor: [{\n cursor: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'help', 'not-allowed', 'none', 'context-menu', 'progress', 'cell', 'crosshair', 'vertical-text', 'alias', 'copy', 'no-drop', 'grab', 'grabbing', 'all-scroll', 'col-resize', 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Field Sizing\n * @see https://tailwindcss.com/docs/field-sizing\n */\n 'field-sizing': [{\n 'field-sizing': ['fixed', 'content']\n }],\n /**\n * Pointer Events\n * @see https://tailwindcss.com/docs/pointer-events\n */\n 'pointer-events': [{\n 'pointer-events': ['auto', 'none']\n }],\n /**\n * Resize\n * @see https://tailwindcss.com/docs/resize\n */\n resize: [{\n resize: ['none', '', 'y', 'x']\n }],\n /**\n * Scroll Behavior\n * @see https://tailwindcss.com/docs/scroll-behavior\n */\n 'scroll-behavior': [{\n scroll: ['auto', 'smooth']\n }],\n /**\n * Scroll Margin\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-m': [{\n 'scroll-m': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin X\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mx': [{\n 'scroll-mx': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Y\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-my': [{\n 'scroll-my': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Start\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ms': [{\n 'scroll-ms': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin End\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-me': [{\n 'scroll-me': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Top\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mt': [{\n 'scroll-mt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Right\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mr': [{\n 'scroll-mr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Bottom\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mb': [{\n 'scroll-mb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Left\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ml': [{\n 'scroll-ml': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-p': [{\n 'scroll-p': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding X\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-px': [{\n 'scroll-px': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Y\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-py': [{\n 'scroll-py': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Start\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-ps': [{\n 'scroll-ps': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding End\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pe': [{\n 'scroll-pe': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Top\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pt': [{\n 'scroll-pt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Right\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pr': [{\n 'scroll-pr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Bottom\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pb': [{\n 'scroll-pb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Left\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pl': [{\n 'scroll-pl': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Snap Align\n * @see https://tailwindcss.com/docs/scroll-snap-align\n */\n 'snap-align': [{\n snap: ['start', 'end', 'center', 'align-none']\n }],\n /**\n * Scroll Snap Stop\n * @see https://tailwindcss.com/docs/scroll-snap-stop\n */\n 'snap-stop': [{\n snap: ['normal', 'always']\n }],\n /**\n * Scroll Snap Type\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-type': [{\n snap: ['none', 'x', 'y', 'both']\n }],\n /**\n * Scroll Snap Type Strictness\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-strictness': [{\n snap: ['mandatory', 'proximity']\n }],\n /**\n * Touch Action\n * @see https://tailwindcss.com/docs/touch-action\n */\n touch: [{\n touch: ['auto', 'none', 'manipulation']\n }],\n /**\n * Touch Action X\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-x': [{\n 'touch-pan': ['x', 'left', 'right']\n }],\n /**\n * Touch Action Y\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-y': [{\n 'touch-pan': ['y', 'up', 'down']\n }],\n /**\n * Touch Action Pinch Zoom\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-pz': ['touch-pinch-zoom'],\n /**\n * User Select\n * @see https://tailwindcss.com/docs/user-select\n */\n select: [{\n select: ['none', 'text', 'all', 'auto']\n }],\n /**\n * Will Change\n * @see https://tailwindcss.com/docs/will-change\n */\n 'will-change': [{\n 'will-change': ['auto', 'scroll', 'contents', 'transform', isArbitraryVariable, isArbitraryValue]\n }],\n // -----------\n // --- SVG ---\n // -----------\n /**\n * Fill\n * @see https://tailwindcss.com/docs/fill\n */\n fill: [{\n fill: ['none', ...scaleColor()]\n }],\n /**\n * Stroke Width\n * @see https://tailwindcss.com/docs/stroke-width\n */\n 'stroke-w': [{\n stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]\n }],\n /**\n * Stroke\n * @see https://tailwindcss.com/docs/stroke\n */\n stroke: [{\n stroke: ['none', ...scaleColor()]\n }],\n // ---------------------\n // --- Accessibility ---\n // ---------------------\n /**\n * Forced Color Adjust\n * @see https://tailwindcss.com/docs/forced-color-adjust\n */\n 'forced-color-adjust': [{\n 'forced-color-adjust': ['auto', 'none']\n }]\n },\n conflictingClassGroups: {\n overflow: ['overflow-x', 'overflow-y'],\n overscroll: ['overscroll-x', 'overscroll-y'],\n inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],\n 'inset-x': ['right', 'left'],\n 'inset-y': ['top', 'bottom'],\n flex: ['basis', 'grow', 'shrink'],\n gap: ['gap-x', 'gap-y'],\n p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],\n px: ['pr', 'pl'],\n py: ['pt', 'pb'],\n m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],\n mx: ['mr', 'ml'],\n my: ['mt', 'mb'],\n size: ['w', 'h'],\n 'font-size': ['leading'],\n 'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],\n 'fvn-ordinal': ['fvn-normal'],\n 'fvn-slashed-zero': ['fvn-normal'],\n 'fvn-figure': ['fvn-normal'],\n 'fvn-spacing': ['fvn-normal'],\n 'fvn-fraction': ['fvn-normal'],\n 'line-clamp': ['display', 'overflow'],\n rounded: ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],\n 'rounded-s': ['rounded-ss', 'rounded-es'],\n 'rounded-e': ['rounded-se', 'rounded-ee'],\n 'rounded-t': ['rounded-tl', 'rounded-tr'],\n 'rounded-r': ['rounded-tr', 'rounded-br'],\n 'rounded-b': ['rounded-br', 'rounded-bl'],\n 'rounded-l': ['rounded-tl', 'rounded-bl'],\n 'border-spacing': ['border-spacing-x', 'border-spacing-y'],\n 'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],\n 'border-w-x': ['border-w-r', 'border-w-l'],\n 'border-w-y': ['border-w-t', 'border-w-b'],\n 'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],\n 'border-color-x': ['border-color-r', 'border-color-l'],\n 'border-color-y': ['border-color-t', 'border-color-b'],\n translate: ['translate-x', 'translate-y', 'translate-none'],\n 'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],\n 'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],\n 'scroll-mx': ['scroll-mr', 'scroll-ml'],\n 'scroll-my': ['scroll-mt', 'scroll-mb'],\n 'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],\n 'scroll-px': ['scroll-pr', 'scroll-pl'],\n 'scroll-py': ['scroll-pt', 'scroll-pb'],\n touch: ['touch-x', 'touch-y', 'touch-pz'],\n 'touch-x': ['touch'],\n 'touch-y': ['touch'],\n 'touch-pz': ['touch']\n },\n conflictingClassGroupModifiers: {\n 'font-size': ['leading']\n },\n orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']\n };\n};\n\n/**\n * @param baseConfig Config where other config will be merged into. This object will be mutated.\n * @param configExtension Partial config to merge into the `baseConfig`.\n */\nconst mergeConfigs = (baseConfig, {\n cacheSize,\n prefix,\n experimentalParseClassName,\n extend = {},\n override = {}\n}) => {\n overrideProperty(baseConfig, 'cacheSize', cacheSize);\n overrideProperty(baseConfig, 'prefix', prefix);\n overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName);\n overrideConfigProperties(baseConfig.theme, override.theme);\n overrideConfigProperties(baseConfig.classGroups, override.classGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroupModifiers, override.conflictingClassGroupModifiers);\n overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers);\n mergeConfigProperties(baseConfig.theme, extend.theme);\n mergeConfigProperties(baseConfig.classGroups, extend.classGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroupModifiers, extend.conflictingClassGroupModifiers);\n mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers');\n return baseConfig;\n};\nconst overrideProperty = (baseObject, overrideKey, overrideValue) => {\n if (overrideValue !== undefined) {\n baseObject[overrideKey] = overrideValue;\n }\n};\nconst overrideConfigProperties = (baseObject, overrideObject) => {\n if (overrideObject) {\n for (const key in overrideObject) {\n overrideProperty(baseObject, key, overrideObject[key]);\n }\n }\n};\nconst mergeConfigProperties = (baseObject, mergeObject) => {\n if (mergeObject) {\n for (const key in mergeObject) {\n mergeArrayProperties(baseObject, mergeObject, key);\n }\n }\n};\nconst mergeArrayProperties = (baseObject, mergeObject, key) => {\n const mergeValue = mergeObject[key];\n if (mergeValue !== undefined) {\n baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue;\n }\n};\nconst extendTailwindMerge = (configExtension, ...createConfig) => typeof configExtension === 'function' ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig) : createTailwindMerge(() => mergeConfigs(getDefaultConfig(), configExtension), ...createConfig);\nconst twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);\nexport { createTailwindMerge, extendTailwindMerge, fromTheme, getDefaultConfig, mergeConfigs, twJoin, twMerge, validators };\n//# sourceMappingURL=bundle-mjs.mjs.map\n","import { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nexport const cn = (...inputs) => twMerge(clsx(inputs));\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","/**\n * @license React\n * scheduler.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nfunction push(heap, node) {\n var index = heap.length;\n heap.push(node);\n a: for (; 0 < index; ) {\n var parentIndex = (index - 1) >>> 1,\n parent = heap[parentIndex];\n if (0 < compare(parent, node))\n (heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);\n else break a;\n }\n}\nfunction peek(heap) {\n return 0 === heap.length ? null : heap[0];\n}\nfunction pop(heap) {\n if (0 === heap.length) return null;\n var first = heap[0],\n last = heap.pop();\n if (last !== first) {\n heap[0] = last;\n a: for (\n var index = 0, length = heap.length, halfLength = length >>> 1;\n index < halfLength;\n\n ) {\n var leftIndex = 2 * (index + 1) - 1,\n left = heap[leftIndex],\n rightIndex = leftIndex + 1,\n right = heap[rightIndex];\n if (0 > compare(left, last))\n rightIndex < length && 0 > compare(right, left)\n ? ((heap[index] = right),\n (heap[rightIndex] = last),\n (index = rightIndex))\n : ((heap[index] = left),\n (heap[leftIndex] = last),\n (index = leftIndex));\n else if (rightIndex < length && 0 > compare(right, last))\n (heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);\n else break a;\n }\n }\n return first;\n}\nfunction compare(a, b) {\n var diff = a.sortIndex - b.sortIndex;\n return 0 !== diff ? diff : a.id - b.id;\n}\nexports.unstable_now = void 0;\nif (\"object\" === typeof performance && \"function\" === typeof performance.now) {\n var localPerformance = performance;\n exports.unstable_now = function () {\n return localPerformance.now();\n };\n} else {\n var localDate = Date,\n initialTime = localDate.now();\n exports.unstable_now = function () {\n return localDate.now() - initialTime;\n };\n}\nvar taskQueue = [],\n timerQueue = [],\n taskIdCounter = 1,\n currentTask = null,\n currentPriorityLevel = 3,\n isPerformingWork = !1,\n isHostCallbackScheduled = !1,\n isHostTimeoutScheduled = !1,\n needsPaint = !1,\n localSetTimeout = \"function\" === typeof setTimeout ? setTimeout : null,\n localClearTimeout = \"function\" === typeof clearTimeout ? clearTimeout : null,\n localSetImmediate = \"undefined\" !== typeof setImmediate ? setImmediate : null;\nfunction advanceTimers(currentTime) {\n for (var timer = peek(timerQueue); null !== timer; ) {\n if (null === timer.callback) pop(timerQueue);\n else if (timer.startTime <= currentTime)\n pop(timerQueue),\n (timer.sortIndex = timer.expirationTime),\n push(taskQueue, timer);\n else break;\n timer = peek(timerQueue);\n }\n}\nfunction handleTimeout(currentTime) {\n isHostTimeoutScheduled = !1;\n advanceTimers(currentTime);\n if (!isHostCallbackScheduled)\n if (null !== peek(taskQueue))\n (isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);\n }\n}\nvar isMessageLoopRunning = !1,\n taskTimeoutID = -1,\n frameInterval = 5,\n startTime = -1;\nfunction shouldYieldToHost() {\n return needsPaint\n ? !0\n : exports.unstable_now() - startTime < frameInterval\n ? !1\n : !0;\n}\nfunction performWorkUntilDeadline() {\n needsPaint = !1;\n if (isMessageLoopRunning) {\n var currentTime = exports.unstable_now();\n startTime = currentTime;\n var hasMoreWork = !0;\n try {\n a: {\n isHostCallbackScheduled = !1;\n isHostTimeoutScheduled &&\n ((isHostTimeoutScheduled = !1),\n localClearTimeout(taskTimeoutID),\n (taskTimeoutID = -1));\n isPerformingWork = !0;\n var previousPriorityLevel = currentPriorityLevel;\n try {\n b: {\n advanceTimers(currentTime);\n for (\n currentTask = peek(taskQueue);\n null !== currentTask &&\n !(\n currentTask.expirationTime > currentTime && shouldYieldToHost()\n );\n\n ) {\n var callback = currentTask.callback;\n if (\"function\" === typeof callback) {\n currentTask.callback = null;\n currentPriorityLevel = currentTask.priorityLevel;\n var continuationCallback = callback(\n currentTask.expirationTime <= currentTime\n );\n currentTime = exports.unstable_now();\n if (\"function\" === typeof continuationCallback) {\n currentTask.callback = continuationCallback;\n advanceTimers(currentTime);\n hasMoreWork = !0;\n break b;\n }\n currentTask === peek(taskQueue) && pop(taskQueue);\n advanceTimers(currentTime);\n } else pop(taskQueue);\n currentTask = peek(taskQueue);\n }\n if (null !== currentTask) hasMoreWork = !0;\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(\n handleTimeout,\n firstTimer.startTime - currentTime\n );\n hasMoreWork = !1;\n }\n }\n break a;\n } finally {\n (currentTask = null),\n (currentPriorityLevel = previousPriorityLevel),\n (isPerformingWork = !1);\n }\n hasMoreWork = void 0;\n }\n } finally {\n hasMoreWork\n ? schedulePerformWorkUntilDeadline()\n : (isMessageLoopRunning = !1);\n }\n }\n}\nvar schedulePerformWorkUntilDeadline;\nif (\"function\" === typeof localSetImmediate)\n schedulePerformWorkUntilDeadline = function () {\n localSetImmediate(performWorkUntilDeadline);\n };\nelse if (\"undefined\" !== typeof MessageChannel) {\n var channel = new MessageChannel(),\n port = channel.port2;\n channel.port1.onmessage = performWorkUntilDeadline;\n schedulePerformWorkUntilDeadline = function () {\n port.postMessage(null);\n };\n} else\n schedulePerformWorkUntilDeadline = function () {\n localSetTimeout(performWorkUntilDeadline, 0);\n };\nfunction requestHostTimeout(callback, ms) {\n taskTimeoutID = localSetTimeout(function () {\n callback(exports.unstable_now());\n }, ms);\n}\nexports.unstable_IdlePriority = 5;\nexports.unstable_ImmediatePriority = 1;\nexports.unstable_LowPriority = 4;\nexports.unstable_NormalPriority = 3;\nexports.unstable_Profiling = null;\nexports.unstable_UserBlockingPriority = 2;\nexports.unstable_cancelCallback = function (task) {\n task.callback = null;\n};\nexports.unstable_forceFrameRate = function (fps) {\n 0 > fps || 125 < fps\n ? console.error(\n \"forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported\"\n )\n : (frameInterval = 0 < fps ? Math.floor(1e3 / fps) : 5);\n};\nexports.unstable_getCurrentPriorityLevel = function () {\n return currentPriorityLevel;\n};\nexports.unstable_next = function (eventHandler) {\n switch (currentPriorityLevel) {\n case 1:\n case 2:\n case 3:\n var priorityLevel = 3;\n break;\n default:\n priorityLevel = currentPriorityLevel;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_requestPaint = function () {\n needsPaint = !0;\n};\nexports.unstable_runWithPriority = function (priorityLevel, eventHandler) {\n switch (priorityLevel) {\n case 1:\n case 2:\n case 3:\n case 4:\n case 5:\n break;\n default:\n priorityLevel = 3;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_scheduleCallback = function (\n priorityLevel,\n callback,\n options\n) {\n var currentTime = exports.unstable_now();\n \"object\" === typeof options && null !== options\n ? ((options = options.delay),\n (options =\n \"number\" === typeof options && 0 < options\n ? currentTime + options\n : currentTime))\n : (options = currentTime);\n switch (priorityLevel) {\n case 1:\n var timeout = -1;\n break;\n case 2:\n timeout = 250;\n break;\n case 5:\n timeout = 1073741823;\n break;\n case 4:\n timeout = 1e4;\n break;\n default:\n timeout = 5e3;\n }\n timeout = options + timeout;\n priorityLevel = {\n id: taskIdCounter++,\n callback: callback,\n priorityLevel: priorityLevel,\n startTime: options,\n expirationTime: timeout,\n sortIndex: -1\n };\n options > currentTime\n ? ((priorityLevel.sortIndex = options),\n push(timerQueue, priorityLevel),\n null === peek(taskQueue) &&\n priorityLevel === peek(timerQueue) &&\n (isHostTimeoutScheduled\n ? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))\n : (isHostTimeoutScheduled = !0),\n requestHostTimeout(handleTimeout, options - currentTime)))\n : ((priorityLevel.sortIndex = timeout),\n push(taskQueue, priorityLevel),\n isHostCallbackScheduled ||\n isPerformingWork ||\n ((isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));\n return priorityLevel;\n};\nexports.unstable_shouldYield = shouldYieldToHost;\nexports.unstable_wrapCallback = function (callback) {\n var parentPriorityLevel = currentPriorityLevel;\n return function () {\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = parentPriorityLevel;\n try {\n return callback.apply(this, arguments);\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n };\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction noop() {}\nvar Internals = {\n d: {\n f: noop,\n r: function () {\n throw Error(formatProdErrorMessage(522));\n },\n D: noop,\n C: noop,\n L: noop,\n m: noop,\n X: noop,\n S: noop,\n M: noop\n },\n p: 0,\n findDOMNode: null\n },\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\");\nfunction createPortal$1(children, containerInfo, implementation) {\n var key =\n 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;\n return {\n $$typeof: REACT_PORTAL_TYPE,\n key: null == key ? null : \"\" + key,\n children: children,\n containerInfo: containerInfo,\n implementation: implementation\n };\n}\nvar ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\nfunction getCrossOriginStringAs(as, input) {\n if (\"font\" === as) return \"\";\n if (\"string\" === typeof input)\n return \"use-credentials\" === input ? input : \"\";\n}\nexports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n Internals;\nexports.createPortal = function (children, container) {\n var key =\n 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;\n if (\n !container ||\n (1 !== container.nodeType &&\n 9 !== container.nodeType &&\n 11 !== container.nodeType)\n )\n throw Error(formatProdErrorMessage(299));\n return createPortal$1(children, container, null, key);\n};\nexports.flushSync = function (fn) {\n var previousTransition = ReactSharedInternals.T,\n previousUpdatePriority = Internals.p;\n try {\n if (((ReactSharedInternals.T = null), (Internals.p = 2), fn)) return fn();\n } finally {\n (ReactSharedInternals.T = previousTransition),\n (Internals.p = previousUpdatePriority),\n Internals.d.f();\n }\n};\nexports.preconnect = function (href, options) {\n \"string\" === typeof href &&\n (options\n ? ((options = options.crossOrigin),\n (options =\n \"string\" === typeof options\n ? \"use-credentials\" === options\n ? options\n : \"\"\n : void 0))\n : (options = null),\n Internals.d.C(href, options));\n};\nexports.prefetchDNS = function (href) {\n \"string\" === typeof href && Internals.d.D(href);\n};\nexports.preinit = function (href, options) {\n if (\"string\" === typeof href && options && \"string\" === typeof options.as) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin),\n integrity =\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n fetchPriority =\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0;\n \"style\" === as\n ? Internals.d.S(\n href,\n \"string\" === typeof options.precedence ? options.precedence : void 0,\n {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority\n }\n )\n : \"script\" === as &&\n Internals.d.X(href, {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n};\nexports.preinitModule = function (href, options) {\n if (\"string\" === typeof href)\n if (\"object\" === typeof options && null !== options) {\n if (null == options.as || \"script\" === options.as) {\n var crossOrigin = getCrossOriginStringAs(\n options.as,\n options.crossOrigin\n );\n Internals.d.M(href, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n } else null == options && Internals.d.M(href);\n};\nexports.preload = function (href, options) {\n if (\n \"string\" === typeof href &&\n \"object\" === typeof options &&\n null !== options &&\n \"string\" === typeof options.as\n ) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);\n Internals.d.L(href, as, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0,\n type: \"string\" === typeof options.type ? options.type : void 0,\n fetchPriority:\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0,\n referrerPolicy:\n \"string\" === typeof options.referrerPolicy\n ? options.referrerPolicy\n : void 0,\n imageSrcSet:\n \"string\" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,\n imageSizes:\n \"string\" === typeof options.imageSizes ? options.imageSizes : void 0,\n media: \"string\" === typeof options.media ? options.media : void 0\n });\n }\n};\nexports.preloadModule = function (href, options) {\n if (\"string\" === typeof href)\n if (options) {\n var crossOrigin = getCrossOriginStringAs(options.as, options.crossOrigin);\n Internals.d.m(href, {\n as:\n \"string\" === typeof options.as && \"script\" !== options.as\n ? options.as\n : void 0,\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0\n });\n } else Internals.d.m(href);\n};\nexports.requestFormReset = function (form) {\n Internals.d.r(form);\n};\nexports.unstable_batchedUpdates = function (fn, a) {\n return fn(a);\n};\nexports.useFormState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useFormState(action, initialState, permalink);\n};\nexports.useFormStatus = function () {\n return ReactSharedInternals.H.useHostTransitionStatus();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nfunction checkDCE() {\n /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\n if (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' ||\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function'\n ) {\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // This branch is unreachable because this function is only called\n // in production, but the condition is true only in development.\n // Therefore if the branch is still here, dead code elimination wasn't\n // properly applied.\n // Don't change the message. React DevTools relies on it. Also make sure\n // this message doesn't occur elsewhere in this function, or it will cause\n // a false positive.\n throw new Error('^_^');\n }\n try {\n // Verify that the code above has been dead code eliminated (DCE'd).\n __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);\n } catch (err) {\n // DevTools shouldn't crash React, no matter what.\n // We should still report in case we break this code.\n console.error(err);\n }\n}\n\nif (process.env.NODE_ENV === 'production') {\n // DCE check should happen before ReactDOM bundle executes so that\n // DevTools can report bad minification during injection.\n checkDCE();\n module.exports = require('./cjs/react-dom.production.js');\n} else {\n module.exports = require('./cjs/react-dom.development.js');\n}\n","/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n\"use strict\";\nvar Scheduler = require(\"scheduler\"),\n React = require(\"react\"),\n ReactDOM = require(\"react-dom\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction isValidContainer(node) {\n return !(\n !node ||\n (1 !== node.nodeType && 9 !== node.nodeType && 11 !== node.nodeType)\n );\n}\nfunction getNearestMountedFiber(fiber) {\n var node = fiber,\n nearestMounted = fiber;\n if (fiber.alternate) for (; node.return; ) node = node.return;\n else {\n fiber = node;\n do\n (node = fiber),\n 0 !== (node.flags & 4098) && (nearestMounted = node.return),\n (fiber = node.return);\n while (fiber);\n }\n return 3 === node.tag ? nearestMounted : null;\n}\nfunction getSuspenseInstanceFromFiber(fiber) {\n if (13 === fiber.tag) {\n var suspenseState = fiber.memoizedState;\n null === suspenseState &&\n ((fiber = fiber.alternate),\n null !== fiber && (suspenseState = fiber.memoizedState));\n if (null !== suspenseState) return suspenseState.dehydrated;\n }\n return null;\n}\nfunction getActivityInstanceFromFiber(fiber) {\n if (31 === fiber.tag) {\n var activityState = fiber.memoizedState;\n null === activityState &&\n ((fiber = fiber.alternate),\n null !== fiber && (activityState = fiber.memoizedState));\n if (null !== activityState) return activityState.dehydrated;\n }\n return null;\n}\nfunction assertIsMounted(fiber) {\n if (getNearestMountedFiber(fiber) !== fiber)\n throw Error(formatProdErrorMessage(188));\n}\nfunction findCurrentFiberUsingSlowPath(fiber) {\n var alternate = fiber.alternate;\n if (!alternate) {\n alternate = getNearestMountedFiber(fiber);\n if (null === alternate) throw Error(formatProdErrorMessage(188));\n return alternate !== fiber ? null : fiber;\n }\n for (var a = fiber, b = alternate; ; ) {\n var parentA = a.return;\n if (null === parentA) break;\n var parentB = parentA.alternate;\n if (null === parentB) {\n b = parentA.return;\n if (null !== b) {\n a = b;\n continue;\n }\n break;\n }\n if (parentA.child === parentB.child) {\n for (parentB = parentA.child; parentB; ) {\n if (parentB === a) return assertIsMounted(parentA), fiber;\n if (parentB === b) return assertIsMounted(parentA), alternate;\n parentB = parentB.sibling;\n }\n throw Error(formatProdErrorMessage(188));\n }\n if (a.return !== b.return) (a = parentA), (b = parentB);\n else {\n for (var didFindChild = !1, child$0 = parentA.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentA;\n b = parentB;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentA;\n a = parentB;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) {\n for (child$0 = parentB.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentB;\n b = parentA;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentB;\n a = parentA;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) throw Error(formatProdErrorMessage(189));\n }\n }\n if (a.alternate !== b) throw Error(formatProdErrorMessage(190));\n }\n if (3 !== a.tag) throw Error(formatProdErrorMessage(188));\n return a.stateNode.current === a ? fiber : alternate;\n}\nfunction findCurrentHostFiberImpl(node) {\n var tag = node.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return node;\n for (node = node.child; null !== node; ) {\n tag = findCurrentHostFiberImpl(node);\n if (null !== tag) return tag;\n node = node.sibling;\n }\n return null;\n}\nvar assign = Object.assign,\n REACT_LEGACY_ELEMENT_TYPE = Symbol.for(\"react.element\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\nSymbol.for(\"react.scope\");\nvar REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\");\nSymbol.for(\"react.legacy_hidden\");\nSymbol.for(\"react.tracing_marker\");\nvar REACT_MEMO_CACHE_SENTINEL = Symbol.for(\"react.memo_cache_sentinel\");\nSymbol.for(\"react.view_transition\");\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\");\nfunction getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (type.$$typeof) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n}\nvar isArrayImpl = Array.isArray,\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n ReactDOMSharedInternals =\n ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n sharedNotPendingObject = {\n pending: !1,\n data: null,\n method: null,\n action: null\n },\n valueStack = [],\n index = -1;\nfunction createCursor(defaultValue) {\n return { current: defaultValue };\n}\nfunction pop(cursor) {\n 0 > index ||\n ((cursor.current = valueStack[index]), (valueStack[index] = null), index--);\n}\nfunction push(cursor, value) {\n index++;\n valueStack[index] = cursor.current;\n cursor.current = value;\n}\nvar contextStackCursor = createCursor(null),\n contextFiberStackCursor = createCursor(null),\n rootInstanceStackCursor = createCursor(null),\n hostTransitionProviderCursor = createCursor(null);\nfunction pushHostContainer(fiber, nextRootInstance) {\n push(rootInstanceStackCursor, nextRootInstance);\n push(contextFiberStackCursor, fiber);\n push(contextStackCursor, null);\n switch (nextRootInstance.nodeType) {\n case 9:\n case 11:\n fiber = (fiber = nextRootInstance.documentElement)\n ? (fiber = fiber.namespaceURI)\n ? getOwnHostContext(fiber)\n : 0\n : 0;\n break;\n default:\n if (\n ((fiber = nextRootInstance.tagName),\n (nextRootInstance = nextRootInstance.namespaceURI))\n )\n (nextRootInstance = getOwnHostContext(nextRootInstance)),\n (fiber = getChildHostContextProd(nextRootInstance, fiber));\n else\n switch (fiber) {\n case \"svg\":\n fiber = 1;\n break;\n case \"math\":\n fiber = 2;\n break;\n default:\n fiber = 0;\n }\n }\n pop(contextStackCursor);\n push(contextStackCursor, fiber);\n}\nfunction popHostContainer() {\n pop(contextStackCursor);\n pop(contextFiberStackCursor);\n pop(rootInstanceStackCursor);\n}\nfunction pushHostContext(fiber) {\n null !== fiber.memoizedState && push(hostTransitionProviderCursor, fiber);\n var context = contextStackCursor.current;\n var JSCompiler_inline_result = getChildHostContextProd(context, fiber.type);\n context !== JSCompiler_inline_result &&\n (push(contextFiberStackCursor, fiber),\n push(contextStackCursor, JSCompiler_inline_result));\n}\nfunction popHostContext(fiber) {\n contextFiberStackCursor.current === fiber &&\n (pop(contextStackCursor), pop(contextFiberStackCursor));\n hostTransitionProviderCursor.current === fiber &&\n (pop(hostTransitionProviderCursor),\n (HostTransitionContext._currentValue = sharedNotPendingObject));\n}\nvar prefix, suffix;\nfunction describeBuiltInComponentFrame(name) {\n if (void 0 === prefix)\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = (match && match[1]) || \"\";\n suffix =\n -1 < x.stack.indexOf(\"\\n at\")\n ? \" ()\"\n : -1 < x.stack.indexOf(\"@\")\n ? \"@unknown:0:0\"\n : \"\";\n }\n return \"\\n\" + prefix + name + suffix;\n}\nvar reentry = !1;\nfunction describeNativeComponentFrame(fn, construct) {\n if (!fn || reentry) return \"\";\n reentry = !0;\n var previousPrepareStackTrace = Error.prepareStackTrace;\n Error.prepareStackTrace = void 0;\n try {\n var RunInRootFrame = {\n DetermineComponentFrameRoot: function () {\n try {\n if (construct) {\n var Fake = function () {\n throw Error();\n };\n Object.defineProperty(Fake.prototype, \"props\", {\n set: function () {\n throw Error();\n }\n });\n if (\"object\" === typeof Reflect && Reflect.construct) {\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n var control = x;\n }\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x$1) {\n control = x$1;\n }\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x$2) {\n control = x$2;\n }\n (Fake = fn()) &&\n \"function\" === typeof Fake.catch &&\n Fake.catch(function () {});\n }\n } catch (sample) {\n if (sample && control && \"string\" === typeof sample.stack)\n return [sample.stack, control.stack];\n }\n return [null, null];\n }\n };\n RunInRootFrame.DetermineComponentFrameRoot.displayName =\n \"DetermineComponentFrameRoot\";\n var namePropDescriptor = Object.getOwnPropertyDescriptor(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\"\n );\n namePropDescriptor &&\n namePropDescriptor.configurable &&\n Object.defineProperty(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\",\n { value: \"DetermineComponentFrameRoot\" }\n );\n var _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(),\n sampleStack = _RunInRootFrame$Deter[0],\n controlStack = _RunInRootFrame$Deter[1];\n if (sampleStack && controlStack) {\n var sampleLines = sampleStack.split(\"\\n\"),\n controlLines = controlStack.split(\"\\n\");\n for (\n namePropDescriptor = RunInRootFrame = 0;\n RunInRootFrame < sampleLines.length &&\n !sampleLines[RunInRootFrame].includes(\"DetermineComponentFrameRoot\");\n\n )\n RunInRootFrame++;\n for (\n ;\n namePropDescriptor < controlLines.length &&\n !controlLines[namePropDescriptor].includes(\n \"DetermineComponentFrameRoot\"\n );\n\n )\n namePropDescriptor++;\n if (\n RunInRootFrame === sampleLines.length ||\n namePropDescriptor === controlLines.length\n )\n for (\n RunInRootFrame = sampleLines.length - 1,\n namePropDescriptor = controlLines.length - 1;\n 1 <= RunInRootFrame &&\n 0 <= namePropDescriptor &&\n sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor];\n\n )\n namePropDescriptor--;\n for (\n ;\n 1 <= RunInRootFrame && 0 <= namePropDescriptor;\n RunInRootFrame--, namePropDescriptor--\n )\n if (sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {\n if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {\n do\n if (\n (RunInRootFrame--,\n namePropDescriptor--,\n 0 > namePropDescriptor ||\n sampleLines[RunInRootFrame] !==\n controlLines[namePropDescriptor])\n ) {\n var frame =\n \"\\n\" +\n sampleLines[RunInRootFrame].replace(\" at new \", \" at \");\n fn.displayName &&\n frame.includes(\"\") &&\n (frame = frame.replace(\"\", fn.displayName));\n return frame;\n }\n while (1 <= RunInRootFrame && 0 <= namePropDescriptor);\n }\n break;\n }\n }\n } finally {\n (reentry = !1), (Error.prepareStackTrace = previousPrepareStackTrace);\n }\n return (previousPrepareStackTrace = fn ? fn.displayName || fn.name : \"\")\n ? describeBuiltInComponentFrame(previousPrepareStackTrace)\n : \"\";\n}\nfunction describeFiber(fiber, childFiber) {\n switch (fiber.tag) {\n case 26:\n case 27:\n case 5:\n return describeBuiltInComponentFrame(fiber.type);\n case 16:\n return describeBuiltInComponentFrame(\"Lazy\");\n case 13:\n return fiber.child !== childFiber && null !== childFiber\n ? describeBuiltInComponentFrame(\"Suspense Fallback\")\n : describeBuiltInComponentFrame(\"Suspense\");\n case 19:\n return describeBuiltInComponentFrame(\"SuspenseList\");\n case 0:\n case 15:\n return describeNativeComponentFrame(fiber.type, !1);\n case 11:\n return describeNativeComponentFrame(fiber.type.render, !1);\n case 1:\n return describeNativeComponentFrame(fiber.type, !0);\n case 31:\n return describeBuiltInComponentFrame(\"Activity\");\n default:\n return \"\";\n }\n}\nfunction getStackByFiberInDevAndProd(workInProgress) {\n try {\n var info = \"\",\n previous = null;\n do\n (info += describeFiber(workInProgress, previous)),\n (previous = workInProgress),\n (workInProgress = workInProgress.return);\n while (workInProgress);\n return info;\n } catch (x) {\n return \"\\nError generating stack: \" + x.message + \"\\n\" + x.stack;\n }\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty,\n scheduleCallback$3 = Scheduler.unstable_scheduleCallback,\n cancelCallback$1 = Scheduler.unstable_cancelCallback,\n shouldYield = Scheduler.unstable_shouldYield,\n requestPaint = Scheduler.unstable_requestPaint,\n now = Scheduler.unstable_now,\n getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel,\n ImmediatePriority = Scheduler.unstable_ImmediatePriority,\n UserBlockingPriority = Scheduler.unstable_UserBlockingPriority,\n NormalPriority$1 = Scheduler.unstable_NormalPriority,\n LowPriority = Scheduler.unstable_LowPriority,\n IdlePriority = Scheduler.unstable_IdlePriority,\n log$1 = Scheduler.log,\n unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue,\n rendererID = null,\n injectedHook = null;\nfunction setIsStrictModeForDevtools(newIsStrictMode) {\n \"function\" === typeof log$1 && unstable_setDisableYieldValue(newIsStrictMode);\n if (injectedHook && \"function\" === typeof injectedHook.setStrictMode)\n try {\n injectedHook.setStrictMode(rendererID, newIsStrictMode);\n } catch (err) {}\n}\nvar clz32 = Math.clz32 ? Math.clz32 : clz32Fallback,\n log = Math.log,\n LN2 = Math.LN2;\nfunction clz32Fallback(x) {\n x >>>= 0;\n return 0 === x ? 32 : (31 - ((log(x) / LN2) | 0)) | 0;\n}\nvar nextTransitionUpdateLane = 256,\n nextTransitionDeferredLane = 262144,\n nextRetryLane = 4194304;\nfunction getHighestPriorityLanes(lanes) {\n var pendingSyncLanes = lanes & 42;\n if (0 !== pendingSyncLanes) return pendingSyncLanes;\n switch (lanes & -lanes) {\n case 1:\n return 1;\n case 2:\n return 2;\n case 4:\n return 4;\n case 8:\n return 8;\n case 16:\n return 16;\n case 32:\n return 32;\n case 64:\n return 64;\n case 128:\n return 128;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n return lanes & 261888;\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return lanes & 3932160;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return lanes & 62914560;\n case 67108864:\n return 67108864;\n case 134217728:\n return 134217728;\n case 268435456:\n return 268435456;\n case 536870912:\n return 536870912;\n case 1073741824:\n return 0;\n default:\n return lanes;\n }\n}\nfunction getNextLanes(root, wipLanes, rootHasPendingCommit) {\n var pendingLanes = root.pendingLanes;\n if (0 === pendingLanes) return 0;\n var nextLanes = 0,\n suspendedLanes = root.suspendedLanes,\n pingedLanes = root.pingedLanes;\n root = root.warmLanes;\n var nonIdlePendingLanes = pendingLanes & 134217727;\n 0 !== nonIdlePendingLanes\n ? ((pendingLanes = nonIdlePendingLanes & ~suspendedLanes),\n 0 !== pendingLanes\n ? (nextLanes = getHighestPriorityLanes(pendingLanes))\n : ((pingedLanes &= nonIdlePendingLanes),\n 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = nonIdlePendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit)))))\n : ((nonIdlePendingLanes = pendingLanes & ~suspendedLanes),\n 0 !== nonIdlePendingLanes\n ? (nextLanes = getHighestPriorityLanes(nonIdlePendingLanes))\n : 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = pendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit))));\n return 0 === nextLanes\n ? 0\n : 0 !== wipLanes &&\n wipLanes !== nextLanes &&\n 0 === (wipLanes & suspendedLanes) &&\n ((suspendedLanes = nextLanes & -nextLanes),\n (rootHasPendingCommit = wipLanes & -wipLanes),\n suspendedLanes >= rootHasPendingCommit ||\n (32 === suspendedLanes && 0 !== (rootHasPendingCommit & 4194048)))\n ? wipLanes\n : nextLanes;\n}\nfunction checkIfRootIsPrerendering(root, renderLanes) {\n return (\n 0 ===\n (root.pendingLanes &\n ~(root.suspendedLanes & ~root.pingedLanes) &\n renderLanes)\n );\n}\nfunction computeExpirationTime(lane, currentTime) {\n switch (lane) {\n case 1:\n case 2:\n case 4:\n case 8:\n case 64:\n return currentTime + 250;\n case 16:\n case 32:\n case 128:\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return currentTime + 5e3;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return -1;\n case 67108864:\n case 134217728:\n case 268435456:\n case 536870912:\n case 1073741824:\n return -1;\n default:\n return -1;\n }\n}\nfunction claimNextRetryLane() {\n var lane = nextRetryLane;\n nextRetryLane <<= 1;\n 0 === (nextRetryLane & 62914560) && (nextRetryLane = 4194304);\n return lane;\n}\nfunction createLaneMap(initial) {\n for (var laneMap = [], i = 0; 31 > i; i++) laneMap.push(initial);\n return laneMap;\n}\nfunction markRootUpdated$1(root, updateLane) {\n root.pendingLanes |= updateLane;\n 268435456 !== updateLane &&\n ((root.suspendedLanes = 0), (root.pingedLanes = 0), (root.warmLanes = 0));\n}\nfunction markRootFinished(\n root,\n finishedLanes,\n remainingLanes,\n spawnedLane,\n updatedLanes,\n suspendedRetryLanes\n) {\n var previouslyPendingLanes = root.pendingLanes;\n root.pendingLanes = remainingLanes;\n root.suspendedLanes = 0;\n root.pingedLanes = 0;\n root.warmLanes = 0;\n root.expiredLanes &= remainingLanes;\n root.entangledLanes &= remainingLanes;\n root.errorRecoveryDisabledLanes &= remainingLanes;\n root.shellSuspendCounter = 0;\n var entanglements = root.entanglements,\n expirationTimes = root.expirationTimes,\n hiddenUpdates = root.hiddenUpdates;\n for (\n remainingLanes = previouslyPendingLanes & ~remainingLanes;\n 0 < remainingLanes;\n\n ) {\n var index$7 = 31 - clz32(remainingLanes),\n lane = 1 << index$7;\n entanglements[index$7] = 0;\n expirationTimes[index$7] = -1;\n var hiddenUpdatesForLane = hiddenUpdates[index$7];\n if (null !== hiddenUpdatesForLane)\n for (\n hiddenUpdates[index$7] = null, index$7 = 0;\n index$7 < hiddenUpdatesForLane.length;\n index$7++\n ) {\n var update = hiddenUpdatesForLane[index$7];\n null !== update && (update.lane &= -536870913);\n }\n remainingLanes &= ~lane;\n }\n 0 !== spawnedLane && markSpawnedDeferredLane(root, spawnedLane, 0);\n 0 !== suspendedRetryLanes &&\n 0 === updatedLanes &&\n 0 !== root.tag &&\n (root.suspendedLanes |=\n suspendedRetryLanes & ~(previouslyPendingLanes & ~finishedLanes));\n}\nfunction markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {\n root.pendingLanes |= spawnedLane;\n root.suspendedLanes &= ~spawnedLane;\n var spawnedLaneIndex = 31 - clz32(spawnedLane);\n root.entangledLanes |= spawnedLane;\n root.entanglements[spawnedLaneIndex] =\n root.entanglements[spawnedLaneIndex] |\n 1073741824 |\n (entangledLanes & 261930);\n}\nfunction markRootEntangled(root, entangledLanes) {\n var rootEntangledLanes = (root.entangledLanes |= entangledLanes);\n for (root = root.entanglements; rootEntangledLanes; ) {\n var index$8 = 31 - clz32(rootEntangledLanes),\n lane = 1 << index$8;\n (lane & entangledLanes) | (root[index$8] & entangledLanes) &&\n (root[index$8] |= entangledLanes);\n rootEntangledLanes &= ~lane;\n }\n}\nfunction getBumpedLaneForHydration(root, renderLanes) {\n var renderLane = renderLanes & -renderLanes;\n renderLane =\n 0 !== (renderLane & 42) ? 1 : getBumpedLaneForHydrationByLane(renderLane);\n return 0 !== (renderLane & (root.suspendedLanes | renderLanes))\n ? 0\n : renderLane;\n}\nfunction getBumpedLaneForHydrationByLane(lane) {\n switch (lane) {\n case 2:\n lane = 1;\n break;\n case 8:\n lane = 4;\n break;\n case 32:\n lane = 16;\n break;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n lane = 128;\n break;\n case 268435456:\n lane = 134217728;\n break;\n default:\n lane = 0;\n }\n return lane;\n}\nfunction lanesToEventPriority(lanes) {\n lanes &= -lanes;\n return 2 < lanes\n ? 8 < lanes\n ? 0 !== (lanes & 134217727)\n ? 32\n : 268435456\n : 8\n : 2;\n}\nfunction resolveUpdatePriority() {\n var updatePriority = ReactDOMSharedInternals.p;\n if (0 !== updatePriority) return updatePriority;\n updatePriority = window.event;\n return void 0 === updatePriority ? 32 : getEventPriority(updatePriority.type);\n}\nfunction runWithPriority(priority, fn) {\n var previousPriority = ReactDOMSharedInternals.p;\n try {\n return (ReactDOMSharedInternals.p = priority), fn();\n } finally {\n ReactDOMSharedInternals.p = previousPriority;\n }\n}\nvar randomKey = Math.random().toString(36).slice(2),\n internalInstanceKey = \"__reactFiber$\" + randomKey,\n internalPropsKey = \"__reactProps$\" + randomKey,\n internalContainerInstanceKey = \"__reactContainer$\" + randomKey,\n internalEventHandlersKey = \"__reactEvents$\" + randomKey,\n internalEventHandlerListenersKey = \"__reactListeners$\" + randomKey,\n internalEventHandlesSetKey = \"__reactHandles$\" + randomKey,\n internalRootNodeResourcesKey = \"__reactResources$\" + randomKey,\n internalHoistableMarker = \"__reactMarker$\" + randomKey;\nfunction detachDeletedInstance(node) {\n delete node[internalInstanceKey];\n delete node[internalPropsKey];\n delete node[internalEventHandlersKey];\n delete node[internalEventHandlerListenersKey];\n delete node[internalEventHandlesSetKey];\n}\nfunction getClosestInstanceFromNode(targetNode) {\n var targetInst = targetNode[internalInstanceKey];\n if (targetInst) return targetInst;\n for (var parentNode = targetNode.parentNode; parentNode; ) {\n if (\n (targetInst =\n parentNode[internalContainerInstanceKey] ||\n parentNode[internalInstanceKey])\n ) {\n parentNode = targetInst.alternate;\n if (\n null !== targetInst.child ||\n (null !== parentNode && null !== parentNode.child)\n )\n for (\n targetNode = getParentHydrationBoundary(targetNode);\n null !== targetNode;\n\n ) {\n if ((parentNode = targetNode[internalInstanceKey])) return parentNode;\n targetNode = getParentHydrationBoundary(targetNode);\n }\n return targetInst;\n }\n targetNode = parentNode;\n parentNode = targetNode.parentNode;\n }\n return null;\n}\nfunction getInstanceFromNode(node) {\n if (\n (node = node[internalInstanceKey] || node[internalContainerInstanceKey])\n ) {\n var tag = node.tag;\n if (\n 5 === tag ||\n 6 === tag ||\n 13 === tag ||\n 31 === tag ||\n 26 === tag ||\n 27 === tag ||\n 3 === tag\n )\n return node;\n }\n return null;\n}\nfunction getNodeFromInstance(inst) {\n var tag = inst.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return inst.stateNode;\n throw Error(formatProdErrorMessage(33));\n}\nfunction getResourcesFromRoot(root) {\n var resources = root[internalRootNodeResourcesKey];\n resources ||\n (resources = root[internalRootNodeResourcesKey] =\n { hoistableStyles: new Map(), hoistableScripts: new Map() });\n return resources;\n}\nfunction markNodeAsHoistable(node) {\n node[internalHoistableMarker] = !0;\n}\nvar allNativeEvents = new Set(),\n registrationNameDependencies = {};\nfunction registerTwoPhaseEvent(registrationName, dependencies) {\n registerDirectEvent(registrationName, dependencies);\n registerDirectEvent(registrationName + \"Capture\", dependencies);\n}\nfunction registerDirectEvent(registrationName, dependencies) {\n registrationNameDependencies[registrationName] = dependencies;\n for (\n registrationName = 0;\n registrationName < dependencies.length;\n registrationName++\n )\n allNativeEvents.add(dependencies[registrationName]);\n}\nvar VALID_ATTRIBUTE_NAME_REGEX = RegExp(\n \"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"\n ),\n illegalAttributeNameCache = {},\n validatedAttributeNameCache = {};\nfunction isAttributeNameSafe(attributeName) {\n if (hasOwnProperty.call(validatedAttributeNameCache, attributeName))\n return !0;\n if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) return !1;\n if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName))\n return (validatedAttributeNameCache[attributeName] = !0);\n illegalAttributeNameCache[attributeName] = !0;\n return !1;\n}\nfunction setValueForAttribute(node, name, value) {\n if (isAttributeNameSafe(name))\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n node.removeAttribute(name);\n return;\n case \"boolean\":\n var prefix$10 = name.toLowerCase().slice(0, 5);\n if (\"data-\" !== prefix$10 && \"aria-\" !== prefix$10) {\n node.removeAttribute(name);\n return;\n }\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForKnownAttribute(node, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForNamespacedAttribute(node, namespace, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttributeNS(namespace, name, \"\" + value);\n }\n}\nfunction getToStringValue(value) {\n switch (typeof value) {\n case \"bigint\":\n case \"boolean\":\n case \"number\":\n case \"string\":\n case \"undefined\":\n return value;\n case \"object\":\n return value;\n default:\n return \"\";\n }\n}\nfunction isCheckable(elem) {\n var type = elem.type;\n return (\n (elem = elem.nodeName) &&\n \"input\" === elem.toLowerCase() &&\n (\"checkbox\" === type || \"radio\" === type)\n );\n}\nfunction trackValueOnNode(node, valueField, currentValue) {\n var descriptor = Object.getOwnPropertyDescriptor(\n node.constructor.prototype,\n valueField\n );\n if (\n !node.hasOwnProperty(valueField) &&\n \"undefined\" !== typeof descriptor &&\n \"function\" === typeof descriptor.get &&\n \"function\" === typeof descriptor.set\n ) {\n var get = descriptor.get,\n set = descriptor.set;\n Object.defineProperty(node, valueField, {\n configurable: !0,\n get: function () {\n return get.call(this);\n },\n set: function (value) {\n currentValue = \"\" + value;\n set.call(this, value);\n }\n });\n Object.defineProperty(node, valueField, {\n enumerable: descriptor.enumerable\n });\n return {\n getValue: function () {\n return currentValue;\n },\n setValue: function (value) {\n currentValue = \"\" + value;\n },\n stopTracking: function () {\n node._valueTracker = null;\n delete node[valueField];\n }\n };\n }\n}\nfunction track(node) {\n if (!node._valueTracker) {\n var valueField = isCheckable(node) ? \"checked\" : \"value\";\n node._valueTracker = trackValueOnNode(\n node,\n valueField,\n \"\" + node[valueField]\n );\n }\n}\nfunction updateValueIfChanged(node) {\n if (!node) return !1;\n var tracker = node._valueTracker;\n if (!tracker) return !0;\n var lastValue = tracker.getValue();\n var value = \"\";\n node &&\n (value = isCheckable(node)\n ? node.checked\n ? \"true\"\n : \"false\"\n : node.value);\n node = value;\n return node !== lastValue ? (tracker.setValue(node), !0) : !1;\n}\nfunction getActiveElement(doc) {\n doc = doc || (\"undefined\" !== typeof document ? document : void 0);\n if (\"undefined\" === typeof doc) return null;\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\nvar escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\\n\"\\\\]/g;\nfunction escapeSelectorAttributeValueInsideDoubleQuotes(value) {\n return value.replace(\n escapeSelectorAttributeValueInsideDoubleQuotesRegex,\n function (ch) {\n return \"\\\\\" + ch.charCodeAt(0).toString(16) + \" \";\n }\n );\n}\nfunction updateInput(\n element,\n value,\n defaultValue,\n lastDefaultValue,\n checked,\n defaultChecked,\n type,\n name\n) {\n element.name = \"\";\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type\n ? (element.type = type)\n : element.removeAttribute(\"type\");\n if (null != value)\n if (\"number\" === type) {\n if ((0 === value && \"\" === element.value) || element.value != value)\n element.value = \"\" + getToStringValue(value);\n } else\n element.value !== \"\" + getToStringValue(value) &&\n (element.value = \"\" + getToStringValue(value));\n else\n (\"submit\" !== type && \"reset\" !== type) || element.removeAttribute(\"value\");\n null != value\n ? setDefaultValue(element, type, getToStringValue(value))\n : null != defaultValue\n ? setDefaultValue(element, type, getToStringValue(defaultValue))\n : null != lastDefaultValue && element.removeAttribute(\"value\");\n null == checked &&\n null != defaultChecked &&\n (element.defaultChecked = !!defaultChecked);\n null != checked &&\n (element.checked =\n checked && \"function\" !== typeof checked && \"symbol\" !== typeof checked);\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name\n ? (element.name = \"\" + getToStringValue(name))\n : element.removeAttribute(\"name\");\n}\nfunction initInput(\n element,\n value,\n defaultValue,\n checked,\n defaultChecked,\n type,\n name,\n isHydrating\n) {\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type &&\n (element.type = type);\n if (null != value || null != defaultValue) {\n if (\n !(\n (\"submit\" !== type && \"reset\" !== type) ||\n (void 0 !== value && null !== value)\n )\n ) {\n track(element);\n return;\n }\n defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n value = null != value ? \"\" + getToStringValue(value) : defaultValue;\n isHydrating || value === element.value || (element.value = value);\n element.defaultValue = value;\n }\n checked = null != checked ? checked : defaultChecked;\n checked =\n \"function\" !== typeof checked && \"symbol\" !== typeof checked && !!checked;\n element.checked = isHydrating ? element.checked : !!checked;\n element.defaultChecked = !!checked;\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name &&\n (element.name = name);\n track(element);\n}\nfunction setDefaultValue(node, type, value) {\n (\"number\" === type && getActiveElement(node.ownerDocument) === node) ||\n node.defaultValue === \"\" + value ||\n (node.defaultValue = \"\" + value);\n}\nfunction updateOptions(node, multiple, propValue, setDefaultSelected) {\n node = node.options;\n if (multiple) {\n multiple = {};\n for (var i = 0; i < propValue.length; i++)\n multiple[\"$\" + propValue[i]] = !0;\n for (propValue = 0; propValue < node.length; propValue++)\n (i = multiple.hasOwnProperty(\"$\" + node[propValue].value)),\n node[propValue].selected !== i && (node[propValue].selected = i),\n i && setDefaultSelected && (node[propValue].defaultSelected = !0);\n } else {\n propValue = \"\" + getToStringValue(propValue);\n multiple = null;\n for (i = 0; i < node.length; i++) {\n if (node[i].value === propValue) {\n node[i].selected = !0;\n setDefaultSelected && (node[i].defaultSelected = !0);\n return;\n }\n null !== multiple || node[i].disabled || (multiple = node[i]);\n }\n null !== multiple && (multiple.selected = !0);\n }\n}\nfunction updateTextarea(element, value, defaultValue) {\n if (\n null != value &&\n ((value = \"\" + getToStringValue(value)),\n value !== element.value && (element.value = value),\n null == defaultValue)\n ) {\n element.defaultValue !== value && (element.defaultValue = value);\n return;\n }\n element.defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n}\nfunction initTextarea(element, value, defaultValue, children) {\n if (null == value) {\n if (null != children) {\n if (null != defaultValue) throw Error(formatProdErrorMessage(92));\n if (isArrayImpl(children)) {\n if (1 < children.length) throw Error(formatProdErrorMessage(93));\n children = children[0];\n }\n defaultValue = children;\n }\n null == defaultValue && (defaultValue = \"\");\n value = defaultValue;\n }\n defaultValue = getToStringValue(value);\n element.defaultValue = defaultValue;\n children = element.textContent;\n children === defaultValue &&\n \"\" !== children &&\n null !== children &&\n (element.value = children);\n track(element);\n}\nfunction setTextContent(node, text) {\n if (text) {\n var firstChild = node.firstChild;\n if (\n firstChild &&\n firstChild === node.lastChild &&\n 3 === firstChild.nodeType\n ) {\n firstChild.nodeValue = text;\n return;\n }\n }\n node.textContent = text;\n}\nvar unitlessNumbers = new Set(\n \"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\n \" \"\n )\n);\nfunction setValueForStyle(style, styleName, value) {\n var isCustomProperty = 0 === styleName.indexOf(\"--\");\n null == value || \"boolean\" === typeof value || \"\" === value\n ? isCustomProperty\n ? style.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (style.cssFloat = \"\")\n : (style[styleName] = \"\")\n : isCustomProperty\n ? style.setProperty(styleName, value)\n : \"number\" !== typeof value ||\n 0 === value ||\n unitlessNumbers.has(styleName)\n ? \"float\" === styleName\n ? (style.cssFloat = value)\n : (style[styleName] = (\"\" + value).trim())\n : (style[styleName] = value + \"px\");\n}\nfunction setValueForStyles(node, styles, prevStyles) {\n if (null != styles && \"object\" !== typeof styles)\n throw Error(formatProdErrorMessage(62));\n node = node.style;\n if (null != prevStyles) {\n for (var styleName in prevStyles)\n !prevStyles.hasOwnProperty(styleName) ||\n (null != styles && styles.hasOwnProperty(styleName)) ||\n (0 === styleName.indexOf(\"--\")\n ? node.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (node.cssFloat = \"\")\n : (node[styleName] = \"\"));\n for (var styleName$16 in styles)\n (styleName = styles[styleName$16]),\n styles.hasOwnProperty(styleName$16) &&\n prevStyles[styleName$16] !== styleName &&\n setValueForStyle(node, styleName$16, styleName);\n } else\n for (var styleName$17 in styles)\n styles.hasOwnProperty(styleName$17) &&\n setValueForStyle(node, styleName$17, styles[styleName$17]);\n}\nfunction isCustomElement(tagName) {\n if (-1 === tagName.indexOf(\"-\")) return !1;\n switch (tagName) {\n case \"annotation-xml\":\n case \"color-profile\":\n case \"font-face\":\n case \"font-face-src\":\n case \"font-face-uri\":\n case \"font-face-format\":\n case \"font-face-name\":\n case \"missing-glyph\":\n return !1;\n default:\n return !0;\n }\n}\nvar aliases = new Map([\n [\"acceptCharset\", \"accept-charset\"],\n [\"htmlFor\", \"for\"],\n [\"httpEquiv\", \"http-equiv\"],\n [\"crossOrigin\", \"crossorigin\"],\n [\"accentHeight\", \"accent-height\"],\n [\"alignmentBaseline\", \"alignment-baseline\"],\n [\"arabicForm\", \"arabic-form\"],\n [\"baselineShift\", \"baseline-shift\"],\n [\"capHeight\", \"cap-height\"],\n [\"clipPath\", \"clip-path\"],\n [\"clipRule\", \"clip-rule\"],\n [\"colorInterpolation\", \"color-interpolation\"],\n [\"colorInterpolationFilters\", \"color-interpolation-filters\"],\n [\"colorProfile\", \"color-profile\"],\n [\"colorRendering\", \"color-rendering\"],\n [\"dominantBaseline\", \"dominant-baseline\"],\n [\"enableBackground\", \"enable-background\"],\n [\"fillOpacity\", \"fill-opacity\"],\n [\"fillRule\", \"fill-rule\"],\n [\"floodColor\", \"flood-color\"],\n [\"floodOpacity\", \"flood-opacity\"],\n [\"fontFamily\", \"font-family\"],\n [\"fontSize\", \"font-size\"],\n [\"fontSizeAdjust\", \"font-size-adjust\"],\n [\"fontStretch\", \"font-stretch\"],\n [\"fontStyle\", \"font-style\"],\n [\"fontVariant\", \"font-variant\"],\n [\"fontWeight\", \"font-weight\"],\n [\"glyphName\", \"glyph-name\"],\n [\"glyphOrientationHorizontal\", \"glyph-orientation-horizontal\"],\n [\"glyphOrientationVertical\", \"glyph-orientation-vertical\"],\n [\"horizAdvX\", \"horiz-adv-x\"],\n [\"horizOriginX\", \"horiz-origin-x\"],\n [\"imageRendering\", \"image-rendering\"],\n [\"letterSpacing\", \"letter-spacing\"],\n [\"lightingColor\", \"lighting-color\"],\n [\"markerEnd\", \"marker-end\"],\n [\"markerMid\", \"marker-mid\"],\n [\"markerStart\", \"marker-start\"],\n [\"overlinePosition\", \"overline-position\"],\n [\"overlineThickness\", \"overline-thickness\"],\n [\"paintOrder\", \"paint-order\"],\n [\"panose-1\", \"panose-1\"],\n [\"pointerEvents\", \"pointer-events\"],\n [\"renderingIntent\", \"rendering-intent\"],\n [\"shapeRendering\", \"shape-rendering\"],\n [\"stopColor\", \"stop-color\"],\n [\"stopOpacity\", \"stop-opacity\"],\n [\"strikethroughPosition\", \"strikethrough-position\"],\n [\"strikethroughThickness\", \"strikethrough-thickness\"],\n [\"strokeDasharray\", \"stroke-dasharray\"],\n [\"strokeDashoffset\", \"stroke-dashoffset\"],\n [\"strokeLinecap\", \"stroke-linecap\"],\n [\"strokeLinejoin\", \"stroke-linejoin\"],\n [\"strokeMiterlimit\", \"stroke-miterlimit\"],\n [\"strokeOpacity\", \"stroke-opacity\"],\n [\"strokeWidth\", \"stroke-width\"],\n [\"textAnchor\", \"text-anchor\"],\n [\"textDecoration\", \"text-decoration\"],\n [\"textRendering\", \"text-rendering\"],\n [\"transformOrigin\", \"transform-origin\"],\n [\"underlinePosition\", \"underline-position\"],\n [\"underlineThickness\", \"underline-thickness\"],\n [\"unicodeBidi\", \"unicode-bidi\"],\n [\"unicodeRange\", \"unicode-range\"],\n [\"unitsPerEm\", \"units-per-em\"],\n [\"vAlphabetic\", \"v-alphabetic\"],\n [\"vHanging\", \"v-hanging\"],\n [\"vIdeographic\", \"v-ideographic\"],\n [\"vMathematical\", \"v-mathematical\"],\n [\"vectorEffect\", \"vector-effect\"],\n [\"vertAdvY\", \"vert-adv-y\"],\n [\"vertOriginX\", \"vert-origin-x\"],\n [\"vertOriginY\", \"vert-origin-y\"],\n [\"wordSpacing\", \"word-spacing\"],\n [\"writingMode\", \"writing-mode\"],\n [\"xmlnsXlink\", \"xmlns:xlink\"],\n [\"xHeight\", \"x-height\"]\n ]),\n isJavaScriptProtocol =\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;\nfunction sanitizeURL(url) {\n return isJavaScriptProtocol.test(\"\" + url)\n ? \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\"\n : url;\n}\nfunction noop$1() {}\nvar currentReplayingEvent = null;\nfunction getEventTarget(nativeEvent) {\n nativeEvent = nativeEvent.target || nativeEvent.srcElement || window;\n nativeEvent.correspondingUseElement &&\n (nativeEvent = nativeEvent.correspondingUseElement);\n return 3 === nativeEvent.nodeType ? nativeEvent.parentNode : nativeEvent;\n}\nvar restoreTarget = null,\n restoreQueue = null;\nfunction restoreStateOfTarget(target) {\n var internalInstance = getInstanceFromNode(target);\n if (internalInstance && (target = internalInstance.stateNode)) {\n var props = target[internalPropsKey] || null;\n a: switch (((target = internalInstance.stateNode), internalInstance.type)) {\n case \"input\":\n updateInput(\n target,\n props.value,\n props.defaultValue,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name\n );\n internalInstance = props.name;\n if (\"radio\" === props.type && null != internalInstance) {\n for (props = target; props.parentNode; ) props = props.parentNode;\n props = props.querySelectorAll(\n 'input[name=\"' +\n escapeSelectorAttributeValueInsideDoubleQuotes(\n \"\" + internalInstance\n ) +\n '\"][type=\"radio\"]'\n );\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n ) {\n var otherNode = props[internalInstance];\n if (otherNode !== target && otherNode.form === target.form) {\n var otherProps = otherNode[internalPropsKey] || null;\n if (!otherProps) throw Error(formatProdErrorMessage(90));\n updateInput(\n otherNode,\n otherProps.value,\n otherProps.defaultValue,\n otherProps.defaultValue,\n otherProps.checked,\n otherProps.defaultChecked,\n otherProps.type,\n otherProps.name\n );\n }\n }\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n )\n (otherNode = props[internalInstance]),\n otherNode.form === target.form && updateValueIfChanged(otherNode);\n }\n break a;\n case \"textarea\":\n updateTextarea(target, props.value, props.defaultValue);\n break a;\n case \"select\":\n (internalInstance = props.value),\n null != internalInstance &&\n updateOptions(target, !!props.multiple, internalInstance, !1);\n }\n }\n}\nvar isInsideEventHandler = !1;\nfunction batchedUpdates$1(fn, a, b) {\n if (isInsideEventHandler) return fn(a, b);\n isInsideEventHandler = !0;\n try {\n var JSCompiler_inline_result = fn(a);\n return JSCompiler_inline_result;\n } finally {\n if (\n ((isInsideEventHandler = !1),\n null !== restoreTarget || null !== restoreQueue)\n )\n if (\n (flushSyncWork$1(),\n restoreTarget &&\n ((a = restoreTarget),\n (fn = restoreQueue),\n (restoreQueue = restoreTarget = null),\n restoreStateOfTarget(a),\n fn))\n )\n for (a = 0; a < fn.length; a++) restoreStateOfTarget(fn[a]);\n }\n}\nfunction getListener(inst, registrationName) {\n var stateNode = inst.stateNode;\n if (null === stateNode) return null;\n var props = stateNode[internalPropsKey] || null;\n if (null === props) return null;\n stateNode = props[registrationName];\n a: switch (registrationName) {\n case \"onClick\":\n case \"onClickCapture\":\n case \"onDoubleClick\":\n case \"onDoubleClickCapture\":\n case \"onMouseDown\":\n case \"onMouseDownCapture\":\n case \"onMouseMove\":\n case \"onMouseMoveCapture\":\n case \"onMouseUp\":\n case \"onMouseUpCapture\":\n case \"onMouseEnter\":\n (props = !props.disabled) ||\n ((inst = inst.type),\n (props = !(\n \"button\" === inst ||\n \"input\" === inst ||\n \"select\" === inst ||\n \"textarea\" === inst\n )));\n inst = !props;\n break a;\n default:\n inst = !1;\n }\n if (inst) return null;\n if (stateNode && \"function\" !== typeof stateNode)\n throw Error(\n formatProdErrorMessage(231, registrationName, typeof stateNode)\n );\n return stateNode;\n}\nvar canUseDOM = !(\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ),\n passiveBrowserEventsSupported = !1;\nif (canUseDOM)\n try {\n var options = {};\n Object.defineProperty(options, \"passive\", {\n get: function () {\n passiveBrowserEventsSupported = !0;\n }\n });\n window.addEventListener(\"test\", options, options);\n window.removeEventListener(\"test\", options, options);\n } catch (e) {\n passiveBrowserEventsSupported = !1;\n }\nvar root = null,\n startText = null,\n fallbackText = null;\nfunction getData() {\n if (fallbackText) return fallbackText;\n var start,\n startValue = startText,\n startLength = startValue.length,\n end,\n endValue = \"value\" in root ? root.value : root.textContent,\n endLength = endValue.length;\n for (\n start = 0;\n start < startLength && startValue[start] === endValue[start];\n start++\n );\n var minEnd = startLength - start;\n for (\n end = 1;\n end <= minEnd &&\n startValue[startLength - end] === endValue[endLength - end];\n end++\n );\n return (fallbackText = endValue.slice(start, 1 < end ? 1 - end : void 0));\n}\nfunction getEventCharCode(nativeEvent) {\n var keyCode = nativeEvent.keyCode;\n \"charCode\" in nativeEvent\n ? ((nativeEvent = nativeEvent.charCode),\n 0 === nativeEvent && 13 === keyCode && (nativeEvent = 13))\n : (nativeEvent = keyCode);\n 10 === nativeEvent && (nativeEvent = 13);\n return 32 <= nativeEvent || 13 === nativeEvent ? nativeEvent : 0;\n}\nfunction functionThatReturnsTrue() {\n return !0;\n}\nfunction functionThatReturnsFalse() {\n return !1;\n}\nfunction createSyntheticEvent(Interface) {\n function SyntheticBaseEvent(\n reactName,\n reactEventType,\n targetInst,\n nativeEvent,\n nativeEventTarget\n ) {\n this._reactName = reactName;\n this._targetInst = targetInst;\n this.type = reactEventType;\n this.nativeEvent = nativeEvent;\n this.target = nativeEventTarget;\n this.currentTarget = null;\n for (var propName in Interface)\n Interface.hasOwnProperty(propName) &&\n ((reactName = Interface[propName]),\n (this[propName] = reactName\n ? reactName(nativeEvent)\n : nativeEvent[propName]));\n this.isDefaultPrevented = (\n null != nativeEvent.defaultPrevented\n ? nativeEvent.defaultPrevented\n : !1 === nativeEvent.returnValue\n )\n ? functionThatReturnsTrue\n : functionThatReturnsFalse;\n this.isPropagationStopped = functionThatReturnsFalse;\n return this;\n }\n assign(SyntheticBaseEvent.prototype, {\n preventDefault: function () {\n this.defaultPrevented = !0;\n var event = this.nativeEvent;\n event &&\n (event.preventDefault\n ? event.preventDefault()\n : \"unknown\" !== typeof event.returnValue && (event.returnValue = !1),\n (this.isDefaultPrevented = functionThatReturnsTrue));\n },\n stopPropagation: function () {\n var event = this.nativeEvent;\n event &&\n (event.stopPropagation\n ? event.stopPropagation()\n : \"unknown\" !== typeof event.cancelBubble &&\n (event.cancelBubble = !0),\n (this.isPropagationStopped = functionThatReturnsTrue));\n },\n persist: function () {},\n isPersistent: functionThatReturnsTrue\n });\n return SyntheticBaseEvent;\n}\nvar EventInterface = {\n eventPhase: 0,\n bubbles: 0,\n cancelable: 0,\n timeStamp: function (event) {\n return event.timeStamp || Date.now();\n },\n defaultPrevented: 0,\n isTrusted: 0\n },\n SyntheticEvent = createSyntheticEvent(EventInterface),\n UIEventInterface = assign({}, EventInterface, { view: 0, detail: 0 }),\n SyntheticUIEvent = createSyntheticEvent(UIEventInterface),\n lastMovementX,\n lastMovementY,\n lastMouseEvent,\n MouseEventInterface = assign({}, UIEventInterface, {\n screenX: 0,\n screenY: 0,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n getModifierState: getEventModifierState,\n button: 0,\n buttons: 0,\n relatedTarget: function (event) {\n return void 0 === event.relatedTarget\n ? event.fromElement === event.srcElement\n ? event.toElement\n : event.fromElement\n : event.relatedTarget;\n },\n movementX: function (event) {\n if (\"movementX\" in event) return event.movementX;\n event !== lastMouseEvent &&\n (lastMouseEvent && \"mousemove\" === event.type\n ? ((lastMovementX = event.screenX - lastMouseEvent.screenX),\n (lastMovementY = event.screenY - lastMouseEvent.screenY))\n : (lastMovementY = lastMovementX = 0),\n (lastMouseEvent = event));\n return lastMovementX;\n },\n movementY: function (event) {\n return \"movementY\" in event ? event.movementY : lastMovementY;\n }\n }),\n SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface),\n DragEventInterface = assign({}, MouseEventInterface, { dataTransfer: 0 }),\n SyntheticDragEvent = createSyntheticEvent(DragEventInterface),\n FocusEventInterface = assign({}, UIEventInterface, { relatedTarget: 0 }),\n SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface),\n AnimationEventInterface = assign({}, EventInterface, {\n animationName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface),\n ClipboardEventInterface = assign({}, EventInterface, {\n clipboardData: function (event) {\n return \"clipboardData\" in event\n ? event.clipboardData\n : window.clipboardData;\n }\n }),\n SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface),\n CompositionEventInterface = assign({}, EventInterface, { data: 0 }),\n SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface),\n normalizeKey = {\n Esc: \"Escape\",\n Spacebar: \" \",\n Left: \"ArrowLeft\",\n Up: \"ArrowUp\",\n Right: \"ArrowRight\",\n Down: \"ArrowDown\",\n Del: \"Delete\",\n Win: \"OS\",\n Menu: \"ContextMenu\",\n Apps: \"ContextMenu\",\n Scroll: \"ScrollLock\",\n MozPrintableKey: \"Unidentified\"\n },\n translateToKey = {\n 8: \"Backspace\",\n 9: \"Tab\",\n 12: \"Clear\",\n 13: \"Enter\",\n 16: \"Shift\",\n 17: \"Control\",\n 18: \"Alt\",\n 19: \"Pause\",\n 20: \"CapsLock\",\n 27: \"Escape\",\n 32: \" \",\n 33: \"PageUp\",\n 34: \"PageDown\",\n 35: \"End\",\n 36: \"Home\",\n 37: \"ArrowLeft\",\n 38: \"ArrowUp\",\n 39: \"ArrowRight\",\n 40: \"ArrowDown\",\n 45: \"Insert\",\n 46: \"Delete\",\n 112: \"F1\",\n 113: \"F2\",\n 114: \"F3\",\n 115: \"F4\",\n 116: \"F5\",\n 117: \"F6\",\n 118: \"F7\",\n 119: \"F8\",\n 120: \"F9\",\n 121: \"F10\",\n 122: \"F11\",\n 123: \"F12\",\n 144: \"NumLock\",\n 145: \"ScrollLock\",\n 224: \"Meta\"\n },\n modifierKeyToProp = {\n Alt: \"altKey\",\n Control: \"ctrlKey\",\n Meta: \"metaKey\",\n Shift: \"shiftKey\"\n };\nfunction modifierStateGetter(keyArg) {\n var nativeEvent = this.nativeEvent;\n return nativeEvent.getModifierState\n ? nativeEvent.getModifierState(keyArg)\n : (keyArg = modifierKeyToProp[keyArg])\n ? !!nativeEvent[keyArg]\n : !1;\n}\nfunction getEventModifierState() {\n return modifierStateGetter;\n}\nvar KeyboardEventInterface = assign({}, UIEventInterface, {\n key: function (nativeEvent) {\n if (nativeEvent.key) {\n var key = normalizeKey[nativeEvent.key] || nativeEvent.key;\n if (\"Unidentified\" !== key) return key;\n }\n return \"keypress\" === nativeEvent.type\n ? ((nativeEvent = getEventCharCode(nativeEvent)),\n 13 === nativeEvent ? \"Enter\" : String.fromCharCode(nativeEvent))\n : \"keydown\" === nativeEvent.type || \"keyup\" === nativeEvent.type\n ? translateToKey[nativeEvent.keyCode] || \"Unidentified\"\n : \"\";\n },\n code: 0,\n location: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n repeat: 0,\n locale: 0,\n getModifierState: getEventModifierState,\n charCode: function (event) {\n return \"keypress\" === event.type ? getEventCharCode(event) : 0;\n },\n keyCode: function (event) {\n return \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n },\n which: function (event) {\n return \"keypress\" === event.type\n ? getEventCharCode(event)\n : \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n }\n }),\n SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface),\n PointerEventInterface = assign({}, MouseEventInterface, {\n pointerId: 0,\n width: 0,\n height: 0,\n pressure: 0,\n tangentialPressure: 0,\n tiltX: 0,\n tiltY: 0,\n twist: 0,\n pointerType: 0,\n isPrimary: 0\n }),\n SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface),\n TouchEventInterface = assign({}, UIEventInterface, {\n touches: 0,\n targetTouches: 0,\n changedTouches: 0,\n altKey: 0,\n metaKey: 0,\n ctrlKey: 0,\n shiftKey: 0,\n getModifierState: getEventModifierState\n }),\n SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface),\n TransitionEventInterface = assign({}, EventInterface, {\n propertyName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface),\n WheelEventInterface = assign({}, MouseEventInterface, {\n deltaX: function (event) {\n return \"deltaX\" in event\n ? event.deltaX\n : \"wheelDeltaX\" in event\n ? -event.wheelDeltaX\n : 0;\n },\n deltaY: function (event) {\n return \"deltaY\" in event\n ? event.deltaY\n : \"wheelDeltaY\" in event\n ? -event.wheelDeltaY\n : \"wheelDelta\" in event\n ? -event.wheelDelta\n : 0;\n },\n deltaZ: 0,\n deltaMode: 0\n }),\n SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface),\n ToggleEventInterface = assign({}, EventInterface, {\n newState: 0,\n oldState: 0\n }),\n SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface),\n END_KEYCODES = [9, 13, 27, 32],\n canUseCompositionEvent = canUseDOM && \"CompositionEvent\" in window,\n documentMode = null;\ncanUseDOM &&\n \"documentMode\" in document &&\n (documentMode = document.documentMode);\nvar canUseTextInputEvent = canUseDOM && \"TextEvent\" in window && !documentMode,\n useFallbackCompositionData =\n canUseDOM &&\n (!canUseCompositionEvent ||\n (documentMode && 8 < documentMode && 11 >= documentMode)),\n SPACEBAR_CHAR = String.fromCharCode(32),\n hasSpaceKeypress = !1;\nfunction isFallbackCompositionEnd(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"keyup\":\n return -1 !== END_KEYCODES.indexOf(nativeEvent.keyCode);\n case \"keydown\":\n return 229 !== nativeEvent.keyCode;\n case \"keypress\":\n case \"mousedown\":\n case \"focusout\":\n return !0;\n default:\n return !1;\n }\n}\nfunction getDataFromCustomEvent(nativeEvent) {\n nativeEvent = nativeEvent.detail;\n return \"object\" === typeof nativeEvent && \"data\" in nativeEvent\n ? nativeEvent.data\n : null;\n}\nvar isComposing = !1;\nfunction getNativeBeforeInputChars(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"compositionend\":\n return getDataFromCustomEvent(nativeEvent);\n case \"keypress\":\n if (32 !== nativeEvent.which) return null;\n hasSpaceKeypress = !0;\n return SPACEBAR_CHAR;\n case \"textInput\":\n return (\n (domEventName = nativeEvent.data),\n domEventName === SPACEBAR_CHAR && hasSpaceKeypress ? null : domEventName\n );\n default:\n return null;\n }\n}\nfunction getFallbackBeforeInputChars(domEventName, nativeEvent) {\n if (isComposing)\n return \"compositionend\" === domEventName ||\n (!canUseCompositionEvent &&\n isFallbackCompositionEnd(domEventName, nativeEvent))\n ? ((domEventName = getData()),\n (fallbackText = startText = root = null),\n (isComposing = !1),\n domEventName)\n : null;\n switch (domEventName) {\n case \"paste\":\n return null;\n case \"keypress\":\n if (\n !(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) ||\n (nativeEvent.ctrlKey && nativeEvent.altKey)\n ) {\n if (nativeEvent.char && 1 < nativeEvent.char.length)\n return nativeEvent.char;\n if (nativeEvent.which) return String.fromCharCode(nativeEvent.which);\n }\n return null;\n case \"compositionend\":\n return useFallbackCompositionData && \"ko\" !== nativeEvent.locale\n ? null\n : nativeEvent.data;\n default:\n return null;\n }\n}\nvar supportedInputTypes = {\n color: !0,\n date: !0,\n datetime: !0,\n \"datetime-local\": !0,\n email: !0,\n month: !0,\n number: !0,\n password: !0,\n range: !0,\n search: !0,\n tel: !0,\n text: !0,\n time: !0,\n url: !0,\n week: !0\n};\nfunction isTextInputElement(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return \"input\" === nodeName\n ? !!supportedInputTypes[elem.type]\n : \"textarea\" === nodeName\n ? !0\n : !1;\n}\nfunction createAndAccumulateChangeEvent(\n dispatchQueue,\n inst,\n nativeEvent,\n target\n) {\n restoreTarget\n ? restoreQueue\n ? restoreQueue.push(target)\n : (restoreQueue = [target])\n : (restoreTarget = target);\n inst = accumulateTwoPhaseListeners(inst, \"onChange\");\n 0 < inst.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onChange\",\n \"change\",\n null,\n nativeEvent,\n target\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: inst }));\n}\nvar activeElement$1 = null,\n activeElementInst$1 = null;\nfunction runEventInBatch(dispatchQueue) {\n processDispatchQueue(dispatchQueue, 0);\n}\nfunction getInstIfValueChanged(targetInst) {\n var targetNode = getNodeFromInstance(targetInst);\n if (updateValueIfChanged(targetNode)) return targetInst;\n}\nfunction getTargetInstForChangeEvent(domEventName, targetInst) {\n if (\"change\" === domEventName) return targetInst;\n}\nvar isInputEventSupported = !1;\nif (canUseDOM) {\n var JSCompiler_inline_result$jscomp$286;\n if (canUseDOM) {\n var isSupported$jscomp$inline_427 = \"oninput\" in document;\n if (!isSupported$jscomp$inline_427) {\n var element$jscomp$inline_428 = document.createElement(\"div\");\n element$jscomp$inline_428.setAttribute(\"oninput\", \"return;\");\n isSupported$jscomp$inline_427 =\n \"function\" === typeof element$jscomp$inline_428.oninput;\n }\n JSCompiler_inline_result$jscomp$286 = isSupported$jscomp$inline_427;\n } else JSCompiler_inline_result$jscomp$286 = !1;\n isInputEventSupported =\n JSCompiler_inline_result$jscomp$286 &&\n (!document.documentMode || 9 < document.documentMode);\n}\nfunction stopWatchingForValueChange() {\n activeElement$1 &&\n (activeElement$1.detachEvent(\"onpropertychange\", handlePropertyChange),\n (activeElementInst$1 = activeElement$1 = null));\n}\nfunction handlePropertyChange(nativeEvent) {\n if (\n \"value\" === nativeEvent.propertyName &&\n getInstIfValueChanged(activeElementInst$1)\n ) {\n var dispatchQueue = [];\n createAndAccumulateChangeEvent(\n dispatchQueue,\n activeElementInst$1,\n nativeEvent,\n getEventTarget(nativeEvent)\n );\n batchedUpdates$1(runEventInBatch, dispatchQueue);\n }\n}\nfunction handleEventsForInputEventPolyfill(domEventName, target, targetInst) {\n \"focusin\" === domEventName\n ? (stopWatchingForValueChange(),\n (activeElement$1 = target),\n (activeElementInst$1 = targetInst),\n activeElement$1.attachEvent(\"onpropertychange\", handlePropertyChange))\n : \"focusout\" === domEventName && stopWatchingForValueChange();\n}\nfunction getTargetInstForInputEventPolyfill(domEventName) {\n if (\n \"selectionchange\" === domEventName ||\n \"keyup\" === domEventName ||\n \"keydown\" === domEventName\n )\n return getInstIfValueChanged(activeElementInst$1);\n}\nfunction getTargetInstForClickEvent(domEventName, targetInst) {\n if (\"click\" === domEventName) return getInstIfValueChanged(targetInst);\n}\nfunction getTargetInstForInputOrChangeEvent(domEventName, targetInst) {\n if (\"input\" === domEventName || \"change\" === domEventName)\n return getInstIfValueChanged(targetInst);\n}\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is;\nfunction shallowEqual(objA, objB) {\n if (objectIs(objA, objB)) return !0;\n if (\n \"object\" !== typeof objA ||\n null === objA ||\n \"object\" !== typeof objB ||\n null === objB\n )\n return !1;\n var keysA = Object.keys(objA),\n keysB = Object.keys(objB);\n if (keysA.length !== keysB.length) return !1;\n for (keysB = 0; keysB < keysA.length; keysB++) {\n var currentKey = keysA[keysB];\n if (\n !hasOwnProperty.call(objB, currentKey) ||\n !objectIs(objA[currentKey], objB[currentKey])\n )\n return !1;\n }\n return !0;\n}\nfunction getLeafNode(node) {\n for (; node && node.firstChild; ) node = node.firstChild;\n return node;\n}\nfunction getNodeForCharacterOffset(root, offset) {\n var node = getLeafNode(root);\n root = 0;\n for (var nodeEnd; node; ) {\n if (3 === node.nodeType) {\n nodeEnd = root + node.textContent.length;\n if (root <= offset && nodeEnd >= offset)\n return { node: node, offset: offset - root };\n root = nodeEnd;\n }\n a: {\n for (; node; ) {\n if (node.nextSibling) {\n node = node.nextSibling;\n break a;\n }\n node = node.parentNode;\n }\n node = void 0;\n }\n node = getLeafNode(node);\n }\n}\nfunction containsNode(outerNode, innerNode) {\n return outerNode && innerNode\n ? outerNode === innerNode\n ? !0\n : outerNode && 3 === outerNode.nodeType\n ? !1\n : innerNode && 3 === innerNode.nodeType\n ? containsNode(outerNode, innerNode.parentNode)\n : \"contains\" in outerNode\n ? outerNode.contains(innerNode)\n : outerNode.compareDocumentPosition\n ? !!(outerNode.compareDocumentPosition(innerNode) & 16)\n : !1\n : !1;\n}\nfunction getActiveElementDeep(containerInfo) {\n containerInfo =\n null != containerInfo &&\n null != containerInfo.ownerDocument &&\n null != containerInfo.ownerDocument.defaultView\n ? containerInfo.ownerDocument.defaultView\n : window;\n for (\n var element = getActiveElement(containerInfo.document);\n element instanceof containerInfo.HTMLIFrameElement;\n\n ) {\n try {\n var JSCompiler_inline_result =\n \"string\" === typeof element.contentWindow.location.href;\n } catch (err) {\n JSCompiler_inline_result = !1;\n }\n if (JSCompiler_inline_result) containerInfo = element.contentWindow;\n else break;\n element = getActiveElement(containerInfo.document);\n }\n return element;\n}\nfunction hasSelectionCapabilities(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return (\n nodeName &&\n ((\"input\" === nodeName &&\n (\"text\" === elem.type ||\n \"search\" === elem.type ||\n \"tel\" === elem.type ||\n \"url\" === elem.type ||\n \"password\" === elem.type)) ||\n \"textarea\" === nodeName ||\n \"true\" === elem.contentEditable)\n );\n}\nvar skipSelectionChangeEvent =\n canUseDOM && \"documentMode\" in document && 11 >= document.documentMode,\n activeElement = null,\n activeElementInst = null,\n lastSelection = null,\n mouseDown = !1;\nfunction constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {\n var doc =\n nativeEventTarget.window === nativeEventTarget\n ? nativeEventTarget.document\n : 9 === nativeEventTarget.nodeType\n ? nativeEventTarget\n : nativeEventTarget.ownerDocument;\n mouseDown ||\n null == activeElement ||\n activeElement !== getActiveElement(doc) ||\n ((doc = activeElement),\n \"selectionStart\" in doc && hasSelectionCapabilities(doc)\n ? (doc = { start: doc.selectionStart, end: doc.selectionEnd })\n : ((doc = (\n (doc.ownerDocument && doc.ownerDocument.defaultView) ||\n window\n ).getSelection()),\n (doc = {\n anchorNode: doc.anchorNode,\n anchorOffset: doc.anchorOffset,\n focusNode: doc.focusNode,\n focusOffset: doc.focusOffset\n })),\n (lastSelection && shallowEqual(lastSelection, doc)) ||\n ((lastSelection = doc),\n (doc = accumulateTwoPhaseListeners(activeElementInst, \"onSelect\")),\n 0 < doc.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onSelect\",\n \"select\",\n null,\n nativeEvent,\n nativeEventTarget\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: doc }),\n (nativeEvent.target = activeElement))));\n}\nfunction makePrefixMap(styleProp, eventName) {\n var prefixes = {};\n prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();\n prefixes[\"Webkit\" + styleProp] = \"webkit\" + eventName;\n prefixes[\"Moz\" + styleProp] = \"moz\" + eventName;\n return prefixes;\n}\nvar vendorPrefixes = {\n animationend: makePrefixMap(\"Animation\", \"AnimationEnd\"),\n animationiteration: makePrefixMap(\"Animation\", \"AnimationIteration\"),\n animationstart: makePrefixMap(\"Animation\", \"AnimationStart\"),\n transitionrun: makePrefixMap(\"Transition\", \"TransitionRun\"),\n transitionstart: makePrefixMap(\"Transition\", \"TransitionStart\"),\n transitioncancel: makePrefixMap(\"Transition\", \"TransitionCancel\"),\n transitionend: makePrefixMap(\"Transition\", \"TransitionEnd\")\n },\n prefixedEventNames = {},\n style = {};\ncanUseDOM &&\n ((style = document.createElement(\"div\").style),\n \"AnimationEvent\" in window ||\n (delete vendorPrefixes.animationend.animation,\n delete vendorPrefixes.animationiteration.animation,\n delete vendorPrefixes.animationstart.animation),\n \"TransitionEvent\" in window ||\n delete vendorPrefixes.transitionend.transition);\nfunction getVendorPrefixedEventName(eventName) {\n if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];\n if (!vendorPrefixes[eventName]) return eventName;\n var prefixMap = vendorPrefixes[eventName],\n styleProp;\n for (styleProp in prefixMap)\n if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)\n return (prefixedEventNames[eventName] = prefixMap[styleProp]);\n return eventName;\n}\nvar ANIMATION_END = getVendorPrefixedEventName(\"animationend\"),\n ANIMATION_ITERATION = getVendorPrefixedEventName(\"animationiteration\"),\n ANIMATION_START = getVendorPrefixedEventName(\"animationstart\"),\n TRANSITION_RUN = getVendorPrefixedEventName(\"transitionrun\"),\n TRANSITION_START = getVendorPrefixedEventName(\"transitionstart\"),\n TRANSITION_CANCEL = getVendorPrefixedEventName(\"transitioncancel\"),\n TRANSITION_END = getVendorPrefixedEventName(\"transitionend\"),\n topLevelEventsToReactNames = new Map(),\n simpleEventPluginEvents =\n \"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\n \" \"\n );\nsimpleEventPluginEvents.push(\"scrollEnd\");\nfunction registerSimpleEvent(domEventName, reactName) {\n topLevelEventsToReactNames.set(domEventName, reactName);\n registerTwoPhaseEvent(reactName, [domEventName]);\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n concurrentQueues = [],\n concurrentQueuesIndex = 0,\n concurrentlyUpdatedLanes = 0;\nfunction finishQueueingConcurrentUpdates() {\n for (\n var endIndex = concurrentQueuesIndex,\n i = (concurrentlyUpdatedLanes = concurrentQueuesIndex = 0);\n i < endIndex;\n\n ) {\n var fiber = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var queue = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var update = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var lane = concurrentQueues[i];\n concurrentQueues[i++] = null;\n if (null !== queue && null !== update) {\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n }\n 0 !== lane && markUpdateLaneFromFiberToRoot(fiber, update, lane);\n }\n}\nfunction enqueueUpdate$1(fiber, queue, update, lane) {\n concurrentQueues[concurrentQueuesIndex++] = fiber;\n concurrentQueues[concurrentQueuesIndex++] = queue;\n concurrentQueues[concurrentQueuesIndex++] = update;\n concurrentQueues[concurrentQueuesIndex++] = lane;\n concurrentlyUpdatedLanes |= lane;\n fiber.lanes |= lane;\n fiber = fiber.alternate;\n null !== fiber && (fiber.lanes |= lane);\n}\nfunction enqueueConcurrentHookUpdate(fiber, queue, update, lane) {\n enqueueUpdate$1(fiber, queue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction enqueueConcurrentRenderForLane(fiber, lane) {\n enqueueUpdate$1(fiber, null, null, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {\n sourceFiber.lanes |= lane;\n var alternate = sourceFiber.alternate;\n null !== alternate && (alternate.lanes |= lane);\n for (var isHidden = !1, parent = sourceFiber.return; null !== parent; )\n (parent.childLanes |= lane),\n (alternate = parent.alternate),\n null !== alternate && (alternate.childLanes |= lane),\n 22 === parent.tag &&\n ((sourceFiber = parent.stateNode),\n null === sourceFiber || sourceFiber._visibility & 1 || (isHidden = !0)),\n (sourceFiber = parent),\n (parent = parent.return);\n return 3 === sourceFiber.tag\n ? ((parent = sourceFiber.stateNode),\n isHidden &&\n null !== update &&\n ((isHidden = 31 - clz32(lane)),\n (sourceFiber = parent.hiddenUpdates),\n (alternate = sourceFiber[isHidden]),\n null === alternate\n ? (sourceFiber[isHidden] = [update])\n : alternate.push(update),\n (update.lane = lane | 536870912)),\n parent)\n : null;\n}\nfunction getRootForUpdatedFiber(sourceFiber) {\n if (50 < nestedUpdateCount)\n throw (\n ((nestedUpdateCount = 0),\n (rootWithNestedUpdates = null),\n Error(formatProdErrorMessage(185)))\n );\n for (var parent = sourceFiber.return; null !== parent; )\n (sourceFiber = parent), (parent = sourceFiber.return);\n return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;\n}\nvar emptyContextObject = {};\nfunction FiberNode(tag, pendingProps, key, mode) {\n this.tag = tag;\n this.key = key;\n this.sibling =\n this.child =\n this.return =\n this.stateNode =\n this.type =\n this.elementType =\n null;\n this.index = 0;\n this.refCleanup = this.ref = null;\n this.pendingProps = pendingProps;\n this.dependencies =\n this.memoizedState =\n this.updateQueue =\n this.memoizedProps =\n null;\n this.mode = mode;\n this.subtreeFlags = this.flags = 0;\n this.deletions = null;\n this.childLanes = this.lanes = 0;\n this.alternate = null;\n}\nfunction createFiberImplClass(tag, pendingProps, key, mode) {\n return new FiberNode(tag, pendingProps, key, mode);\n}\nfunction shouldConstruct(Component) {\n Component = Component.prototype;\n return !(!Component || !Component.isReactComponent);\n}\nfunction createWorkInProgress(current, pendingProps) {\n var workInProgress = current.alternate;\n null === workInProgress\n ? ((workInProgress = createFiberImplClass(\n current.tag,\n pendingProps,\n current.key,\n current.mode\n )),\n (workInProgress.elementType = current.elementType),\n (workInProgress.type = current.type),\n (workInProgress.stateNode = current.stateNode),\n (workInProgress.alternate = current),\n (current.alternate = workInProgress))\n : ((workInProgress.pendingProps = pendingProps),\n (workInProgress.type = current.type),\n (workInProgress.flags = 0),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null));\n workInProgress.flags = current.flags & 65011712;\n workInProgress.childLanes = current.childLanes;\n workInProgress.lanes = current.lanes;\n workInProgress.child = current.child;\n workInProgress.memoizedProps = current.memoizedProps;\n workInProgress.memoizedState = current.memoizedState;\n workInProgress.updateQueue = current.updateQueue;\n pendingProps = current.dependencies;\n workInProgress.dependencies =\n null === pendingProps\n ? null\n : { lanes: pendingProps.lanes, firstContext: pendingProps.firstContext };\n workInProgress.sibling = current.sibling;\n workInProgress.index = current.index;\n workInProgress.ref = current.ref;\n workInProgress.refCleanup = current.refCleanup;\n return workInProgress;\n}\nfunction resetWorkInProgress(workInProgress, renderLanes) {\n workInProgress.flags &= 65011714;\n var current = workInProgress.alternate;\n null === current\n ? ((workInProgress.childLanes = 0),\n (workInProgress.lanes = renderLanes),\n (workInProgress.child = null),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.memoizedProps = null),\n (workInProgress.memoizedState = null),\n (workInProgress.updateQueue = null),\n (workInProgress.dependencies = null),\n (workInProgress.stateNode = null))\n : ((workInProgress.childLanes = current.childLanes),\n (workInProgress.lanes = current.lanes),\n (workInProgress.child = current.child),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null),\n (workInProgress.memoizedProps = current.memoizedProps),\n (workInProgress.memoizedState = current.memoizedState),\n (workInProgress.updateQueue = current.updateQueue),\n (workInProgress.type = current.type),\n (renderLanes = current.dependencies),\n (workInProgress.dependencies =\n null === renderLanes\n ? null\n : {\n lanes: renderLanes.lanes,\n firstContext: renderLanes.firstContext\n }));\n return workInProgress;\n}\nfunction createFiberFromTypeAndProps(\n type,\n key,\n pendingProps,\n owner,\n mode,\n lanes\n) {\n var fiberTag = 0;\n owner = type;\n if (\"function\" === typeof type) shouldConstruct(type) && (fiberTag = 1);\n else if (\"string\" === typeof type)\n fiberTag = isHostHoistableType(\n type,\n pendingProps,\n contextStackCursor.current\n )\n ? 26\n : \"html\" === type || \"head\" === type || \"body\" === type\n ? 27\n : 5;\n else\n a: switch (type) {\n case REACT_ACTIVITY_TYPE:\n return (\n (type = createFiberImplClass(31, pendingProps, key, mode)),\n (type.elementType = REACT_ACTIVITY_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_FRAGMENT_TYPE:\n return createFiberFromFragment(pendingProps.children, mode, lanes, key);\n case REACT_STRICT_MODE_TYPE:\n fiberTag = 8;\n mode |= 24;\n break;\n case REACT_PROFILER_TYPE:\n return (\n (type = createFiberImplClass(12, pendingProps, key, mode | 2)),\n (type.elementType = REACT_PROFILER_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_TYPE:\n return (\n (type = createFiberImplClass(13, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_LIST_TYPE:\n return (\n (type = createFiberImplClass(19, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_LIST_TYPE),\n (type.lanes = lanes),\n type\n );\n default:\n if (\"object\" === typeof type && null !== type)\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n fiberTag = 10;\n break a;\n case REACT_CONSUMER_TYPE:\n fiberTag = 9;\n break a;\n case REACT_FORWARD_REF_TYPE:\n fiberTag = 11;\n break a;\n case REACT_MEMO_TYPE:\n fiberTag = 14;\n break a;\n case REACT_LAZY_TYPE:\n fiberTag = 16;\n owner = null;\n break a;\n }\n fiberTag = 29;\n pendingProps = Error(\n formatProdErrorMessage(130, null === type ? \"null\" : typeof type, \"\")\n );\n owner = null;\n }\n key = createFiberImplClass(fiberTag, pendingProps, key, mode);\n key.elementType = type;\n key.type = owner;\n key.lanes = lanes;\n return key;\n}\nfunction createFiberFromFragment(elements, mode, lanes, key) {\n elements = createFiberImplClass(7, elements, key, mode);\n elements.lanes = lanes;\n return elements;\n}\nfunction createFiberFromText(content, mode, lanes) {\n content = createFiberImplClass(6, content, null, mode);\n content.lanes = lanes;\n return content;\n}\nfunction createFiberFromDehydratedFragment(dehydratedNode) {\n var fiber = createFiberImplClass(18, null, null, 0);\n fiber.stateNode = dehydratedNode;\n return fiber;\n}\nfunction createFiberFromPortal(portal, mode, lanes) {\n mode = createFiberImplClass(\n 4,\n null !== portal.children ? portal.children : [],\n portal.key,\n mode\n );\n mode.lanes = lanes;\n mode.stateNode = {\n containerInfo: portal.containerInfo,\n pendingChildren: null,\n implementation: portal.implementation\n };\n return mode;\n}\nvar CapturedStacks = new WeakMap();\nfunction createCapturedValueAtFiber(value, source) {\n if (\"object\" === typeof value && null !== value) {\n var existing = CapturedStacks.get(value);\n if (void 0 !== existing) return existing;\n source = {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n CapturedStacks.set(value, source);\n return source;\n }\n return {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n}\nvar forkStack = [],\n forkStackIndex = 0,\n treeForkProvider = null,\n treeForkCount = 0,\n idStack = [],\n idStackIndex = 0,\n treeContextProvider = null,\n treeContextId = 1,\n treeContextOverflow = \"\";\nfunction pushTreeFork(workInProgress, totalChildren) {\n forkStack[forkStackIndex++] = treeForkCount;\n forkStack[forkStackIndex++] = treeForkProvider;\n treeForkProvider = workInProgress;\n treeForkCount = totalChildren;\n}\nfunction pushTreeId(workInProgress, totalChildren, index) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextProvider = workInProgress;\n var baseIdWithLeadingBit = treeContextId;\n workInProgress = treeContextOverflow;\n var baseLength = 32 - clz32(baseIdWithLeadingBit) - 1;\n baseIdWithLeadingBit &= ~(1 << baseLength);\n index += 1;\n var length = 32 - clz32(totalChildren) + baseLength;\n if (30 < length) {\n var numberOfOverflowBits = baseLength - (baseLength % 5);\n length = (\n baseIdWithLeadingBit &\n ((1 << numberOfOverflowBits) - 1)\n ).toString(32);\n baseIdWithLeadingBit >>= numberOfOverflowBits;\n baseLength -= numberOfOverflowBits;\n treeContextId =\n (1 << (32 - clz32(totalChildren) + baseLength)) |\n (index << baseLength) |\n baseIdWithLeadingBit;\n treeContextOverflow = length + workInProgress;\n } else\n (treeContextId =\n (1 << length) | (index << baseLength) | baseIdWithLeadingBit),\n (treeContextOverflow = workInProgress);\n}\nfunction pushMaterializedTreeId(workInProgress) {\n null !== workInProgress.return &&\n (pushTreeFork(workInProgress, 1), pushTreeId(workInProgress, 1, 0));\n}\nfunction popTreeContext(workInProgress) {\n for (; workInProgress === treeForkProvider; )\n (treeForkProvider = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null),\n (treeForkCount = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null);\n for (; workInProgress === treeContextProvider; )\n (treeContextProvider = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextOverflow = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextId = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null);\n}\nfunction restoreSuspendedTreeContext(workInProgress, suspendedContext) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextId = suspendedContext.id;\n treeContextOverflow = suspendedContext.overflow;\n treeContextProvider = workInProgress;\n}\nvar hydrationParentFiber = null,\n nextHydratableInstance = null,\n isHydrating = !1,\n hydrationErrors = null,\n rootOrSingletonContext = !1,\n HydrationMismatchException = Error(formatProdErrorMessage(519));\nfunction throwOnHydrationMismatch(fiber) {\n var error = Error(\n formatProdErrorMessage(\n 418,\n 1 < arguments.length && void 0 !== arguments[1] && arguments[1]\n ? \"text\"\n : \"HTML\",\n \"\"\n )\n );\n queueHydrationError(createCapturedValueAtFiber(error, fiber));\n throw HydrationMismatchException;\n}\nfunction prepareToHydrateHostInstance(fiber) {\n var instance = fiber.stateNode,\n type = fiber.type,\n props = fiber.memoizedProps;\n instance[internalInstanceKey] = fiber;\n instance[internalPropsKey] = props;\n switch (type) {\n case \"dialog\":\n listenToNonDelegatedEvent(\"cancel\", instance);\n listenToNonDelegatedEvent(\"close\", instance);\n break;\n case \"iframe\":\n case \"object\":\n case \"embed\":\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"video\":\n case \"audio\":\n for (type = 0; type < mediaEventTypes.length; type++)\n listenToNonDelegatedEvent(mediaEventTypes[type], instance);\n break;\n case \"source\":\n listenToNonDelegatedEvent(\"error\", instance);\n break;\n case \"img\":\n case \"image\":\n case \"link\":\n listenToNonDelegatedEvent(\"error\", instance);\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"details\":\n listenToNonDelegatedEvent(\"toggle\", instance);\n break;\n case \"input\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n initInput(\n instance,\n props.value,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name,\n !0\n );\n break;\n case \"select\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n break;\n case \"textarea\":\n listenToNonDelegatedEvent(\"invalid\", instance),\n initTextarea(instance, props.value, props.defaultValue, props.children);\n }\n type = props.children;\n (\"string\" !== typeof type &&\n \"number\" !== typeof type &&\n \"bigint\" !== typeof type) ||\n instance.textContent === \"\" + type ||\n !0 === props.suppressHydrationWarning ||\n checkForUnmatchedText(instance.textContent, type)\n ? (null != props.popover &&\n (listenToNonDelegatedEvent(\"beforetoggle\", instance),\n listenToNonDelegatedEvent(\"toggle\", instance)),\n null != props.onScroll && listenToNonDelegatedEvent(\"scroll\", instance),\n null != props.onScrollEnd &&\n listenToNonDelegatedEvent(\"scrollend\", instance),\n null != props.onClick && (instance.onclick = noop$1),\n (instance = !0))\n : (instance = !1);\n instance || throwOnHydrationMismatch(fiber, !0);\n}\nfunction popToNextHostParent(fiber) {\n for (hydrationParentFiber = fiber.return; hydrationParentFiber; )\n switch (hydrationParentFiber.tag) {\n case 5:\n case 31:\n case 13:\n rootOrSingletonContext = !1;\n return;\n case 27:\n case 3:\n rootOrSingletonContext = !0;\n return;\n default:\n hydrationParentFiber = hydrationParentFiber.return;\n }\n}\nfunction popHydrationState(fiber) {\n if (fiber !== hydrationParentFiber) return !1;\n if (!isHydrating) return popToNextHostParent(fiber), (isHydrating = !0), !1;\n var tag = fiber.tag,\n JSCompiler_temp;\n if ((JSCompiler_temp = 3 !== tag && 27 !== tag)) {\n if ((JSCompiler_temp = 5 === tag))\n (JSCompiler_temp = fiber.type),\n (JSCompiler_temp =\n !(\"form\" !== JSCompiler_temp && \"button\" !== JSCompiler_temp) ||\n shouldSetTextContent(fiber.type, fiber.memoizedProps));\n JSCompiler_temp = !JSCompiler_temp;\n }\n JSCompiler_temp && nextHydratableInstance && throwOnHydrationMismatch(fiber);\n popToNextHostParent(fiber);\n if (13 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else if (31 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else\n 27 === tag\n ? ((tag = nextHydratableInstance),\n isSingletonScope(fiber.type)\n ? ((fiber = previousHydratableOnEnteringScopedSingleton),\n (previousHydratableOnEnteringScopedSingleton = null),\n (nextHydratableInstance = fiber))\n : (nextHydratableInstance = tag))\n : (nextHydratableInstance = hydrationParentFiber\n ? getNextHydratable(fiber.stateNode.nextSibling)\n : null);\n return !0;\n}\nfunction resetHydrationState() {\n nextHydratableInstance = hydrationParentFiber = null;\n isHydrating = !1;\n}\nfunction upgradeHydrationErrorsToRecoverable() {\n var queuedErrors = hydrationErrors;\n null !== queuedErrors &&\n (null === workInProgressRootRecoverableErrors\n ? (workInProgressRootRecoverableErrors = queuedErrors)\n : workInProgressRootRecoverableErrors.push.apply(\n workInProgressRootRecoverableErrors,\n queuedErrors\n ),\n (hydrationErrors = null));\n return queuedErrors;\n}\nfunction queueHydrationError(error) {\n null === hydrationErrors\n ? (hydrationErrors = [error])\n : hydrationErrors.push(error);\n}\nvar valueCursor = createCursor(null),\n currentlyRenderingFiber$1 = null,\n lastContextDependency = null;\nfunction pushProvider(providerFiber, context, nextValue) {\n push(valueCursor, context._currentValue);\n context._currentValue = nextValue;\n}\nfunction popProvider(context) {\n context._currentValue = valueCursor.current;\n pop(valueCursor);\n}\nfunction scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {\n for (; null !== parent; ) {\n var alternate = parent.alternate;\n (parent.childLanes & renderLanes) !== renderLanes\n ? ((parent.childLanes |= renderLanes),\n null !== alternate && (alternate.childLanes |= renderLanes))\n : null !== alternate &&\n (alternate.childLanes & renderLanes) !== renderLanes &&\n (alternate.childLanes |= renderLanes);\n if (parent === propagationRoot) break;\n parent = parent.return;\n }\n}\nfunction propagateContextChanges(\n workInProgress,\n contexts,\n renderLanes,\n forcePropagateEntireTree\n) {\n var fiber = workInProgress.child;\n null !== fiber && (fiber.return = workInProgress);\n for (; null !== fiber; ) {\n var list = fiber.dependencies;\n if (null !== list) {\n var nextFiber = fiber.child;\n list = list.firstContext;\n a: for (; null !== list; ) {\n var dependency = list;\n list = fiber;\n for (var i = 0; i < contexts.length; i++)\n if (dependency.context === contexts[i]) {\n list.lanes |= renderLanes;\n dependency = list.alternate;\n null !== dependency && (dependency.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(\n list.return,\n renderLanes,\n workInProgress\n );\n forcePropagateEntireTree || (nextFiber = null);\n break a;\n }\n list = dependency.next;\n }\n } else if (18 === fiber.tag) {\n nextFiber = fiber.return;\n if (null === nextFiber) throw Error(formatProdErrorMessage(341));\n nextFiber.lanes |= renderLanes;\n list = nextFiber.alternate;\n null !== list && (list.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(nextFiber, renderLanes, workInProgress);\n nextFiber = null;\n } else nextFiber = fiber.child;\n if (null !== nextFiber) nextFiber.return = fiber;\n else\n for (nextFiber = fiber; null !== nextFiber; ) {\n if (nextFiber === workInProgress) {\n nextFiber = null;\n break;\n }\n fiber = nextFiber.sibling;\n if (null !== fiber) {\n fiber.return = nextFiber.return;\n nextFiber = fiber;\n break;\n }\n nextFiber = nextFiber.return;\n }\n fiber = nextFiber;\n }\n}\nfunction propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n forcePropagateEntireTree\n) {\n current = null;\n for (\n var parent = workInProgress, isInsidePropagationBailout = !1;\n null !== parent;\n\n ) {\n if (!isInsidePropagationBailout)\n if (0 !== (parent.flags & 524288)) isInsidePropagationBailout = !0;\n else if (0 !== (parent.flags & 262144)) break;\n if (10 === parent.tag) {\n var currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent = currentParent.memoizedProps;\n if (null !== currentParent) {\n var context = parent.type;\n objectIs(parent.pendingProps.value, currentParent.value) ||\n (null !== current ? current.push(context) : (current = [context]));\n }\n } else if (parent === hostTransitionProviderCursor.current) {\n currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent.memoizedState.memoizedState !==\n parent.memoizedState.memoizedState &&\n (null !== current\n ? current.push(HostTransitionContext)\n : (current = [HostTransitionContext]));\n }\n parent = parent.return;\n }\n null !== current &&\n propagateContextChanges(\n workInProgress,\n current,\n renderLanes,\n forcePropagateEntireTree\n );\n workInProgress.flags |= 262144;\n}\nfunction checkIfContextChanged(currentDependencies) {\n for (\n currentDependencies = currentDependencies.firstContext;\n null !== currentDependencies;\n\n ) {\n if (\n !objectIs(\n currentDependencies.context._currentValue,\n currentDependencies.memoizedValue\n )\n )\n return !0;\n currentDependencies = currentDependencies.next;\n }\n return !1;\n}\nfunction prepareToReadContext(workInProgress) {\n currentlyRenderingFiber$1 = workInProgress;\n lastContextDependency = null;\n workInProgress = workInProgress.dependencies;\n null !== workInProgress && (workInProgress.firstContext = null);\n}\nfunction readContext(context) {\n return readContextForConsumer(currentlyRenderingFiber$1, context);\n}\nfunction readContextDuringReconciliation(consumer, context) {\n null === currentlyRenderingFiber$1 && prepareToReadContext(consumer);\n return readContextForConsumer(consumer, context);\n}\nfunction readContextForConsumer(consumer, context) {\n var value = context._currentValue;\n context = { context: context, memoizedValue: value, next: null };\n if (null === lastContextDependency) {\n if (null === consumer) throw Error(formatProdErrorMessage(308));\n lastContextDependency = context;\n consumer.dependencies = { lanes: 0, firstContext: context };\n consumer.flags |= 524288;\n } else lastContextDependency = lastContextDependency.next = context;\n return value;\n}\nvar AbortControllerLocal =\n \"undefined\" !== typeof AbortController\n ? AbortController\n : function () {\n var listeners = [],\n signal = (this.signal = {\n aborted: !1,\n addEventListener: function (type, listener) {\n listeners.push(listener);\n }\n });\n this.abort = function () {\n signal.aborted = !0;\n listeners.forEach(function (listener) {\n return listener();\n });\n };\n },\n scheduleCallback$2 = Scheduler.unstable_scheduleCallback,\n NormalPriority = Scheduler.unstable_NormalPriority,\n CacheContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n Consumer: null,\n Provider: null,\n _currentValue: null,\n _currentValue2: null,\n _threadCount: 0\n };\nfunction createCache() {\n return {\n controller: new AbortControllerLocal(),\n data: new Map(),\n refCount: 0\n };\n}\nfunction releaseCache(cache) {\n cache.refCount--;\n 0 === cache.refCount &&\n scheduleCallback$2(NormalPriority, function () {\n cache.controller.abort();\n });\n}\nvar currentEntangledListeners = null,\n currentEntangledPendingCount = 0,\n currentEntangledLane = 0,\n currentEntangledActionThenable = null;\nfunction entangleAsyncAction(transition, thenable) {\n if (null === currentEntangledListeners) {\n var entangledListeners = (currentEntangledListeners = []);\n currentEntangledPendingCount = 0;\n currentEntangledLane = requestTransitionLane();\n currentEntangledActionThenable = {\n status: \"pending\",\n value: void 0,\n then: function (resolve) {\n entangledListeners.push(resolve);\n }\n };\n }\n currentEntangledPendingCount++;\n thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);\n return thenable;\n}\nfunction pingEngtangledActionScope() {\n if (\n 0 === --currentEntangledPendingCount &&\n null !== currentEntangledListeners\n ) {\n null !== currentEntangledActionThenable &&\n (currentEntangledActionThenable.status = \"fulfilled\");\n var listeners = currentEntangledListeners;\n currentEntangledListeners = null;\n currentEntangledLane = 0;\n currentEntangledActionThenable = null;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])();\n }\n}\nfunction chainThenableValue(thenable, result) {\n var listeners = [],\n thenableWithOverride = {\n status: \"pending\",\n value: null,\n reason: null,\n then: function (resolve) {\n listeners.push(resolve);\n }\n };\n thenable.then(\n function () {\n thenableWithOverride.status = \"fulfilled\";\n thenableWithOverride.value = result;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])(result);\n },\n function (error) {\n thenableWithOverride.status = \"rejected\";\n thenableWithOverride.reason = error;\n for (error = 0; error < listeners.length; error++)\n (0, listeners[error])(void 0);\n }\n );\n return thenableWithOverride;\n}\nvar prevOnStartTransitionFinish = ReactSharedInternals.S;\nReactSharedInternals.S = function (transition, returnValue) {\n globalMostRecentTransitionTime = now();\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n entangleAsyncAction(transition, returnValue);\n null !== prevOnStartTransitionFinish &&\n prevOnStartTransitionFinish(transition, returnValue);\n};\nvar resumedCache = createCursor(null);\nfunction peekCacheFromPool() {\n var cacheResumedFromPreviousRender = resumedCache.current;\n return null !== cacheResumedFromPreviousRender\n ? cacheResumedFromPreviousRender\n : workInProgressRoot.pooledCache;\n}\nfunction pushTransition(offscreenWorkInProgress, prevCachePool) {\n null === prevCachePool\n ? push(resumedCache, resumedCache.current)\n : push(resumedCache, prevCachePool.pool);\n}\nfunction getSuspendedCache() {\n var cacheFromPool = peekCacheFromPool();\n return null === cacheFromPool\n ? null\n : { parent: CacheContext._currentValue, pool: cacheFromPool };\n}\nvar SuspenseException = Error(formatProdErrorMessage(460)),\n SuspenseyCommitException = Error(formatProdErrorMessage(474)),\n SuspenseActionException = Error(formatProdErrorMessage(542)),\n noopSuspenseyCommitThenable = { then: function () {} };\nfunction isThenableResolved(thenable) {\n thenable = thenable.status;\n return \"fulfilled\" === thenable || \"rejected\" === thenable;\n}\nfunction trackUsedThenable(thenableState, thenable, index) {\n index = thenableState[index];\n void 0 === index\n ? thenableState.push(thenable)\n : index !== thenable && (thenable.then(noop$1, noop$1), (thenable = index));\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n default:\n if (\"string\" === typeof thenable.status) thenable.then(noop$1, noop$1);\n else {\n thenableState = workInProgressRoot;\n if (null !== thenableState && 100 < thenableState.shellSuspendCounter)\n throw Error(formatProdErrorMessage(482));\n thenableState = thenable;\n thenableState.status = \"pending\";\n thenableState.then(\n function (fulfilledValue) {\n if (\"pending\" === thenable.status) {\n var fulfilledThenable = thenable;\n fulfilledThenable.status = \"fulfilled\";\n fulfilledThenable.value = fulfilledValue;\n }\n },\n function (error) {\n if (\"pending\" === thenable.status) {\n var rejectedThenable = thenable;\n rejectedThenable.status = \"rejected\";\n rejectedThenable.reason = error;\n }\n }\n );\n }\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n }\n suspendedThenable = thenable;\n throw SuspenseException;\n }\n}\nfunction resolveLazy(lazyType) {\n try {\n var init = lazyType._init;\n return init(lazyType._payload);\n } catch (x) {\n if (null !== x && \"object\" === typeof x && \"function\" === typeof x.then)\n throw ((suspendedThenable = x), SuspenseException);\n throw x;\n }\n}\nvar suspendedThenable = null;\nfunction getSuspendedThenable() {\n if (null === suspendedThenable) throw Error(formatProdErrorMessage(459));\n var thenable = suspendedThenable;\n suspendedThenable = null;\n return thenable;\n}\nfunction checkIfUseWrappedInAsyncCatch(rejectedReason) {\n if (\n rejectedReason === SuspenseException ||\n rejectedReason === SuspenseActionException\n )\n throw Error(formatProdErrorMessage(483));\n}\nvar thenableState$1 = null,\n thenableIndexCounter$1 = 0;\nfunction unwrapThenable(thenable) {\n var index = thenableIndexCounter$1;\n thenableIndexCounter$1 += 1;\n null === thenableState$1 && (thenableState$1 = []);\n return trackUsedThenable(thenableState$1, thenable, index);\n}\nfunction coerceRef(workInProgress, element) {\n element = element.props.ref;\n workInProgress.ref = void 0 !== element ? element : null;\n}\nfunction throwOnInvalidObjectTypeImpl(returnFiber, newChild) {\n if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE)\n throw Error(formatProdErrorMessage(525));\n returnFiber = Object.prototype.toString.call(newChild);\n throw Error(\n formatProdErrorMessage(\n 31,\n \"[object Object]\" === returnFiber\n ? \"object with keys {\" + Object.keys(newChild).join(\", \") + \"}\"\n : returnFiber\n )\n );\n}\nfunction createChildReconciler(shouldTrackSideEffects) {\n function deleteChild(returnFiber, childToDelete) {\n if (shouldTrackSideEffects) {\n var deletions = returnFiber.deletions;\n null === deletions\n ? ((returnFiber.deletions = [childToDelete]), (returnFiber.flags |= 16))\n : deletions.push(childToDelete);\n }\n }\n function deleteRemainingChildren(returnFiber, currentFirstChild) {\n if (!shouldTrackSideEffects) return null;\n for (; null !== currentFirstChild; )\n deleteChild(returnFiber, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return null;\n }\n function mapRemainingChildren(currentFirstChild) {\n for (var existingChildren = new Map(); null !== currentFirstChild; )\n null !== currentFirstChild.key\n ? existingChildren.set(currentFirstChild.key, currentFirstChild)\n : existingChildren.set(currentFirstChild.index, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return existingChildren;\n }\n function useFiber(fiber, pendingProps) {\n fiber = createWorkInProgress(fiber, pendingProps);\n fiber.index = 0;\n fiber.sibling = null;\n return fiber;\n }\n function placeChild(newFiber, lastPlacedIndex, newIndex) {\n newFiber.index = newIndex;\n if (!shouldTrackSideEffects)\n return (newFiber.flags |= 1048576), lastPlacedIndex;\n newIndex = newFiber.alternate;\n if (null !== newIndex)\n return (\n (newIndex = newIndex.index),\n newIndex < lastPlacedIndex\n ? ((newFiber.flags |= 67108866), lastPlacedIndex)\n : newIndex\n );\n newFiber.flags |= 67108866;\n return lastPlacedIndex;\n }\n function placeSingleChild(newFiber) {\n shouldTrackSideEffects &&\n null === newFiber.alternate &&\n (newFiber.flags |= 67108866);\n return newFiber;\n }\n function updateTextNode(returnFiber, current, textContent, lanes) {\n if (null === current || 6 !== current.tag)\n return (\n (current = createFiberFromText(textContent, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, textContent);\n current.return = returnFiber;\n return current;\n }\n function updateElement(returnFiber, current, element, lanes) {\n var elementType = element.type;\n if (elementType === REACT_FRAGMENT_TYPE)\n return updateFragment(\n returnFiber,\n current,\n element.props.children,\n lanes,\n element.key\n );\n if (\n null !== current &&\n (current.elementType === elementType ||\n (\"object\" === typeof elementType &&\n null !== elementType &&\n elementType.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(elementType) === current.type))\n )\n return (\n (current = useFiber(current, element.props)),\n coerceRef(current, element),\n (current.return = returnFiber),\n current\n );\n current = createFiberFromTypeAndProps(\n element.type,\n element.key,\n element.props,\n null,\n returnFiber.mode,\n lanes\n );\n coerceRef(current, element);\n current.return = returnFiber;\n return current;\n }\n function updatePortal(returnFiber, current, portal, lanes) {\n if (\n null === current ||\n 4 !== current.tag ||\n current.stateNode.containerInfo !== portal.containerInfo ||\n current.stateNode.implementation !== portal.implementation\n )\n return (\n (current = createFiberFromPortal(portal, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, portal.children || []);\n current.return = returnFiber;\n return current;\n }\n function updateFragment(returnFiber, current, fragment, lanes, key) {\n if (null === current || 7 !== current.tag)\n return (\n (current = createFiberFromFragment(\n fragment,\n returnFiber.mode,\n lanes,\n key\n )),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, fragment);\n current.return = returnFiber;\n return current;\n }\n function createChild(returnFiber, newChild, lanes) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (newChild = createFiberFromText(\n \"\" + newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n lanes\n );\n case REACT_PORTAL_TYPE:\n return (\n (newChild = createFiberFromPortal(\n newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n createChild(returnFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (newChild = createFiberFromFragment(\n newChild,\n returnFiber.mode,\n lanes,\n null\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"function\" === typeof newChild.then)\n return createChild(returnFiber, unwrapThenable(newChild), lanes);\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return createChild(\n returnFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateSlot(returnFiber, oldFiber, newChild, lanes) {\n var key = null !== oldFiber ? oldFiber.key : null;\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return null !== key\n ? null\n : updateTextNode(returnFiber, oldFiber, \"\" + newChild, lanes);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return newChild.key === key\n ? updateElement(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_PORTAL_TYPE:\n return newChild.key === key\n ? updatePortal(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateSlot(returnFiber, oldFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return null !== key\n ? null\n : updateFragment(returnFiber, oldFiber, newChild, lanes, null);\n if (\"function\" === typeof newChild.then)\n return updateSlot(\n returnFiber,\n oldFiber,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateSlot(\n returnFiber,\n oldFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n ) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateTextNode(returnFiber, existingChildren, \"\" + newChild, lanes)\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updateElement(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_PORTAL_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updatePortal(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateFragment(returnFiber, existingChildren, newChild, lanes, null)\n );\n if (\"function\" === typeof newChild.then)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null;\n null !== oldFiber && newIdx < newChildren.length;\n newIdx++\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(\n returnFiber,\n oldFiber,\n newChildren[newIdx],\n lanes\n );\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (newIdx === newChildren.length)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; newIdx < newChildren.length; newIdx++)\n (oldFiber = createChild(returnFiber, newChildren[newIdx], lanes)),\n null !== oldFiber &&\n ((currentFirstChild = placeChild(\n oldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = oldFiber)\n : (previousNewFiber.sibling = oldFiber),\n (previousNewFiber = oldFiber));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n newIdx < newChildren.length;\n newIdx++\n )\n (nextOldFiber = updateFromMap(\n oldFiber,\n returnFiber,\n newIdx,\n newChildren[newIdx],\n lanes\n )),\n null !== nextOldFiber &&\n (shouldTrackSideEffects &&\n null !== nextOldFiber.alternate &&\n oldFiber.delete(\n null === nextOldFiber.key ? newIdx : nextOldFiber.key\n ),\n (currentFirstChild = placeChild(\n nextOldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = nextOldFiber)\n : (previousNewFiber.sibling = nextOldFiber),\n (previousNewFiber = nextOldFiber));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n if (null == newChildren) throw Error(formatProdErrorMessage(151));\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null,\n step = newChildren.next();\n null !== oldFiber && !step.done;\n newIdx++, step = newChildren.next()\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (step.done)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; !step.done; newIdx++, step = newChildren.next())\n (step = createChild(returnFiber, step.value, lanes)),\n null !== step &&\n ((currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n !step.done;\n newIdx++, step = newChildren.next()\n )\n (step = updateFromMap(oldFiber, returnFiber, newIdx, step.value, lanes)),\n null !== step &&\n (shouldTrackSideEffects &&\n null !== step.alternate &&\n oldFiber.delete(null === step.key ? newIdx : step.key),\n (currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n ) {\n \"object\" === typeof newChild &&\n null !== newChild &&\n newChild.type === REACT_FRAGMENT_TYPE &&\n null === newChild.key &&\n (newChild = newChild.props.children);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n a: {\n for (var key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key) {\n key = newChild.type;\n if (key === REACT_FRAGMENT_TYPE) {\n if (7 === currentFirstChild.tag) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(\n currentFirstChild,\n newChild.props.children\n );\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n } else if (\n currentFirstChild.elementType === key ||\n (\"object\" === typeof key &&\n null !== key &&\n key.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(key) === currentFirstChild.type)\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.props);\n coerceRef(lanes, newChild);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n } else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n newChild.type === REACT_FRAGMENT_TYPE\n ? ((lanes = createFiberFromFragment(\n newChild.props.children,\n returnFiber.mode,\n lanes,\n newChild.key\n )),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : ((lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n (returnFiber = lanes));\n }\n return placeSingleChild(returnFiber);\n case REACT_PORTAL_TYPE:\n a: {\n for (key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key)\n if (\n 4 === currentFirstChild.tag &&\n currentFirstChild.stateNode.containerInfo ===\n newChild.containerInfo &&\n currentFirstChild.stateNode.implementation ===\n newChild.implementation\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.children || []);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n } else {\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n }\n else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n lanes = createFiberFromPortal(newChild, returnFiber.mode, lanes);\n lanes.return = returnFiber;\n returnFiber = lanes;\n }\n return placeSingleChild(returnFiber);\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild))\n return reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n if (getIteratorFn(newChild)) {\n key = getIteratorFn(newChild);\n if (\"function\" !== typeof key) throw Error(formatProdErrorMessage(150));\n newChild = key.call(newChild);\n return reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n }\n if (\"function\" === typeof newChild.then)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n ? ((newChild = \"\" + newChild),\n null !== currentFirstChild && 6 === currentFirstChild.tag\n ? (deleteRemainingChildren(returnFiber, currentFirstChild.sibling),\n (lanes = useFiber(currentFirstChild, newChild)),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : (deleteRemainingChildren(returnFiber, currentFirstChild),\n (lanes = createFiberFromText(newChild, returnFiber.mode, lanes)),\n (lanes.return = returnFiber),\n (returnFiber = lanes)),\n placeSingleChild(returnFiber))\n : deleteRemainingChildren(returnFiber, currentFirstChild);\n }\n return function (returnFiber, currentFirstChild, newChild, lanes) {\n try {\n thenableIndexCounter$1 = 0;\n var firstChildFiber = reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n thenableState$1 = null;\n return firstChildFiber;\n } catch (x) {\n if (x === SuspenseException || x === SuspenseActionException) throw x;\n var fiber = createFiberImplClass(29, x, null, returnFiber.mode);\n fiber.lanes = lanes;\n fiber.return = returnFiber;\n return fiber;\n } finally {\n }\n };\n}\nvar reconcileChildFibers = createChildReconciler(!0),\n mountChildFibers = createChildReconciler(!1),\n hasForceUpdate = !1;\nfunction initializeUpdateQueue(fiber) {\n fiber.updateQueue = {\n baseState: fiber.memoizedState,\n firstBaseUpdate: null,\n lastBaseUpdate: null,\n shared: { pending: null, lanes: 0, hiddenCallbacks: null },\n callbacks: null\n };\n}\nfunction cloneUpdateQueue(current, workInProgress) {\n current = current.updateQueue;\n workInProgress.updateQueue === current &&\n (workInProgress.updateQueue = {\n baseState: current.baseState,\n firstBaseUpdate: current.firstBaseUpdate,\n lastBaseUpdate: current.lastBaseUpdate,\n shared: current.shared,\n callbacks: null\n });\n}\nfunction createUpdate(lane) {\n return { lane: lane, tag: 0, payload: null, callback: null, next: null };\n}\nfunction enqueueUpdate(fiber, update, lane) {\n var updateQueue = fiber.updateQueue;\n if (null === updateQueue) return null;\n updateQueue = updateQueue.shared;\n if (0 !== (executionContext & 2)) {\n var pending = updateQueue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n updateQueue.pending = update;\n update = getRootForUpdatedFiber(fiber);\n markUpdateLaneFromFiberToRoot(fiber, null, lane);\n return update;\n }\n enqueueUpdate$1(fiber, updateQueue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction entangleTransitions(root, fiber, lane) {\n fiber = fiber.updateQueue;\n if (null !== fiber && ((fiber = fiber.shared), 0 !== (lane & 4194048))) {\n var queueLanes = fiber.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n fiber.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nfunction enqueueCapturedUpdate(workInProgress, capturedUpdate) {\n var queue = workInProgress.updateQueue,\n current = workInProgress.alternate;\n if (\n null !== current &&\n ((current = current.updateQueue), queue === current)\n ) {\n var newFirst = null,\n newLast = null;\n queue = queue.firstBaseUpdate;\n if (null !== queue) {\n do {\n var clone = {\n lane: queue.lane,\n tag: queue.tag,\n payload: queue.payload,\n callback: null,\n next: null\n };\n null === newLast\n ? (newFirst = newLast = clone)\n : (newLast = newLast.next = clone);\n queue = queue.next;\n } while (null !== queue);\n null === newLast\n ? (newFirst = newLast = capturedUpdate)\n : (newLast = newLast.next = capturedUpdate);\n } else newFirst = newLast = capturedUpdate;\n queue = {\n baseState: current.baseState,\n firstBaseUpdate: newFirst,\n lastBaseUpdate: newLast,\n shared: current.shared,\n callbacks: current.callbacks\n };\n workInProgress.updateQueue = queue;\n return;\n }\n workInProgress = queue.lastBaseUpdate;\n null === workInProgress\n ? (queue.firstBaseUpdate = capturedUpdate)\n : (workInProgress.next = capturedUpdate);\n queue.lastBaseUpdate = capturedUpdate;\n}\nvar didReadFromEntangledAsyncAction = !1;\nfunction suspendIfUpdateReadFromEntangledAsyncAction() {\n if (didReadFromEntangledAsyncAction) {\n var entangledActionThenable = currentEntangledActionThenable;\n if (null !== entangledActionThenable) throw entangledActionThenable;\n }\n}\nfunction processUpdateQueue(\n workInProgress$jscomp$0,\n props,\n instance$jscomp$0,\n renderLanes\n) {\n didReadFromEntangledAsyncAction = !1;\n var queue = workInProgress$jscomp$0.updateQueue;\n hasForceUpdate = !1;\n var firstBaseUpdate = queue.firstBaseUpdate,\n lastBaseUpdate = queue.lastBaseUpdate,\n pendingQueue = queue.shared.pending;\n if (null !== pendingQueue) {\n queue.shared.pending = null;\n var lastPendingUpdate = pendingQueue,\n firstPendingUpdate = lastPendingUpdate.next;\n lastPendingUpdate.next = null;\n null === lastBaseUpdate\n ? (firstBaseUpdate = firstPendingUpdate)\n : (lastBaseUpdate.next = firstPendingUpdate);\n lastBaseUpdate = lastPendingUpdate;\n var current = workInProgress$jscomp$0.alternate;\n null !== current &&\n ((current = current.updateQueue),\n (pendingQueue = current.lastBaseUpdate),\n pendingQueue !== lastBaseUpdate &&\n (null === pendingQueue\n ? (current.firstBaseUpdate = firstPendingUpdate)\n : (pendingQueue.next = firstPendingUpdate),\n (current.lastBaseUpdate = lastPendingUpdate)));\n }\n if (null !== firstBaseUpdate) {\n var newState = queue.baseState;\n lastBaseUpdate = 0;\n current = firstPendingUpdate = lastPendingUpdate = null;\n pendingQueue = firstBaseUpdate;\n do {\n var updateLane = pendingQueue.lane & -536870913,\n isHiddenUpdate = updateLane !== pendingQueue.lane;\n if (\n isHiddenUpdate\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n 0 !== updateLane &&\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction = !0);\n null !== current &&\n (current = current.next =\n {\n lane: 0,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: null,\n next: null\n });\n a: {\n var workInProgress = workInProgress$jscomp$0,\n update = pendingQueue;\n updateLane = props;\n var instance = instance$jscomp$0;\n switch (update.tag) {\n case 1:\n workInProgress = update.payload;\n if (\"function\" === typeof workInProgress) {\n newState = workInProgress.call(instance, newState, updateLane);\n break a;\n }\n newState = workInProgress;\n break a;\n case 3:\n workInProgress.flags = (workInProgress.flags & -65537) | 128;\n case 0:\n workInProgress = update.payload;\n updateLane =\n \"function\" === typeof workInProgress\n ? workInProgress.call(instance, newState, updateLane)\n : workInProgress;\n if (null === updateLane || void 0 === updateLane) break a;\n newState = assign({}, newState, updateLane);\n break a;\n case 2:\n hasForceUpdate = !0;\n }\n }\n updateLane = pendingQueue.callback;\n null !== updateLane &&\n ((workInProgress$jscomp$0.flags |= 64),\n isHiddenUpdate && (workInProgress$jscomp$0.flags |= 8192),\n (isHiddenUpdate = queue.callbacks),\n null === isHiddenUpdate\n ? (queue.callbacks = [updateLane])\n : isHiddenUpdate.push(updateLane));\n } else\n (isHiddenUpdate = {\n lane: updateLane,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: pendingQueue.callback,\n next: null\n }),\n null === current\n ? ((firstPendingUpdate = current = isHiddenUpdate),\n (lastPendingUpdate = newState))\n : (current = current.next = isHiddenUpdate),\n (lastBaseUpdate |= updateLane);\n pendingQueue = pendingQueue.next;\n if (null === pendingQueue)\n if (((pendingQueue = queue.shared.pending), null === pendingQueue))\n break;\n else\n (isHiddenUpdate = pendingQueue),\n (pendingQueue = isHiddenUpdate.next),\n (isHiddenUpdate.next = null),\n (queue.lastBaseUpdate = isHiddenUpdate),\n (queue.shared.pending = null);\n } while (1);\n null === current && (lastPendingUpdate = newState);\n queue.baseState = lastPendingUpdate;\n queue.firstBaseUpdate = firstPendingUpdate;\n queue.lastBaseUpdate = current;\n null === firstBaseUpdate && (queue.shared.lanes = 0);\n workInProgressRootSkippedLanes |= lastBaseUpdate;\n workInProgress$jscomp$0.lanes = lastBaseUpdate;\n workInProgress$jscomp$0.memoizedState = newState;\n }\n}\nfunction callCallback(callback, context) {\n if (\"function\" !== typeof callback)\n throw Error(formatProdErrorMessage(191, callback));\n callback.call(context);\n}\nfunction commitCallbacks(updateQueue, context) {\n var callbacks = updateQueue.callbacks;\n if (null !== callbacks)\n for (\n updateQueue.callbacks = null, updateQueue = 0;\n updateQueue < callbacks.length;\n updateQueue++\n )\n callCallback(callbacks[updateQueue], context);\n}\nvar currentTreeHiddenStackCursor = createCursor(null),\n prevEntangledRenderLanesCursor = createCursor(0);\nfunction pushHiddenContext(fiber, context) {\n fiber = entangledRenderLanes;\n push(prevEntangledRenderLanesCursor, fiber);\n push(currentTreeHiddenStackCursor, context);\n entangledRenderLanes = fiber | context.baseLanes;\n}\nfunction reuseHiddenContextOnStack() {\n push(prevEntangledRenderLanesCursor, entangledRenderLanes);\n push(currentTreeHiddenStackCursor, currentTreeHiddenStackCursor.current);\n}\nfunction popHiddenContext() {\n entangledRenderLanes = prevEntangledRenderLanesCursor.current;\n pop(currentTreeHiddenStackCursor);\n pop(prevEntangledRenderLanesCursor);\n}\nvar suspenseHandlerStackCursor = createCursor(null),\n shellBoundary = null;\nfunction pushPrimaryTreeSuspenseHandler(handler) {\n var current = handler.alternate;\n push(suspenseStackCursor, suspenseStackCursor.current & 1);\n push(suspenseHandlerStackCursor, handler);\n null === shellBoundary &&\n (null === current || null !== currentTreeHiddenStackCursor.current\n ? (shellBoundary = handler)\n : null !== current.memoizedState && (shellBoundary = handler));\n}\nfunction pushDehydratedActivitySuspenseHandler(fiber) {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, fiber);\n null === shellBoundary && (shellBoundary = fiber);\n}\nfunction pushOffscreenSuspenseHandler(fiber) {\n 22 === fiber.tag\n ? (push(suspenseStackCursor, suspenseStackCursor.current),\n push(suspenseHandlerStackCursor, fiber),\n null === shellBoundary && (shellBoundary = fiber))\n : reuseSuspenseHandlerOnStack(fiber);\n}\nfunction reuseSuspenseHandlerOnStack() {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, suspenseHandlerStackCursor.current);\n}\nfunction popSuspenseHandler(fiber) {\n pop(suspenseHandlerStackCursor);\n shellBoundary === fiber && (shellBoundary = null);\n pop(suspenseStackCursor);\n}\nvar suspenseStackCursor = createCursor(0);\nfunction findFirstSuspended(row) {\n for (var node = row; null !== node; ) {\n if (13 === node.tag) {\n var state = node.memoizedState;\n if (\n null !== state &&\n ((state = state.dehydrated),\n null === state ||\n isSuspenseInstancePending(state) ||\n isSuspenseInstanceFallback(state))\n )\n return node;\n } else if (\n 19 === node.tag &&\n (\"forwards\" === node.memoizedProps.revealOrder ||\n \"backwards\" === node.memoizedProps.revealOrder ||\n \"unstable_legacy-backwards\" === node.memoizedProps.revealOrder ||\n \"together\" === node.memoizedProps.revealOrder)\n ) {\n if (0 !== (node.flags & 128)) return node;\n } else if (null !== node.child) {\n node.child.return = node;\n node = node.child;\n continue;\n }\n if (node === row) break;\n for (; null === node.sibling; ) {\n if (null === node.return || node.return === row) return null;\n node = node.return;\n }\n node.sibling.return = node.return;\n node = node.sibling;\n }\n return null;\n}\nvar renderLanes = 0,\n currentlyRenderingFiber = null,\n currentHook = null,\n workInProgressHook = null,\n didScheduleRenderPhaseUpdate = !1,\n didScheduleRenderPhaseUpdateDuringThisPass = !1,\n shouldDoubleInvokeUserFnsInHooksDEV = !1,\n localIdCounter = 0,\n thenableIndexCounter = 0,\n thenableState = null,\n globalClientIdCounter = 0;\nfunction throwInvalidHookError() {\n throw Error(formatProdErrorMessage(321));\n}\nfunction areHookInputsEqual(nextDeps, prevDeps) {\n if (null === prevDeps) return !1;\n for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)\n if (!objectIs(nextDeps[i], prevDeps[i])) return !1;\n return !0;\n}\nfunction renderWithHooks(\n current,\n workInProgress,\n Component,\n props,\n secondArg,\n nextRenderLanes\n) {\n renderLanes = nextRenderLanes;\n currentlyRenderingFiber = workInProgress;\n workInProgress.memoizedState = null;\n workInProgress.updateQueue = null;\n workInProgress.lanes = 0;\n ReactSharedInternals.H =\n null === current || null === current.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate;\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n nextRenderLanes = Component(props, secondArg);\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n didScheduleRenderPhaseUpdateDuringThisPass &&\n (nextRenderLanes = renderWithHooksAgain(\n workInProgress,\n Component,\n props,\n secondArg\n ));\n finishRenderingHooks(current);\n return nextRenderLanes;\n}\nfunction finishRenderingHooks(current) {\n ReactSharedInternals.H = ContextOnlyDispatcher;\n var didRenderTooFewHooks = null !== currentHook && null !== currentHook.next;\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdate = !1;\n thenableIndexCounter = 0;\n thenableState = null;\n if (didRenderTooFewHooks) throw Error(formatProdErrorMessage(300));\n null === current ||\n didReceiveUpdate ||\n ((current = current.dependencies),\n null !== current &&\n checkIfContextChanged(current) &&\n (didReceiveUpdate = !0));\n}\nfunction renderWithHooksAgain(workInProgress, Component, props, secondArg) {\n currentlyRenderingFiber = workInProgress;\n var numberOfReRenders = 0;\n do {\n didScheduleRenderPhaseUpdateDuringThisPass && (thenableState = null);\n thenableIndexCounter = 0;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n if (25 <= numberOfReRenders) throw Error(formatProdErrorMessage(301));\n numberOfReRenders += 1;\n workInProgressHook = currentHook = null;\n if (null != workInProgress.updateQueue) {\n var children = workInProgress.updateQueue;\n children.lastEffect = null;\n children.events = null;\n children.stores = null;\n null != children.memoCache && (children.memoCache.index = 0);\n }\n ReactSharedInternals.H = HooksDispatcherOnRerender;\n children = Component(props, secondArg);\n } while (didScheduleRenderPhaseUpdateDuringThisPass);\n return children;\n}\nfunction TransitionAwareHostComponent() {\n var dispatcher = ReactSharedInternals.H,\n maybeThenable = dispatcher.useState()[0];\n maybeThenable =\n \"function\" === typeof maybeThenable.then\n ? useThenable(maybeThenable)\n : maybeThenable;\n dispatcher = dispatcher.useState()[0];\n (null !== currentHook ? currentHook.memoizedState : null) !== dispatcher &&\n (currentlyRenderingFiber.flags |= 1024);\n return maybeThenable;\n}\nfunction checkDidRenderIdHook() {\n var didRenderIdHook = 0 !== localIdCounter;\n localIdCounter = 0;\n return didRenderIdHook;\n}\nfunction bailoutHooks(current, workInProgress, lanes) {\n workInProgress.updateQueue = current.updateQueue;\n workInProgress.flags &= -2053;\n current.lanes &= ~lanes;\n}\nfunction resetHooksOnUnwind(workInProgress) {\n if (didScheduleRenderPhaseUpdate) {\n for (\n workInProgress = workInProgress.memoizedState;\n null !== workInProgress;\n\n ) {\n var queue = workInProgress.queue;\n null !== queue && (queue.pending = null);\n workInProgress = workInProgress.next;\n }\n didScheduleRenderPhaseUpdate = !1;\n }\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n thenableIndexCounter = localIdCounter = 0;\n thenableState = null;\n}\nfunction mountWorkInProgressHook() {\n var hook = {\n memoizedState: null,\n baseState: null,\n baseQueue: null,\n queue: null,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook = hook)\n : (workInProgressHook = workInProgressHook.next = hook);\n return workInProgressHook;\n}\nfunction updateWorkInProgressHook() {\n if (null === currentHook) {\n var nextCurrentHook = currentlyRenderingFiber.alternate;\n nextCurrentHook =\n null !== nextCurrentHook ? nextCurrentHook.memoizedState : null;\n } else nextCurrentHook = currentHook.next;\n var nextWorkInProgressHook =\n null === workInProgressHook\n ? currentlyRenderingFiber.memoizedState\n : workInProgressHook.next;\n if (null !== nextWorkInProgressHook)\n (workInProgressHook = nextWorkInProgressHook),\n (currentHook = nextCurrentHook);\n else {\n if (null === nextCurrentHook) {\n if (null === currentlyRenderingFiber.alternate)\n throw Error(formatProdErrorMessage(467));\n throw Error(formatProdErrorMessage(310));\n }\n currentHook = nextCurrentHook;\n nextCurrentHook = {\n memoizedState: currentHook.memoizedState,\n baseState: currentHook.baseState,\n baseQueue: currentHook.baseQueue,\n queue: currentHook.queue,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook =\n nextCurrentHook)\n : (workInProgressHook = workInProgressHook.next = nextCurrentHook);\n }\n return workInProgressHook;\n}\nfunction createFunctionComponentUpdateQueue() {\n return { lastEffect: null, events: null, stores: null, memoCache: null };\n}\nfunction useThenable(thenable) {\n var index = thenableIndexCounter;\n thenableIndexCounter += 1;\n null === thenableState && (thenableState = []);\n thenable = trackUsedThenable(thenableState, thenable, index);\n index = currentlyRenderingFiber;\n null ===\n (null === workInProgressHook\n ? index.memoizedState\n : workInProgressHook.next) &&\n ((index = index.alternate),\n (ReactSharedInternals.H =\n null === index || null === index.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate));\n return thenable;\n}\nfunction use(usable) {\n if (null !== usable && \"object\" === typeof usable) {\n if (\"function\" === typeof usable.then) return useThenable(usable);\n if (usable.$$typeof === REACT_CONTEXT_TYPE) return readContext(usable);\n }\n throw Error(formatProdErrorMessage(438, String(usable)));\n}\nfunction useMemoCache(size) {\n var memoCache = null,\n updateQueue = currentlyRenderingFiber.updateQueue;\n null !== updateQueue && (memoCache = updateQueue.memoCache);\n if (null == memoCache) {\n var current = currentlyRenderingFiber.alternate;\n null !== current &&\n ((current = current.updateQueue),\n null !== current &&\n ((current = current.memoCache),\n null != current &&\n (memoCache = {\n data: current.data.map(function (array) {\n return array.slice();\n }),\n index: 0\n })));\n }\n null == memoCache && (memoCache = { data: [], index: 0 });\n null === updateQueue &&\n ((updateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = updateQueue));\n updateQueue.memoCache = memoCache;\n updateQueue = memoCache.data[memoCache.index];\n if (void 0 === updateQueue)\n for (\n updateQueue = memoCache.data[memoCache.index] = Array(size), current = 0;\n current < size;\n current++\n )\n updateQueue[current] = REACT_MEMO_CACHE_SENTINEL;\n memoCache.index++;\n return updateQueue;\n}\nfunction basicStateReducer(state, action) {\n return \"function\" === typeof action ? action(state) : action;\n}\nfunction updateReducer(reducer) {\n var hook = updateWorkInProgressHook();\n return updateReducerImpl(hook, currentHook, reducer);\n}\nfunction updateReducerImpl(hook, current, reducer) {\n var queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var baseQueue = hook.baseQueue,\n pendingQueue = queue.pending;\n if (null !== pendingQueue) {\n if (null !== baseQueue) {\n var baseFirst = baseQueue.next;\n baseQueue.next = pendingQueue.next;\n pendingQueue.next = baseFirst;\n }\n current.baseQueue = baseQueue = pendingQueue;\n queue.pending = null;\n }\n pendingQueue = hook.baseState;\n if (null === baseQueue) hook.memoizedState = pendingQueue;\n else {\n current = baseQueue.next;\n var newBaseQueueFirst = (baseFirst = null),\n newBaseQueueLast = null,\n update = current,\n didReadFromEntangledAsyncAction$60 = !1;\n do {\n var updateLane = update.lane & -536870913;\n if (\n updateLane !== update.lane\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n var revertLane = update.revertLane;\n if (0 === revertLane)\n null !== newBaseQueueLast &&\n (newBaseQueueLast = newBaseQueueLast.next =\n {\n lane: 0,\n revertLane: 0,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n else if ((renderLanes & revertLane) === revertLane) {\n update = update.next;\n revertLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n continue;\n } else\n (updateLane = {\n lane: 0,\n revertLane: update.revertLane,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = updateLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = updateLane),\n (currentlyRenderingFiber.lanes |= revertLane),\n (workInProgressRootSkippedLanes |= revertLane);\n updateLane = update.action;\n shouldDoubleInvokeUserFnsInHooksDEV &&\n reducer(pendingQueue, updateLane);\n pendingQueue = update.hasEagerState\n ? update.eagerState\n : reducer(pendingQueue, updateLane);\n } else\n (revertLane = {\n lane: updateLane,\n revertLane: update.revertLane,\n gesture: update.gesture,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = revertLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = revertLane),\n (currentlyRenderingFiber.lanes |= updateLane),\n (workInProgressRootSkippedLanes |= updateLane);\n update = update.next;\n } while (null !== update && update !== current);\n null === newBaseQueueLast\n ? (baseFirst = pendingQueue)\n : (newBaseQueueLast.next = newBaseQueueFirst);\n if (\n !objectIs(pendingQueue, hook.memoizedState) &&\n ((didReceiveUpdate = !0),\n didReadFromEntangledAsyncAction$60 &&\n ((reducer = currentEntangledActionThenable), null !== reducer))\n )\n throw reducer;\n hook.memoizedState = pendingQueue;\n hook.baseState = baseFirst;\n hook.baseQueue = newBaseQueueLast;\n queue.lastRenderedState = pendingQueue;\n }\n null === baseQueue && (queue.lanes = 0);\n return [hook.memoizedState, queue.dispatch];\n}\nfunction rerenderReducer(reducer) {\n var hook = updateWorkInProgressHook(),\n queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var dispatch = queue.dispatch,\n lastRenderPhaseUpdate = queue.pending,\n newState = hook.memoizedState;\n if (null !== lastRenderPhaseUpdate) {\n queue.pending = null;\n var update = (lastRenderPhaseUpdate = lastRenderPhaseUpdate.next);\n do (newState = reducer(newState, update.action)), (update = update.next);\n while (update !== lastRenderPhaseUpdate);\n objectIs(newState, hook.memoizedState) || (didReceiveUpdate = !0);\n hook.memoizedState = newState;\n null === hook.baseQueue && (hook.baseState = newState);\n queue.lastRenderedState = newState;\n }\n return [newState, dispatch];\n}\nfunction updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = updateWorkInProgressHook(),\n isHydrating$jscomp$0 = isHydrating;\n if (isHydrating$jscomp$0) {\n if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else getServerSnapshot = getSnapshot();\n var snapshotChanged = !objectIs(\n (currentHook || hook).memoizedState,\n getServerSnapshot\n );\n snapshotChanged &&\n ((hook.memoizedState = getServerSnapshot), (didReceiveUpdate = !0));\n hook = hook.queue;\n updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [\n subscribe\n ]);\n if (\n hook.getSnapshot !== getSnapshot ||\n snapshotChanged ||\n (null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1)\n ) {\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n hook,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));\n isHydrating$jscomp$0 ||\n 0 !== (renderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n return getServerSnapshot;\n}\nfunction pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {\n fiber.flags |= 16384;\n fiber = { getSnapshot: getSnapshot, value: renderedSnapshot };\n getSnapshot = currentlyRenderingFiber.updateQueue;\n null === getSnapshot\n ? ((getSnapshot = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = getSnapshot),\n (getSnapshot.stores = [fiber]))\n : ((renderedSnapshot = getSnapshot.stores),\n null === renderedSnapshot\n ? (getSnapshot.stores = [fiber])\n : renderedSnapshot.push(fiber));\n}\nfunction updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {\n inst.value = nextSnapshot;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n}\nfunction subscribeToStore(fiber, inst, subscribe) {\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n });\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction forceStoreRerender(fiber) {\n var root = enqueueConcurrentRenderForLane(fiber, 2);\n null !== root && scheduleUpdateOnFiber(root, fiber, 2);\n}\nfunction mountStateImpl(initialState) {\n var hook = mountWorkInProgressHook();\n if (\"function\" === typeof initialState) {\n var initialStateInitializer = initialState;\n initialState = initialStateInitializer();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n initialStateInitializer();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n }\n hook.memoizedState = hook.baseState = initialState;\n hook.queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState\n };\n return hook;\n}\nfunction updateOptimisticImpl(hook, current, passthrough, reducer) {\n hook.baseState = passthrough;\n return updateReducerImpl(\n hook,\n currentHook,\n \"function\" === typeof reducer ? reducer : basicStateReducer\n );\n}\nfunction dispatchActionState(\n fiber,\n actionQueue,\n setPendingState,\n setState,\n payload\n) {\n if (isRenderPhaseUpdate(fiber)) throw Error(formatProdErrorMessage(485));\n fiber = actionQueue.action;\n if (null !== fiber) {\n var actionNode = {\n payload: payload,\n action: fiber,\n next: null,\n isTransition: !0,\n status: \"pending\",\n value: null,\n reason: null,\n listeners: [],\n then: function (listener) {\n actionNode.listeners.push(listener);\n }\n };\n null !== ReactSharedInternals.T\n ? setPendingState(!0)\n : (actionNode.isTransition = !1);\n setState(actionNode);\n setPendingState = actionQueue.pending;\n null === setPendingState\n ? ((actionNode.next = actionQueue.pending = actionNode),\n runActionStateAction(actionQueue, actionNode))\n : ((actionNode.next = setPendingState.next),\n (actionQueue.pending = setPendingState.next = actionNode));\n }\n}\nfunction runActionStateAction(actionQueue, node) {\n var action = node.action,\n payload = node.payload,\n prevState = actionQueue.state;\n if (node.isTransition) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = action(prevState, payload),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n handleActionReturnValue(actionQueue, node, returnValue);\n } catch (error) {\n onActionError(actionQueue, node, error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n } else\n try {\n (prevTransition = action(prevState, payload)),\n handleActionReturnValue(actionQueue, node, prevTransition);\n } catch (error$66) {\n onActionError(actionQueue, node, error$66);\n }\n}\nfunction handleActionReturnValue(actionQueue, node, returnValue) {\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ? returnValue.then(\n function (nextState) {\n onActionSuccess(actionQueue, node, nextState);\n },\n function (error) {\n return onActionError(actionQueue, node, error);\n }\n )\n : onActionSuccess(actionQueue, node, returnValue);\n}\nfunction onActionSuccess(actionQueue, actionNode, nextState) {\n actionNode.status = \"fulfilled\";\n actionNode.value = nextState;\n notifyActionListeners(actionNode);\n actionQueue.state = nextState;\n actionNode = actionQueue.pending;\n null !== actionNode &&\n ((nextState = actionNode.next),\n nextState === actionNode\n ? (actionQueue.pending = null)\n : ((nextState = nextState.next),\n (actionNode.next = nextState),\n runActionStateAction(actionQueue, nextState)));\n}\nfunction onActionError(actionQueue, actionNode, error) {\n var last = actionQueue.pending;\n actionQueue.pending = null;\n if (null !== last) {\n last = last.next;\n do\n (actionNode.status = \"rejected\"),\n (actionNode.reason = error),\n notifyActionListeners(actionNode),\n (actionNode = actionNode.next);\n while (actionNode !== last);\n }\n actionQueue.action = null;\n}\nfunction notifyActionListeners(actionNode) {\n actionNode = actionNode.listeners;\n for (var i = 0; i < actionNode.length; i++) (0, actionNode[i])();\n}\nfunction actionStateReducer(oldState, newState) {\n return newState;\n}\nfunction mountActionState(action, initialStateProp) {\n if (isHydrating) {\n var ssrFormState = workInProgressRoot.formState;\n if (null !== ssrFormState) {\n a: {\n var JSCompiler_inline_result = currentlyRenderingFiber;\n if (isHydrating) {\n if (nextHydratableInstance) {\n b: {\n var JSCompiler_inline_result$jscomp$0 = nextHydratableInstance;\n for (\n var inRootOrSingleton = rootOrSingletonContext;\n 8 !== JSCompiler_inline_result$jscomp$0.nodeType;\n\n ) {\n if (!inRootOrSingleton) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n JSCompiler_inline_result$jscomp$0 = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n if (null === JSCompiler_inline_result$jscomp$0) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n }\n inRootOrSingleton = JSCompiler_inline_result$jscomp$0.data;\n JSCompiler_inline_result$jscomp$0 =\n \"F!\" === inRootOrSingleton || \"F\" === inRootOrSingleton\n ? JSCompiler_inline_result$jscomp$0\n : null;\n }\n if (JSCompiler_inline_result$jscomp$0) {\n nextHydratableInstance = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n JSCompiler_inline_result =\n \"F!\" === JSCompiler_inline_result$jscomp$0.data;\n break a;\n }\n }\n throwOnHydrationMismatch(JSCompiler_inline_result);\n }\n JSCompiler_inline_result = !1;\n }\n JSCompiler_inline_result && (initialStateProp = ssrFormState[0]);\n }\n }\n ssrFormState = mountWorkInProgressHook();\n ssrFormState.memoizedState = ssrFormState.baseState = initialStateProp;\n JSCompiler_inline_result = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: actionStateReducer,\n lastRenderedState: initialStateProp\n };\n ssrFormState.queue = JSCompiler_inline_result;\n ssrFormState = dispatchSetState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result\n );\n JSCompiler_inline_result.dispatch = ssrFormState;\n JSCompiler_inline_result = mountStateImpl(!1);\n inRootOrSingleton = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !1,\n JSCompiler_inline_result.queue\n );\n JSCompiler_inline_result = mountWorkInProgressHook();\n JSCompiler_inline_result$jscomp$0 = {\n state: initialStateProp,\n dispatch: null,\n action: action,\n pending: null\n };\n JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;\n ssrFormState = dispatchActionState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result$jscomp$0,\n inRootOrSingleton,\n ssrFormState\n );\n JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;\n JSCompiler_inline_result.memoizedState = action;\n return [initialStateProp, ssrFormState, !1];\n}\nfunction updateActionState(action) {\n var stateHook = updateWorkInProgressHook();\n return updateActionStateImpl(stateHook, currentHook, action);\n}\nfunction updateActionStateImpl(stateHook, currentStateHook, action) {\n currentStateHook = updateReducerImpl(\n stateHook,\n currentStateHook,\n actionStateReducer\n )[0];\n stateHook = updateReducer(basicStateReducer)[0];\n if (\n \"object\" === typeof currentStateHook &&\n null !== currentStateHook &&\n \"function\" === typeof currentStateHook.then\n )\n try {\n var state = useThenable(currentStateHook);\n } catch (x) {\n if (x === SuspenseException) throw SuspenseActionException;\n throw x;\n }\n else state = currentStateHook;\n currentStateHook = updateWorkInProgressHook();\n var actionQueue = currentStateHook.queue,\n dispatch = actionQueue.dispatch;\n action !== currentStateHook.memoizedState &&\n ((currentlyRenderingFiber.flags |= 2048),\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n actionStateActionEffect.bind(null, actionQueue, action),\n null\n ));\n return [state, dispatch, stateHook];\n}\nfunction actionStateActionEffect(actionQueue, action) {\n actionQueue.action = action;\n}\nfunction rerenderActionState(action) {\n var stateHook = updateWorkInProgressHook(),\n currentStateHook = currentHook;\n if (null !== currentStateHook)\n return updateActionStateImpl(stateHook, currentStateHook, action);\n updateWorkInProgressHook();\n stateHook = stateHook.memoizedState;\n currentStateHook = updateWorkInProgressHook();\n var dispatch = currentStateHook.queue.dispatch;\n currentStateHook.memoizedState = action;\n return [stateHook, dispatch, !1];\n}\nfunction pushSimpleEffect(tag, inst, create, deps) {\n tag = { tag: tag, create: create, deps: deps, inst: inst, next: null };\n inst = currentlyRenderingFiber.updateQueue;\n null === inst &&\n ((inst = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = inst));\n create = inst.lastEffect;\n null === create\n ? (inst.lastEffect = tag.next = tag)\n : ((deps = create.next),\n (create.next = tag),\n (tag.next = deps),\n (inst.lastEffect = tag));\n return tag;\n}\nfunction updateRef() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = mountWorkInProgressHook();\n currentlyRenderingFiber.flags |= fiberFlags;\n hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n { destroy: void 0 },\n create,\n void 0 === deps ? null : deps\n );\n}\nfunction updateEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var inst = hook.memoizedState.inst;\n null !== currentHook &&\n null !== deps &&\n areHookInputsEqual(deps, currentHook.memoizedState.deps)\n ? (hook.memoizedState = pushSimpleEffect(hookFlags, inst, create, deps))\n : ((currentlyRenderingFiber.flags |= fiberFlags),\n (hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n inst,\n create,\n deps\n )));\n}\nfunction mountEffect(create, deps) {\n mountEffectImpl(8390656, 8, create, deps);\n}\nfunction updateEffect(create, deps) {\n updateEffectImpl(2048, 8, create, deps);\n}\nfunction useEffectEventImpl(payload) {\n currentlyRenderingFiber.flags |= 4;\n var componentUpdateQueue = currentlyRenderingFiber.updateQueue;\n if (null === componentUpdateQueue)\n (componentUpdateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = componentUpdateQueue),\n (componentUpdateQueue.events = [payload]);\n else {\n var events = componentUpdateQueue.events;\n null === events\n ? (componentUpdateQueue.events = [payload])\n : events.push(payload);\n }\n}\nfunction updateEvent(callback) {\n var ref = updateWorkInProgressHook().memoizedState;\n useEffectEventImpl({ ref: ref, nextImpl: callback });\n return function () {\n if (0 !== (executionContext & 2)) throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n}\nfunction updateInsertionEffect(create, deps) {\n return updateEffectImpl(4, 2, create, deps);\n}\nfunction updateLayoutEffect(create, deps) {\n return updateEffectImpl(4, 4, create, deps);\n}\nfunction imperativeHandleEffect(create, ref) {\n if (\"function\" === typeof ref) {\n create = create();\n var refCleanup = ref(create);\n return function () {\n \"function\" === typeof refCleanup ? refCleanup() : ref(null);\n };\n }\n if (null !== ref && void 0 !== ref)\n return (\n (create = create()),\n (ref.current = create),\n function () {\n ref.current = null;\n }\n );\n}\nfunction updateImperativeHandle(ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n updateEffectImpl(4, 4, imperativeHandleEffect.bind(null, create, ref), deps);\n}\nfunction mountDebugValue() {}\nfunction updateCallback(callback, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n hook.memoizedState = [callback, deps];\n return callback;\n}\nfunction updateMemo(nextCreate, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n prevState = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [prevState, deps];\n return prevState;\n}\nfunction mountDeferredValueImpl(hook, value, initialValue) {\n if (\n void 0 === initialValue ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (hook.memoizedState = value);\n hook.memoizedState = initialValue;\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return initialValue;\n}\nfunction updateDeferredValueImpl(hook, prevValue, value, initialValue) {\n if (objectIs(value, prevValue)) return value;\n if (null !== currentTreeHiddenStackCursor.current)\n return (\n (hook = mountDeferredValueImpl(hook, value, initialValue)),\n objectIs(hook, prevValue) || (didReceiveUpdate = !0),\n hook\n );\n if (\n 0 === (renderLanes & 42) ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (didReceiveUpdate = !0), (hook.memoizedState = value);\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return prevValue;\n}\nfunction startTransition(fiber, queue, pendingState, finishedState, callback) {\n var previousPriority = ReactDOMSharedInternals.p;\n ReactDOMSharedInternals.p =\n 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8;\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n dispatchOptimisticSetState(fiber, !1, queue, pendingState);\n try {\n var returnValue = callback(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n if (\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ) {\n var thenableForFinishedState = chainThenableValue(\n returnValue,\n finishedState\n );\n dispatchSetStateInternal(\n fiber,\n queue,\n thenableForFinishedState,\n requestUpdateLane(fiber)\n );\n } else\n dispatchSetStateInternal(\n fiber,\n queue,\n finishedState,\n requestUpdateLane(fiber)\n );\n } catch (error) {\n dispatchSetStateInternal(\n fiber,\n queue,\n { then: function () {}, status: \"rejected\", reason: error },\n requestUpdateLane()\n );\n } finally {\n (ReactDOMSharedInternals.p = previousPriority),\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n}\nfunction noop() {}\nfunction startHostTransition(formFiber, pendingState, action, formData) {\n if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));\n var queue = ensureFormComponentIsStateful(formFiber).queue;\n startTransition(\n formFiber,\n queue,\n pendingState,\n sharedNotPendingObject,\n null === action\n ? noop\n : function () {\n requestFormReset$1(formFiber);\n return action(formData);\n }\n );\n}\nfunction ensureFormComponentIsStateful(formFiber) {\n var existingStateHook = formFiber.memoizedState;\n if (null !== existingStateHook) return existingStateHook;\n existingStateHook = {\n memoizedState: sharedNotPendingObject,\n baseState: sharedNotPendingObject,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: sharedNotPendingObject\n },\n next: null\n };\n var initialResetState = {};\n existingStateHook.next = {\n memoizedState: initialResetState,\n baseState: initialResetState,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialResetState\n },\n next: null\n };\n formFiber.memoizedState = existingStateHook;\n formFiber = formFiber.alternate;\n null !== formFiber && (formFiber.memoizedState = existingStateHook);\n return existingStateHook;\n}\nfunction requestFormReset$1(formFiber) {\n var stateHook = ensureFormComponentIsStateful(formFiber);\n null === stateHook.next && (stateHook = formFiber.alternate.memoizedState);\n dispatchSetStateInternal(\n formFiber,\n stateHook.next.queue,\n {},\n requestUpdateLane()\n );\n}\nfunction useHostTransitionStatus() {\n return readContext(HostTransitionContext);\n}\nfunction updateId() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction updateRefresh() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction refreshCache(fiber) {\n for (var provider = fiber.return; null !== provider; ) {\n switch (provider.tag) {\n case 24:\n case 3:\n var lane = requestUpdateLane();\n fiber = createUpdate(lane);\n var root$69 = enqueueUpdate(provider, fiber, lane);\n null !== root$69 &&\n (scheduleUpdateOnFiber(root$69, provider, lane),\n entangleTransitions(root$69, provider, lane));\n provider = { cache: createCache() };\n fiber.payload = provider;\n return;\n }\n provider = provider.return;\n }\n}\nfunction dispatchReducerAction(fiber, queue, action) {\n var lane = requestUpdateLane();\n action = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n isRenderPhaseUpdate(fiber)\n ? enqueueRenderPhaseUpdate(queue, action)\n : ((action = enqueueConcurrentHookUpdate(fiber, queue, action, lane)),\n null !== action &&\n (scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane)));\n}\nfunction dispatchSetState(fiber, queue, action) {\n var lane = requestUpdateLane();\n dispatchSetStateInternal(fiber, queue, action, lane);\n}\nfunction dispatchSetStateInternal(fiber, queue, action, lane) {\n var update = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) enqueueRenderPhaseUpdate(queue, update);\n else {\n var alternate = fiber.alternate;\n if (\n 0 === fiber.lanes &&\n (null === alternate || 0 === alternate.lanes) &&\n ((alternate = queue.lastRenderedReducer), null !== alternate)\n )\n try {\n var currentState = queue.lastRenderedState,\n eagerState = alternate(currentState, action);\n update.hasEagerState = !0;\n update.eagerState = eagerState;\n if (objectIs(eagerState, currentState))\n return (\n enqueueUpdate$1(fiber, queue, update, 0),\n null === workInProgressRoot && finishQueueingConcurrentUpdates(),\n !1\n );\n } catch (error) {\n } finally {\n }\n action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n if (null !== action)\n return (\n scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane),\n !0\n );\n }\n return !1;\n}\nfunction dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {\n action = {\n lane: 2,\n revertLane: requestTransitionLane(),\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) {\n if (throwIfDuringRender) throw Error(formatProdErrorMessage(479));\n } else\n (throwIfDuringRender = enqueueConcurrentHookUpdate(\n fiber,\n queue,\n action,\n 2\n )),\n null !== throwIfDuringRender &&\n scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);\n}\nfunction isRenderPhaseUpdate(fiber) {\n var alternate = fiber.alternate;\n return (\n fiber === currentlyRenderingFiber ||\n (null !== alternate && alternate === currentlyRenderingFiber)\n );\n}\nfunction enqueueRenderPhaseUpdate(queue, update) {\n didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =\n !0;\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n}\nfunction entangleTransitionUpdate(root, queue, lane) {\n if (0 !== (lane & 4194048)) {\n var queueLanes = queue.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n queue.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nvar ContextOnlyDispatcher = {\n readContext: readContext,\n use: use,\n useCallback: throwInvalidHookError,\n useContext: throwInvalidHookError,\n useEffect: throwInvalidHookError,\n useImperativeHandle: throwInvalidHookError,\n useLayoutEffect: throwInvalidHookError,\n useInsertionEffect: throwInvalidHookError,\n useMemo: throwInvalidHookError,\n useReducer: throwInvalidHookError,\n useRef: throwInvalidHookError,\n useState: throwInvalidHookError,\n useDebugValue: throwInvalidHookError,\n useDeferredValue: throwInvalidHookError,\n useTransition: throwInvalidHookError,\n useSyncExternalStore: throwInvalidHookError,\n useId: throwInvalidHookError,\n useHostTransitionStatus: throwInvalidHookError,\n useFormState: throwInvalidHookError,\n useActionState: throwInvalidHookError,\n useOptimistic: throwInvalidHookError,\n useMemoCache: throwInvalidHookError,\n useCacheRefresh: throwInvalidHookError\n};\nContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;\nvar HooksDispatcherOnMount = {\n readContext: readContext,\n use: use,\n useCallback: function (callback, deps) {\n mountWorkInProgressHook().memoizedState = [\n callback,\n void 0 === deps ? null : deps\n ];\n return callback;\n },\n useContext: readContext,\n useEffect: mountEffect,\n useImperativeHandle: function (ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n mountEffectImpl(\n 4194308,\n 4,\n imperativeHandleEffect.bind(null, create, ref),\n deps\n );\n },\n useLayoutEffect: function (create, deps) {\n return mountEffectImpl(4194308, 4, create, deps);\n },\n useInsertionEffect: function (create, deps) {\n mountEffectImpl(4, 2, create, deps);\n },\n useMemo: function (nextCreate, deps) {\n var hook = mountWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var nextValue = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [nextValue, deps];\n return nextValue;\n },\n useReducer: function (reducer, initialArg, init) {\n var hook = mountWorkInProgressHook();\n if (void 0 !== init) {\n var initialState = init(initialArg);\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n init(initialArg);\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n } else initialState = initialArg;\n hook.memoizedState = hook.baseState = initialState;\n reducer = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: reducer,\n lastRenderedState: initialState\n };\n hook.queue = reducer;\n reducer = reducer.dispatch = dispatchReducerAction.bind(\n null,\n currentlyRenderingFiber,\n reducer\n );\n return [hook.memoizedState, reducer];\n },\n useRef: function (initialValue) {\n var hook = mountWorkInProgressHook();\n initialValue = { current: initialValue };\n return (hook.memoizedState = initialValue);\n },\n useState: function (initialState) {\n initialState = mountStateImpl(initialState);\n var queue = initialState.queue,\n dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);\n queue.dispatch = dispatch;\n return [initialState.memoizedState, dispatch];\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = mountWorkInProgressHook();\n return mountDeferredValueImpl(hook, value, initialValue);\n },\n useTransition: function () {\n var stateHook = mountStateImpl(!1);\n stateHook = startTransition.bind(\n null,\n currentlyRenderingFiber,\n stateHook.queue,\n !0,\n !1\n );\n mountWorkInProgressHook().memoizedState = stateHook;\n return [!1, stateHook];\n },\n useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = mountWorkInProgressHook();\n if (isHydrating) {\n if (void 0 === getServerSnapshot)\n throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else {\n getServerSnapshot = getSnapshot();\n if (null === workInProgressRoot)\n throw Error(formatProdErrorMessage(349));\n 0 !== (workInProgressRootRenderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n hook.memoizedState = getServerSnapshot;\n var inst = { value: getServerSnapshot, getSnapshot: getSnapshot };\n hook.queue = inst;\n mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [\n subscribe\n ]);\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n inst,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n return getServerSnapshot;\n },\n useId: function () {\n var hook = mountWorkInProgressHook(),\n identifierPrefix = workInProgressRoot.identifierPrefix;\n if (isHydrating) {\n var JSCompiler_inline_result = treeContextOverflow;\n var idWithLeadingBit = treeContextId;\n JSCompiler_inline_result =\n (\n idWithLeadingBit & ~(1 << (32 - clz32(idWithLeadingBit) - 1))\n ).toString(32) + JSCompiler_inline_result;\n identifierPrefix =\n \"_\" + identifierPrefix + \"R_\" + JSCompiler_inline_result;\n JSCompiler_inline_result = localIdCounter++;\n 0 < JSCompiler_inline_result &&\n (identifierPrefix += \"H\" + JSCompiler_inline_result.toString(32));\n identifierPrefix += \"_\";\n } else\n (JSCompiler_inline_result = globalClientIdCounter++),\n (identifierPrefix =\n \"_\" +\n identifierPrefix +\n \"r_\" +\n JSCompiler_inline_result.toString(32) +\n \"_\");\n return (hook.memoizedState = identifierPrefix);\n },\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: mountActionState,\n useActionState: mountActionState,\n useOptimistic: function (passthrough) {\n var hook = mountWorkInProgressHook();\n hook.memoizedState = hook.baseState = passthrough;\n var queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: null,\n lastRenderedState: null\n };\n hook.queue = queue;\n hook = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !0,\n queue\n );\n queue.dispatch = hook;\n return [passthrough, hook];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: function () {\n return (mountWorkInProgressHook().memoizedState = refreshCache.bind(\n null,\n currentlyRenderingFiber\n ));\n },\n useEffectEvent: function (callback) {\n var hook = mountWorkInProgressHook(),\n ref = { impl: callback };\n hook.memoizedState = ref;\n return function () {\n if (0 !== (executionContext & 2))\n throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n }\n },\n HooksDispatcherOnUpdate = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: updateReducer,\n useRef: updateRef,\n useState: function () {\n return updateReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = updateReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: updateActionState,\n useActionState: updateActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n };\nHooksDispatcherOnUpdate.useEffectEvent = updateEvent;\nvar HooksDispatcherOnRerender = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: rerenderReducer,\n useRef: updateRef,\n useState: function () {\n return rerenderReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return null === currentHook\n ? mountDeferredValueImpl(hook, value, initialValue)\n : updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = rerenderReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: rerenderActionState,\n useActionState: rerenderActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n if (null !== currentHook)\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n hook.baseState = passthrough;\n return [passthrough, hook.queue.dispatch];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n};\nHooksDispatcherOnRerender.useEffectEvent = updateEvent;\nfunction applyDerivedStateFromProps(\n workInProgress,\n ctor,\n getDerivedStateFromProps,\n nextProps\n) {\n ctor = workInProgress.memoizedState;\n getDerivedStateFromProps = getDerivedStateFromProps(nextProps, ctor);\n getDerivedStateFromProps =\n null === getDerivedStateFromProps || void 0 === getDerivedStateFromProps\n ? ctor\n : assign({}, ctor, getDerivedStateFromProps);\n workInProgress.memoizedState = getDerivedStateFromProps;\n 0 === workInProgress.lanes &&\n (workInProgress.updateQueue.baseState = getDerivedStateFromProps);\n}\nvar classComponentUpdater = {\n enqueueSetState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueReplaceState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 1;\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueForceUpdate: function (inst, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 2;\n void 0 !== callback && null !== callback && (update.callback = callback);\n callback = enqueueUpdate(inst, update, lane);\n null !== callback &&\n (scheduleUpdateOnFiber(callback, inst, lane),\n entangleTransitions(callback, inst, lane));\n }\n};\nfunction checkShouldComponentUpdate(\n workInProgress,\n ctor,\n oldProps,\n newProps,\n oldState,\n newState,\n nextContext\n) {\n workInProgress = workInProgress.stateNode;\n return \"function\" === typeof workInProgress.shouldComponentUpdate\n ? workInProgress.shouldComponentUpdate(newProps, newState, nextContext)\n : ctor.prototype && ctor.prototype.isPureReactComponent\n ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)\n : !0;\n}\nfunction callComponentWillReceiveProps(\n workInProgress,\n instance,\n newProps,\n nextContext\n) {\n workInProgress = instance.state;\n \"function\" === typeof instance.componentWillReceiveProps &&\n instance.componentWillReceiveProps(newProps, nextContext);\n \"function\" === typeof instance.UNSAFE_componentWillReceiveProps &&\n instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);\n instance.state !== workInProgress &&\n classComponentUpdater.enqueueReplaceState(instance, instance.state, null);\n}\nfunction resolveClassComponentProps(Component, baseProps) {\n var newProps = baseProps;\n if (\"ref\" in baseProps) {\n newProps = {};\n for (var propName in baseProps)\n \"ref\" !== propName && (newProps[propName] = baseProps[propName]);\n }\n if ((Component = Component.defaultProps)) {\n newProps === baseProps && (newProps = assign({}, newProps));\n for (var propName$73 in Component)\n void 0 === newProps[propName$73] &&\n (newProps[propName$73] = Component[propName$73]);\n }\n return newProps;\n}\nfunction defaultOnUncaughtError(error) {\n reportGlobalError(error);\n}\nfunction defaultOnCaughtError(error) {\n console.error(error);\n}\nfunction defaultOnRecoverableError(error) {\n reportGlobalError(error);\n}\nfunction logUncaughtError(root, errorInfo) {\n try {\n var onUncaughtError = root.onUncaughtError;\n onUncaughtError(errorInfo.value, { componentStack: errorInfo.stack });\n } catch (e$74) {\n setTimeout(function () {\n throw e$74;\n });\n }\n}\nfunction logCaughtError(root, boundary, errorInfo) {\n try {\n var onCaughtError = root.onCaughtError;\n onCaughtError(errorInfo.value, {\n componentStack: errorInfo.stack,\n errorBoundary: 1 === boundary.tag ? boundary.stateNode : null\n });\n } catch (e$75) {\n setTimeout(function () {\n throw e$75;\n });\n }\n}\nfunction createRootErrorUpdate(root, errorInfo, lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n lane.payload = { element: null };\n lane.callback = function () {\n logUncaughtError(root, errorInfo);\n };\n return lane;\n}\nfunction createClassErrorUpdate(lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n return lane;\n}\nfunction initializeClassErrorUpdate(update, root, fiber, errorInfo) {\n var getDerivedStateFromError = fiber.type.getDerivedStateFromError;\n if (\"function\" === typeof getDerivedStateFromError) {\n var error = errorInfo.value;\n update.payload = function () {\n return getDerivedStateFromError(error);\n };\n update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n };\n }\n var inst = fiber.stateNode;\n null !== inst &&\n \"function\" === typeof inst.componentDidCatch &&\n (update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n \"function\" !== typeof getDerivedStateFromError &&\n (null === legacyErrorBoundariesThatAlreadyFailed\n ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this]))\n : legacyErrorBoundariesThatAlreadyFailed.add(this));\n var stack = errorInfo.stack;\n this.componentDidCatch(errorInfo.value, {\n componentStack: null !== stack ? stack : \"\"\n });\n });\n}\nfunction throwException(\n root,\n returnFiber,\n sourceFiber,\n value,\n rootRenderLanes\n) {\n sourceFiber.flags |= 32768;\n if (\n null !== value &&\n \"object\" === typeof value &&\n \"function\" === typeof value.then\n ) {\n returnFiber = sourceFiber.alternate;\n null !== returnFiber &&\n propagateParentContextChanges(\n returnFiber,\n sourceFiber,\n rootRenderLanes,\n !0\n );\n sourceFiber = suspenseHandlerStackCursor.current;\n if (null !== sourceFiber) {\n switch (sourceFiber.tag) {\n case 31:\n case 13:\n return (\n null === shellBoundary\n ? renderDidSuspendDelayIfPossible()\n : null === sourceFiber.alternate &&\n 0 === workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 3),\n (sourceFiber.flags &= -257),\n (sourceFiber.flags |= 65536),\n (sourceFiber.lanes = rootRenderLanes),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? (sourceFiber.updateQueue = new Set([value]))\n : returnFiber.add(value),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n case 22:\n return (\n (sourceFiber.flags |= 65536),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? ((returnFiber = {\n transitions: null,\n markerInstances: null,\n retryQueue: new Set([value])\n }),\n (sourceFiber.updateQueue = returnFiber))\n : ((sourceFiber = returnFiber.retryQueue),\n null === sourceFiber\n ? (returnFiber.retryQueue = new Set([value]))\n : sourceFiber.add(value)),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n }\n throw Error(formatProdErrorMessage(435, sourceFiber.tag));\n }\n attachPingListener(root, value, rootRenderLanes);\n renderDidSuspendDelayIfPossible();\n return !1;\n }\n if (isHydrating)\n return (\n (returnFiber = suspenseHandlerStackCursor.current),\n null !== returnFiber\n ? (0 === (returnFiber.flags & 65536) && (returnFiber.flags |= 256),\n (returnFiber.flags |= 65536),\n (returnFiber.lanes = rootRenderLanes),\n value !== HydrationMismatchException &&\n ((root = Error(formatProdErrorMessage(422), { cause: value })),\n queueHydrationError(createCapturedValueAtFiber(root, sourceFiber))))\n : (value !== HydrationMismatchException &&\n ((returnFiber = Error(formatProdErrorMessage(423), {\n cause: value\n })),\n queueHydrationError(\n createCapturedValueAtFiber(returnFiber, sourceFiber)\n )),\n (root = root.current.alternate),\n (root.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (root.lanes |= rootRenderLanes),\n (value = createCapturedValueAtFiber(value, sourceFiber)),\n (rootRenderLanes = createRootErrorUpdate(\n root.stateNode,\n value,\n rootRenderLanes\n )),\n enqueueCapturedUpdate(root, rootRenderLanes),\n 4 !== workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 2)),\n !1\n );\n var wrapperError = Error(formatProdErrorMessage(520), { cause: value });\n wrapperError = createCapturedValueAtFiber(wrapperError, sourceFiber);\n null === workInProgressRootConcurrentErrors\n ? (workInProgressRootConcurrentErrors = [wrapperError])\n : workInProgressRootConcurrentErrors.push(wrapperError);\n 4 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2);\n if (null === returnFiber) return !0;\n value = createCapturedValueAtFiber(value, sourceFiber);\n sourceFiber = returnFiber;\n do {\n switch (sourceFiber.tag) {\n case 3:\n return (\n (sourceFiber.flags |= 65536),\n (root = rootRenderLanes & -rootRenderLanes),\n (sourceFiber.lanes |= root),\n (root = createRootErrorUpdate(sourceFiber.stateNode, value, root)),\n enqueueCapturedUpdate(sourceFiber, root),\n !1\n );\n case 1:\n if (\n ((returnFiber = sourceFiber.type),\n (wrapperError = sourceFiber.stateNode),\n 0 === (sourceFiber.flags & 128) &&\n (\"function\" === typeof returnFiber.getDerivedStateFromError ||\n (null !== wrapperError &&\n \"function\" === typeof wrapperError.componentDidCatch &&\n (null === legacyErrorBoundariesThatAlreadyFailed ||\n !legacyErrorBoundariesThatAlreadyFailed.has(wrapperError)))))\n )\n return (\n (sourceFiber.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (sourceFiber.lanes |= rootRenderLanes),\n (rootRenderLanes = createClassErrorUpdate(rootRenderLanes)),\n initializeClassErrorUpdate(\n rootRenderLanes,\n root,\n sourceFiber,\n value\n ),\n enqueueCapturedUpdate(sourceFiber, rootRenderLanes),\n !1\n );\n }\n sourceFiber = sourceFiber.return;\n } while (null !== sourceFiber);\n return !1;\n}\nvar SelectiveHydrationException = Error(formatProdErrorMessage(461)),\n didReceiveUpdate = !1;\nfunction reconcileChildren(current, workInProgress, nextChildren, renderLanes) {\n workInProgress.child =\n null === current\n ? mountChildFibers(workInProgress, null, nextChildren, renderLanes)\n : reconcileChildFibers(\n workInProgress,\n current.child,\n nextChildren,\n renderLanes\n );\n}\nfunction updateForwardRef(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n Component = Component.render;\n var ref = workInProgress.ref;\n if (\"ref\" in nextProps) {\n var propsWithoutRef = {};\n for (var key in nextProps)\n \"ref\" !== key && (propsWithoutRef[key] = nextProps[key]);\n } else propsWithoutRef = nextProps;\n prepareToReadContext(workInProgress);\n nextProps = renderWithHooks(\n current,\n workInProgress,\n Component,\n propsWithoutRef,\n ref,\n renderLanes\n );\n key = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && key && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null === current) {\n var type = Component.type;\n if (\n \"function\" === typeof type &&\n !shouldConstruct(type) &&\n void 0 === type.defaultProps &&\n null === Component.compare\n )\n return (\n (workInProgress.tag = 15),\n (workInProgress.type = type),\n updateSimpleMemoComponent(\n current,\n workInProgress,\n type,\n nextProps,\n renderLanes\n )\n );\n current = createFiberFromTypeAndProps(\n Component.type,\n null,\n nextProps,\n workInProgress,\n workInProgress.mode,\n renderLanes\n );\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n }\n type = current.child;\n if (!checkScheduledUpdateOrContext(current, renderLanes)) {\n var prevProps = type.memoizedProps;\n Component = Component.compare;\n Component = null !== Component ? Component : shallowEqual;\n if (Component(prevProps, nextProps) && current.ref === workInProgress.ref)\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n }\n workInProgress.flags |= 1;\n current = createWorkInProgress(type, nextProps);\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n}\nfunction updateSimpleMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null !== current) {\n var prevProps = current.memoizedProps;\n if (\n shallowEqual(prevProps, nextProps) &&\n current.ref === workInProgress.ref\n )\n if (\n ((didReceiveUpdate = !1),\n (workInProgress.pendingProps = nextProps = prevProps),\n checkScheduledUpdateOrContext(current, renderLanes))\n )\n 0 !== (current.flags & 131072) && (didReceiveUpdate = !0);\n else\n return (\n (workInProgress.lanes = current.lanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n }\n return updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n );\n}\nfunction updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n nextProps\n) {\n var nextChildren = nextProps.children,\n prevState = null !== current ? current.memoizedState : null;\n null === current &&\n null === workInProgress.stateNode &&\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n if (\"hidden\" === nextProps.mode) {\n if (0 !== (workInProgress.flags & 128)) {\n prevState =\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes;\n if (null !== current) {\n nextProps = workInProgress.child = current.child;\n for (nextChildren = 0; null !== nextProps; )\n (nextChildren =\n nextChildren | nextProps.lanes | nextProps.childLanes),\n (nextProps = nextProps.sibling);\n nextProps = nextChildren & ~prevState;\n } else (nextProps = 0), (workInProgress.child = null);\n return deferHiddenOffscreenComponent(\n current,\n workInProgress,\n prevState,\n renderLanes,\n nextProps\n );\n }\n if (0 !== (renderLanes & 536870912))\n (workInProgress.memoizedState = { baseLanes: 0, cachePool: null }),\n null !== current &&\n pushTransition(\n workInProgress,\n null !== prevState ? prevState.cachePool : null\n ),\n null !== prevState\n ? pushHiddenContext(workInProgress, prevState)\n : reuseHiddenContextOnStack(),\n pushOffscreenSuspenseHandler(workInProgress);\n else\n return (\n (nextProps = workInProgress.lanes = 536870912),\n deferHiddenOffscreenComponent(\n current,\n workInProgress,\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes,\n renderLanes,\n nextProps\n )\n );\n } else\n null !== prevState\n ? (pushTransition(workInProgress, prevState.cachePool),\n pushHiddenContext(workInProgress, prevState),\n reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.memoizedState = null))\n : (null !== current && pushTransition(workInProgress, null),\n reuseHiddenContextOnStack(),\n reuseSuspenseHandlerOnStack(workInProgress));\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nfunction bailoutOffscreenComponent(current, workInProgress) {\n (null !== current && 22 === current.tag) ||\n null !== workInProgress.stateNode ||\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n return workInProgress.sibling;\n}\nfunction deferHiddenOffscreenComponent(\n current,\n workInProgress,\n nextBaseLanes,\n renderLanes,\n remainingChildLanes\n) {\n var JSCompiler_inline_result = peekCacheFromPool();\n JSCompiler_inline_result =\n null === JSCompiler_inline_result\n ? null\n : { parent: CacheContext._currentValue, pool: JSCompiler_inline_result };\n workInProgress.memoizedState = {\n baseLanes: nextBaseLanes,\n cachePool: JSCompiler_inline_result\n };\n null !== current && pushTransition(workInProgress, null);\n reuseHiddenContextOnStack();\n pushOffscreenSuspenseHandler(workInProgress);\n null !== current &&\n propagateParentContextChanges(current, workInProgress, renderLanes, !0);\n workInProgress.childLanes = remainingChildLanes;\n return null;\n}\nfunction mountActivityChildren(workInProgress, nextProps) {\n nextProps = mountWorkInProgressOffscreenFiber(\n { mode: nextProps.mode, children: nextProps.children },\n workInProgress.mode\n );\n nextProps.ref = workInProgress.ref;\n workInProgress.child = nextProps;\n nextProps.return = workInProgress;\n return nextProps;\n}\nfunction retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountActivityChildren(workInProgress, workInProgress.pendingProps);\n current.flags |= 2;\n popSuspenseHandler(workInProgress);\n workInProgress.memoizedState = null;\n return current;\n}\nfunction updateActivityComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n didSuspend = 0 !== (workInProgress.flags & 128);\n workInProgress.flags &= -129;\n if (null === current) {\n if (isHydrating) {\n if (\"hidden\" === nextProps.mode)\n return (\n (current = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.lanes = 536870912),\n bailoutOffscreenComponent(null, current)\n );\n pushDehydratedActivitySuspenseHandler(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" === current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n workInProgress.lanes = 536870912;\n return null;\n }\n return mountActivityChildren(workInProgress, nextProps);\n }\n var prevState = current.memoizedState;\n if (null !== prevState) {\n var dehydrated = prevState.dehydrated;\n pushDehydratedActivitySuspenseHandler(workInProgress);\n if (didSuspend)\n if (workInProgress.flags & 256)\n (workInProgress.flags &= -257),\n (workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n ));\n else if (null !== workInProgress.memoizedState)\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null);\n else throw Error(formatProdErrorMessage(558));\n else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (didSuspend = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || didSuspend)\n ) {\n nextProps = workInProgressRoot;\n if (\n null !== nextProps &&\n ((dehydrated = getBumpedLaneForHydration(nextProps, renderLanes)),\n 0 !== dehydrated && dehydrated !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = dehydrated),\n enqueueConcurrentRenderForLane(current, dehydrated),\n scheduleUpdateOnFiber(nextProps, current, dehydrated),\n SelectiveHydrationException)\n );\n renderDidSuspendDelayIfPossible();\n workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n (current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(dehydrated.nextSibling)),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.flags |= 4096);\n return workInProgress;\n }\n current = createWorkInProgress(current.child, {\n mode: nextProps.mode,\n children: nextProps.children\n });\n current.ref = workInProgress.ref;\n workInProgress.child = current;\n current.return = workInProgress;\n return current;\n}\nfunction markRef(current, workInProgress) {\n var ref = workInProgress.ref;\n if (null === ref)\n null !== current &&\n null !== current.ref &&\n (workInProgress.flags |= 4194816);\n else {\n if (\"function\" !== typeof ref && \"object\" !== typeof ref)\n throw Error(formatProdErrorMessage(284));\n if (null === current || current.ref !== ref)\n workInProgress.flags |= 4194816;\n }\n}\nfunction updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n Component = renderWithHooks(\n current,\n workInProgress,\n Component,\n nextProps,\n void 0,\n renderLanes\n );\n nextProps = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && nextProps && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, Component, renderLanes);\n return workInProgress.child;\n}\nfunction replayFunctionComponent(\n current,\n workInProgress,\n nextProps,\n Component,\n secondArg,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n workInProgress.updateQueue = null;\n nextProps = renderWithHooksAgain(\n workInProgress,\n Component,\n nextProps,\n secondArg\n );\n finishRenderingHooks(current);\n Component = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && Component && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateClassComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n if (null === workInProgress.stateNode) {\n var context = emptyContextObject,\n contextType = Component.contextType;\n \"object\" === typeof contextType &&\n null !== contextType &&\n (context = readContext(contextType));\n context = new Component(nextProps, context);\n workInProgress.memoizedState =\n null !== context.state && void 0 !== context.state ? context.state : null;\n context.updater = classComponentUpdater;\n workInProgress.stateNode = context;\n context._reactInternals = workInProgress;\n context = workInProgress.stateNode;\n context.props = nextProps;\n context.state = workInProgress.memoizedState;\n context.refs = {};\n initializeUpdateQueue(workInProgress);\n contextType = Component.contextType;\n context.context =\n \"object\" === typeof contextType && null !== contextType\n ? readContext(contextType)\n : emptyContextObject;\n context.state = workInProgress.memoizedState;\n contextType = Component.getDerivedStateFromProps;\n \"function\" === typeof contextType &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n contextType,\n nextProps\n ),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof Component.getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n ((contextType = context.state),\n \"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount(),\n contextType !== context.state &&\n classComponentUpdater.enqueueReplaceState(context, context.state, null),\n processUpdateQueue(workInProgress, nextProps, context, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction(),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308);\n nextProps = !0;\n } else if (null === current) {\n context = workInProgress.stateNode;\n var unresolvedOldProps = workInProgress.memoizedProps,\n oldProps = resolveClassComponentProps(Component, unresolvedOldProps);\n context.props = oldProps;\n var oldContext = context.context,\n contextType$jscomp$0 = Component.contextType;\n contextType = emptyContextObject;\n \"object\" === typeof contextType$jscomp$0 &&\n null !== contextType$jscomp$0 &&\n (contextType = readContext(contextType$jscomp$0));\n var getDerivedStateFromProps = Component.getDerivedStateFromProps;\n contextType$jscomp$0 =\n \"function\" === typeof getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate;\n unresolvedOldProps = workInProgress.pendingProps !== unresolvedOldProps;\n contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((unresolvedOldProps || oldContext !== contextType) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n contextType\n ));\n hasForceUpdate = !1;\n var oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n oldContext = workInProgress.memoizedState;\n unresolvedOldProps || oldState !== oldContext || hasForceUpdate\n ? (\"function\" === typeof getDerivedStateFromProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n getDerivedStateFromProps,\n nextProps\n ),\n (oldContext = workInProgress.memoizedState)),\n (oldProps =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n oldProps,\n nextProps,\n oldState,\n oldContext,\n contextType\n ))\n ? (contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n (\"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount()),\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = oldContext)),\n (context.props = nextProps),\n (context.state = oldContext),\n (context.context = contextType),\n (nextProps = oldProps))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (nextProps = !1));\n } else {\n context = workInProgress.stateNode;\n cloneUpdateQueue(current, workInProgress);\n contextType = workInProgress.memoizedProps;\n contextType$jscomp$0 = resolveClassComponentProps(Component, contextType);\n context.props = contextType$jscomp$0;\n getDerivedStateFromProps = workInProgress.pendingProps;\n oldState = context.context;\n oldContext = Component.contextType;\n oldProps = emptyContextObject;\n \"object\" === typeof oldContext &&\n null !== oldContext &&\n (oldProps = readContext(oldContext));\n unresolvedOldProps = Component.getDerivedStateFromProps;\n (oldContext =\n \"function\" === typeof unresolvedOldProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate) ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((contextType !== getDerivedStateFromProps || oldState !== oldProps) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n oldProps\n ));\n hasForceUpdate = !1;\n oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n var newState = workInProgress.memoizedState;\n contextType !== getDerivedStateFromProps ||\n oldState !== newState ||\n hasForceUpdate ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies))\n ? (\"function\" === typeof unresolvedOldProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n unresolvedOldProps,\n nextProps\n ),\n (newState = workInProgress.memoizedState)),\n (contextType$jscomp$0 =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n contextType$jscomp$0,\n nextProps,\n oldState,\n newState,\n oldProps\n ) ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies)))\n ? (oldContext ||\n (\"function\" !== typeof context.UNSAFE_componentWillUpdate &&\n \"function\" !== typeof context.componentWillUpdate) ||\n (\"function\" === typeof context.componentWillUpdate &&\n context.componentWillUpdate(nextProps, newState, oldProps),\n \"function\" === typeof context.UNSAFE_componentWillUpdate &&\n context.UNSAFE_componentWillUpdate(\n nextProps,\n newState,\n oldProps\n )),\n \"function\" === typeof context.componentDidUpdate &&\n (workInProgress.flags |= 4),\n \"function\" === typeof context.getSnapshotBeforeUpdate &&\n (workInProgress.flags |= 1024))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = newState)),\n (context.props = nextProps),\n (context.state = newState),\n (context.context = oldProps),\n (nextProps = contextType$jscomp$0))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (nextProps = !1));\n }\n context = nextProps;\n markRef(current, workInProgress);\n nextProps = 0 !== (workInProgress.flags & 128);\n context || nextProps\n ? ((context = workInProgress.stateNode),\n (Component =\n nextProps && \"function\" !== typeof Component.getDerivedStateFromError\n ? null\n : context.render()),\n (workInProgress.flags |= 1),\n null !== current && nextProps\n ? ((workInProgress.child = reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n )),\n (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n Component,\n renderLanes\n )))\n : reconcileChildren(current, workInProgress, Component, renderLanes),\n (workInProgress.memoizedState = context.state),\n (current = workInProgress.child))\n : (current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n ));\n return current;\n}\nfunction mountHostRootWithoutHydrating(\n current,\n workInProgress,\n nextChildren,\n renderLanes\n) {\n resetHydrationState();\n workInProgress.flags |= 256;\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nvar SUSPENDED_MARKER = {\n dehydrated: null,\n treeContext: null,\n retryLane: 0,\n hydrationErrors: null\n};\nfunction mountSuspenseOffscreenState(renderLanes) {\n return { baseLanes: renderLanes, cachePool: getSuspendedCache() };\n}\nfunction getRemainingWorkInPrimaryTree(\n current,\n primaryTreeDidDefer,\n renderLanes\n) {\n current = null !== current ? current.childLanes & ~renderLanes : 0;\n primaryTreeDidDefer && (current |= workInProgressDeferredLane);\n return current;\n}\nfunction updateSuspenseComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n showFallback = !1,\n didSuspend = 0 !== (workInProgress.flags & 128),\n JSCompiler_temp;\n (JSCompiler_temp = didSuspend) ||\n (JSCompiler_temp =\n null !== current && null === current.memoizedState\n ? !1\n : 0 !== (suspenseStackCursor.current & 2));\n JSCompiler_temp && ((showFallback = !0), (workInProgress.flags &= -129));\n JSCompiler_temp = 0 !== (workInProgress.flags & 32);\n workInProgress.flags &= -33;\n if (null === current) {\n if (isHydrating) {\n showFallback\n ? pushPrimaryTreeSuspenseHandler(workInProgress)\n : reuseSuspenseHandlerOnStack(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" !== current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n isSuspenseInstanceFallback(current)\n ? (workInProgress.lanes = 32)\n : (workInProgress.lanes = 536870912);\n return null;\n }\n var nextPrimaryChildren = nextProps.children;\n nextProps = nextProps.fallback;\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (showFallback = workInProgress.mode),\n (nextPrimaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"hidden\", children: nextPrimaryChildren },\n showFallback\n )),\n (nextProps = createFiberFromFragment(\n nextProps,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.sibling = nextProps),\n (workInProgress.child = nextPrimaryChildren),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState = mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(null, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);\n }\n var prevState = current.memoizedState;\n if (\n null !== prevState &&\n ((nextPrimaryChildren = prevState.dehydrated), null !== nextPrimaryChildren)\n ) {\n if (didSuspend)\n workInProgress.flags & 256\n ? (pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags &= -257),\n (workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n )))\n : null !== workInProgress.memoizedState\n ? (reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null))\n : (reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (nextProps = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: nextProps.children },\n showFallback\n )),\n (nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n ),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState =\n mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n (workInProgress = bailoutOffscreenComponent(null, nextProps)));\n else if (\n (pushPrimaryTreeSuspenseHandler(workInProgress),\n isSuspenseInstanceFallback(nextPrimaryChildren))\n ) {\n JSCompiler_temp =\n nextPrimaryChildren.nextSibling &&\n nextPrimaryChildren.nextSibling.dataset;\n if (JSCompiler_temp) var digest = JSCompiler_temp.dgst;\n JSCompiler_temp = digest;\n nextProps = Error(formatProdErrorMessage(419));\n nextProps.stack = \"\";\n nextProps.digest = JSCompiler_temp;\n queueHydrationError({ value: nextProps, source: null, stack: null });\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (JSCompiler_temp = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || JSCompiler_temp)\n ) {\n JSCompiler_temp = workInProgressRoot;\n if (\n null !== JSCompiler_temp &&\n ((nextProps = getBumpedLaneForHydration(JSCompiler_temp, renderLanes)),\n 0 !== nextProps && nextProps !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = nextProps),\n enqueueConcurrentRenderForLane(current, nextProps),\n scheduleUpdateOnFiber(JSCompiler_temp, current, nextProps),\n SelectiveHydrationException)\n );\n isSuspenseInstancePending(nextPrimaryChildren) ||\n renderDidSuspendDelayIfPossible();\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n isSuspenseInstancePending(nextPrimaryChildren)\n ? ((workInProgress.flags |= 192),\n (workInProgress.child = current.child),\n (workInProgress = null))\n : ((current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(\n nextPrimaryChildren.nextSibling\n )),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountSuspensePrimaryChildren(\n workInProgress,\n nextProps.children\n )),\n (workInProgress.flags |= 4096));\n return workInProgress;\n }\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (prevState = current.child),\n (digest = prevState.sibling),\n (nextProps = createWorkInProgress(prevState, {\n mode: \"hidden\",\n children: nextProps.children\n })),\n (nextProps.subtreeFlags = prevState.subtreeFlags & 65011712),\n null !== digest\n ? (nextPrimaryChildren = createWorkInProgress(\n digest,\n nextPrimaryChildren\n ))\n : ((nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2)),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n bailoutOffscreenComponent(null, nextProps),\n (nextProps = workInProgress.child),\n (nextPrimaryChildren = current.child.memoizedState),\n null === nextPrimaryChildren\n ? (nextPrimaryChildren = mountSuspenseOffscreenState(renderLanes))\n : ((showFallback = nextPrimaryChildren.cachePool),\n null !== showFallback\n ? ((prevState = CacheContext._currentValue),\n (showFallback =\n showFallback.parent !== prevState\n ? { parent: prevState, pool: prevState }\n : showFallback))\n : (showFallback = getSuspendedCache()),\n (nextPrimaryChildren = {\n baseLanes: nextPrimaryChildren.baseLanes | renderLanes,\n cachePool: showFallback\n })),\n (nextProps.memoizedState = nextPrimaryChildren),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(current.child, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n renderLanes = current.child;\n current = renderLanes.sibling;\n renderLanes = createWorkInProgress(renderLanes, {\n mode: \"visible\",\n children: nextProps.children\n });\n renderLanes.return = workInProgress;\n renderLanes.sibling = null;\n null !== current &&\n ((JSCompiler_temp = workInProgress.deletions),\n null === JSCompiler_temp\n ? ((workInProgress.deletions = [current]), (workInProgress.flags |= 16))\n : JSCompiler_temp.push(current));\n workInProgress.child = renderLanes;\n workInProgress.memoizedState = null;\n return renderLanes;\n}\nfunction mountSuspensePrimaryChildren(workInProgress, primaryChildren) {\n primaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: primaryChildren },\n workInProgress.mode\n );\n primaryChildren.return = workInProgress;\n return (workInProgress.child = primaryChildren);\n}\nfunction mountWorkInProgressOffscreenFiber(offscreenProps, mode) {\n offscreenProps = createFiberImplClass(22, offscreenProps, null, mode);\n offscreenProps.lanes = 0;\n return offscreenProps;\n}\nfunction retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountSuspensePrimaryChildren(\n workInProgress,\n workInProgress.pendingProps.children\n );\n current.flags |= 2;\n workInProgress.memoizedState = null;\n return current;\n}\nfunction scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {\n fiber.lanes |= renderLanes;\n var alternate = fiber.alternate;\n null !== alternate && (alternate.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);\n}\nfunction initSuspenseListRenderState(\n workInProgress,\n isBackwards,\n tail,\n lastContentRow,\n tailMode,\n treeForkCount\n) {\n var renderState = workInProgress.memoizedState;\n null === renderState\n ? (workInProgress.memoizedState = {\n isBackwards: isBackwards,\n rendering: null,\n renderingStartTime: 0,\n last: lastContentRow,\n tail: tail,\n tailMode: tailMode,\n treeForkCount: treeForkCount\n })\n : ((renderState.isBackwards = isBackwards),\n (renderState.rendering = null),\n (renderState.renderingStartTime = 0),\n (renderState.last = lastContentRow),\n (renderState.tail = tail),\n (renderState.tailMode = tailMode),\n (renderState.treeForkCount = treeForkCount));\n}\nfunction updateSuspenseListComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n revealOrder = nextProps.revealOrder,\n tailMode = nextProps.tail;\n nextProps = nextProps.children;\n var suspenseContext = suspenseStackCursor.current,\n shouldForceFallback = 0 !== (suspenseContext & 2);\n shouldForceFallback\n ? ((suspenseContext = (suspenseContext & 1) | 2),\n (workInProgress.flags |= 128))\n : (suspenseContext &= 1);\n push(suspenseStackCursor, suspenseContext);\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n nextProps = isHydrating ? treeForkCount : 0;\n if (!shouldForceFallback && null !== current && 0 !== (current.flags & 128))\n a: for (current = workInProgress.child; null !== current; ) {\n if (13 === current.tag)\n null !== current.memoizedState &&\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (19 === current.tag)\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (null !== current.child) {\n current.child.return = current;\n current = current.child;\n continue;\n }\n if (current === workInProgress) break a;\n for (; null === current.sibling; ) {\n if (null === current.return || current.return === workInProgress)\n break a;\n current = current.return;\n }\n current.sibling.return = current.return;\n current = current.sibling;\n }\n switch (revealOrder) {\n case \"forwards\":\n renderLanes = workInProgress.child;\n for (revealOrder = null; null !== renderLanes; )\n (current = renderLanes.alternate),\n null !== current &&\n null === findFirstSuspended(current) &&\n (revealOrder = renderLanes),\n (renderLanes = renderLanes.sibling);\n renderLanes = revealOrder;\n null === renderLanes\n ? ((revealOrder = workInProgress.child), (workInProgress.child = null))\n : ((revealOrder = renderLanes.sibling), (renderLanes.sibling = null));\n initSuspenseListRenderState(\n workInProgress,\n !1,\n revealOrder,\n renderLanes,\n tailMode,\n nextProps\n );\n break;\n case \"backwards\":\n case \"unstable_legacy-backwards\":\n renderLanes = null;\n revealOrder = workInProgress.child;\n for (workInProgress.child = null; null !== revealOrder; ) {\n current = revealOrder.alternate;\n if (null !== current && null === findFirstSuspended(current)) {\n workInProgress.child = revealOrder;\n break;\n }\n current = revealOrder.sibling;\n revealOrder.sibling = renderLanes;\n renderLanes = revealOrder;\n revealOrder = current;\n }\n initSuspenseListRenderState(\n workInProgress,\n !0,\n renderLanes,\n null,\n tailMode,\n nextProps\n );\n break;\n case \"together\":\n initSuspenseListRenderState(\n workInProgress,\n !1,\n null,\n null,\n void 0,\n nextProps\n );\n break;\n default:\n workInProgress.memoizedState = null;\n }\n return workInProgress.child;\n}\nfunction bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {\n null !== current && (workInProgress.dependencies = current.dependencies);\n workInProgressRootSkippedLanes |= workInProgress.lanes;\n if (0 === (renderLanes & workInProgress.childLanes))\n if (null !== current) {\n if (\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n 0 === (renderLanes & workInProgress.childLanes))\n )\n return null;\n } else return null;\n if (null !== current && workInProgress.child !== current.child)\n throw Error(formatProdErrorMessage(153));\n if (null !== workInProgress.child) {\n current = workInProgress.child;\n renderLanes = createWorkInProgress(current, current.pendingProps);\n workInProgress.child = renderLanes;\n for (renderLanes.return = workInProgress; null !== current.sibling; )\n (current = current.sibling),\n (renderLanes = renderLanes.sibling =\n createWorkInProgress(current, current.pendingProps)),\n (renderLanes.return = workInProgress);\n renderLanes.sibling = null;\n }\n return workInProgress.child;\n}\nfunction checkScheduledUpdateOrContext(current, renderLanes) {\n if (0 !== (current.lanes & renderLanes)) return !0;\n current = current.dependencies;\n return null !== current && checkIfContextChanged(current) ? !0 : !1;\n}\nfunction attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n) {\n switch (workInProgress.tag) {\n case 3:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n resetHydrationState();\n break;\n case 27:\n case 5:\n pushHostContext(workInProgress);\n break;\n case 4:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n break;\n case 10:\n pushProvider(\n workInProgress,\n workInProgress.type,\n workInProgress.memoizedProps.value\n );\n break;\n case 31:\n if (null !== workInProgress.memoizedState)\n return (\n (workInProgress.flags |= 128),\n pushDehydratedActivitySuspenseHandler(workInProgress),\n null\n );\n break;\n case 13:\n var state$102 = workInProgress.memoizedState;\n if (null !== state$102) {\n if (null !== state$102.dehydrated)\n return (\n pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags |= 128),\n null\n );\n if (0 !== (renderLanes & workInProgress.child.childLanes))\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n pushPrimaryTreeSuspenseHandler(workInProgress);\n current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n return null !== current ? current.sibling : null;\n }\n pushPrimaryTreeSuspenseHandler(workInProgress);\n break;\n case 19:\n var didSuspendBefore = 0 !== (current.flags & 128);\n state$102 = 0 !== (renderLanes & workInProgress.childLanes);\n state$102 ||\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n (state$102 = 0 !== (renderLanes & workInProgress.childLanes)));\n if (didSuspendBefore) {\n if (state$102)\n return updateSuspenseListComponent(\n current,\n workInProgress,\n renderLanes\n );\n workInProgress.flags |= 128;\n }\n didSuspendBefore = workInProgress.memoizedState;\n null !== didSuspendBefore &&\n ((didSuspendBefore.rendering = null),\n (didSuspendBefore.tail = null),\n (didSuspendBefore.lastEffect = null));\n push(suspenseStackCursor, suspenseStackCursor.current);\n if (state$102) break;\n else return null;\n case 22:\n return (\n (workInProgress.lanes = 0),\n updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n )\n );\n case 24:\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n }\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n}\nfunction beginWork(current, workInProgress, renderLanes) {\n if (null !== current)\n if (current.memoizedProps !== workInProgress.pendingProps)\n didReceiveUpdate = !0;\n else {\n if (\n !checkScheduledUpdateOrContext(current, renderLanes) &&\n 0 === (workInProgress.flags & 128)\n )\n return (\n (didReceiveUpdate = !1),\n attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n )\n );\n didReceiveUpdate = 0 !== (current.flags & 131072) ? !0 : !1;\n }\n else\n (didReceiveUpdate = !1),\n isHydrating &&\n 0 !== (workInProgress.flags & 1048576) &&\n pushTreeId(workInProgress, treeForkCount, workInProgress.index);\n workInProgress.lanes = 0;\n switch (workInProgress.tag) {\n case 16:\n a: {\n var props = workInProgress.pendingProps;\n current = resolveLazy(workInProgress.elementType);\n workInProgress.type = current;\n if (\"function\" === typeof current)\n shouldConstruct(current)\n ? ((props = resolveClassComponentProps(current, props)),\n (workInProgress.tag = 1),\n (workInProgress = updateClassComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )))\n : ((workInProgress.tag = 0),\n (workInProgress = updateFunctionComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )));\n else {\n if (void 0 !== current && null !== current) {\n var $$typeof = current.$$typeof;\n if ($$typeof === REACT_FORWARD_REF_TYPE) {\n workInProgress.tag = 11;\n workInProgress = updateForwardRef(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n } else if ($$typeof === REACT_MEMO_TYPE) {\n workInProgress.tag = 14;\n workInProgress = updateMemoComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n }\n }\n workInProgress = getComponentNameFromType(current) || current;\n throw Error(formatProdErrorMessage(306, workInProgress, \"\"));\n }\n }\n return workInProgress;\n case 0:\n return updateFunctionComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 1:\n return (\n (props = workInProgress.type),\n ($$typeof = resolveClassComponentProps(\n props,\n workInProgress.pendingProps\n )),\n updateClassComponent(\n current,\n workInProgress,\n props,\n $$typeof,\n renderLanes\n )\n );\n case 3:\n a: {\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n );\n if (null === current) throw Error(formatProdErrorMessage(387));\n props = workInProgress.pendingProps;\n var prevState = workInProgress.memoizedState;\n $$typeof = prevState.element;\n cloneUpdateQueue(current, workInProgress);\n processUpdateQueue(workInProgress, props, null, renderLanes);\n var nextState = workInProgress.memoizedState;\n props = nextState.cache;\n pushProvider(workInProgress, CacheContext, props);\n props !== prevState.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n );\n suspendIfUpdateReadFromEntangledAsyncAction();\n props = nextState.element;\n if (prevState.isDehydrated)\n if (\n ((prevState = {\n element: props,\n isDehydrated: !1,\n cache: nextState.cache\n }),\n (workInProgress.updateQueue.baseState = prevState),\n (workInProgress.memoizedState = prevState),\n workInProgress.flags & 256)\n ) {\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else if (props !== $$typeof) {\n $$typeof = createCapturedValueAtFiber(\n Error(formatProdErrorMessage(424)),\n workInProgress\n );\n queueHydrationError($$typeof);\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else {\n current = workInProgress.stateNode.containerInfo;\n switch (current.nodeType) {\n case 9:\n current = current.body;\n break;\n default:\n current =\n \"HTML\" === current.nodeName\n ? current.ownerDocument.body\n : current;\n }\n nextHydratableInstance = getNextHydratable(current.firstChild);\n hydrationParentFiber = workInProgress;\n isHydrating = !0;\n hydrationErrors = null;\n rootOrSingletonContext = !0;\n renderLanes = mountChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n );\n for (workInProgress.child = renderLanes; renderLanes; )\n (renderLanes.flags = (renderLanes.flags & -3) | 4096),\n (renderLanes = renderLanes.sibling);\n }\n else {\n resetHydrationState();\n if (props === $$typeof) {\n workInProgress = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n break a;\n }\n reconcileChildren(current, workInProgress, props, renderLanes);\n }\n workInProgress = workInProgress.child;\n }\n return workInProgress;\n case 26:\n return (\n markRef(current, workInProgress),\n null === current\n ? (renderLanes = getResource(\n workInProgress.type,\n null,\n workInProgress.pendingProps,\n null\n ))\n ? (workInProgress.memoizedState = renderLanes)\n : isHydrating ||\n ((renderLanes = workInProgress.type),\n (current = workInProgress.pendingProps),\n (props = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n ).createElement(renderLanes)),\n (props[internalInstanceKey] = workInProgress),\n (props[internalPropsKey] = current),\n setInitialProperties(props, renderLanes, current),\n markNodeAsHoistable(props),\n (workInProgress.stateNode = props))\n : (workInProgress.memoizedState = getResource(\n workInProgress.type,\n current.memoizedProps,\n workInProgress.pendingProps,\n current.memoizedState\n )),\n null\n );\n case 27:\n return (\n pushHostContext(workInProgress),\n null === current &&\n isHydrating &&\n ((props = workInProgress.stateNode =\n resolveSingletonInstance(\n workInProgress.type,\n workInProgress.pendingProps,\n rootInstanceStackCursor.current\n )),\n (hydrationParentFiber = workInProgress),\n (rootOrSingletonContext = !0),\n ($$typeof = nextHydratableInstance),\n isSingletonScope(workInProgress.type)\n ? ((previousHydratableOnEnteringScopedSingleton = $$typeof),\n (nextHydratableInstance = getNextHydratable(props.firstChild)))\n : (nextHydratableInstance = $$typeof)),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n markRef(current, workInProgress),\n null === current && (workInProgress.flags |= 4194304),\n workInProgress.child\n );\n case 5:\n if (null === current && isHydrating) {\n if (($$typeof = props = nextHydratableInstance))\n (props = canHydrateInstance(\n props,\n workInProgress.type,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== props\n ? ((workInProgress.stateNode = props),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = getNextHydratable(props.firstChild)),\n (rootOrSingletonContext = !1),\n ($$typeof = !0))\n : ($$typeof = !1);\n $$typeof || throwOnHydrationMismatch(workInProgress);\n }\n pushHostContext(workInProgress);\n $$typeof = workInProgress.type;\n prevState = workInProgress.pendingProps;\n nextState = null !== current ? current.memoizedProps : null;\n props = prevState.children;\n shouldSetTextContent($$typeof, prevState)\n ? (props = null)\n : null !== nextState &&\n shouldSetTextContent($$typeof, nextState) &&\n (workInProgress.flags |= 32);\n null !== workInProgress.memoizedState &&\n (($$typeof = renderWithHooks(\n current,\n workInProgress,\n TransitionAwareHostComponent,\n null,\n null,\n renderLanes\n )),\n (HostTransitionContext._currentValue = $$typeof));\n markRef(current, workInProgress);\n reconcileChildren(current, workInProgress, props, renderLanes);\n return workInProgress.child;\n case 6:\n if (null === current && isHydrating) {\n if ((current = renderLanes = nextHydratableInstance))\n (renderLanes = canHydrateTextInstance(\n renderLanes,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== renderLanes\n ? ((workInProgress.stateNode = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null),\n (current = !0))\n : (current = !1);\n current || throwOnHydrationMismatch(workInProgress);\n }\n return null;\n case 13:\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n case 4:\n return (\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n ),\n (props = workInProgress.pendingProps),\n null === current\n ? (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n ))\n : reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 11:\n return updateForwardRef(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 7:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps,\n renderLanes\n ),\n workInProgress.child\n );\n case 8:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 12:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 10:\n return (\n (props = workInProgress.pendingProps),\n pushProvider(workInProgress, workInProgress.type, props.value),\n reconcileChildren(current, workInProgress, props.children, renderLanes),\n workInProgress.child\n );\n case 9:\n return (\n ($$typeof = workInProgress.type._context),\n (props = workInProgress.pendingProps.children),\n prepareToReadContext(workInProgress),\n ($$typeof = readContext($$typeof)),\n (props = props($$typeof)),\n (workInProgress.flags |= 1),\n reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 14:\n return updateMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 15:\n return updateSimpleMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 19:\n return updateSuspenseListComponent(current, workInProgress, renderLanes);\n case 31:\n return updateActivityComponent(current, workInProgress, renderLanes);\n case 22:\n return updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n );\n case 24:\n return (\n prepareToReadContext(workInProgress),\n (props = readContext(CacheContext)),\n null === current\n ? (($$typeof = peekCacheFromPool()),\n null === $$typeof &&\n (($$typeof = workInProgressRoot),\n (prevState = createCache()),\n ($$typeof.pooledCache = prevState),\n prevState.refCount++,\n null !== prevState && ($$typeof.pooledCacheLanes |= renderLanes),\n ($$typeof = prevState)),\n (workInProgress.memoizedState = { parent: props, cache: $$typeof }),\n initializeUpdateQueue(workInProgress),\n pushProvider(workInProgress, CacheContext, $$typeof))\n : (0 !== (current.lanes & renderLanes) &&\n (cloneUpdateQueue(current, workInProgress),\n processUpdateQueue(workInProgress, null, null, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction()),\n ($$typeof = current.memoizedState),\n (prevState = workInProgress.memoizedState),\n $$typeof.parent !== props\n ? (($$typeof = { parent: props, cache: props }),\n (workInProgress.memoizedState = $$typeof),\n 0 === workInProgress.lanes &&\n (workInProgress.memoizedState =\n workInProgress.updateQueue.baseState =\n $$typeof),\n pushProvider(workInProgress, CacheContext, props))\n : ((props = prevState.cache),\n pushProvider(workInProgress, CacheContext, props),\n props !== $$typeof.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n ))),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 29:\n throw workInProgress.pendingProps;\n }\n throw Error(formatProdErrorMessage(156, workInProgress.tag));\n}\nfunction markUpdate(workInProgress) {\n workInProgress.flags |= 4;\n}\nfunction preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n oldProps,\n newProps,\n renderLanes\n) {\n if ((type = 0 !== (workInProgress.mode & 32))) type = !1;\n if (type) {\n if (\n ((workInProgress.flags |= 16777216),\n (renderLanes & 335544128) === renderLanes)\n )\n if (workInProgress.stateNode.complete) workInProgress.flags |= 8192;\n else if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n } else workInProgress.flags &= -16777217;\n}\nfunction preloadResourceAndSuspendIfNeeded(workInProgress, resource) {\n if (\"stylesheet\" !== resource.type || 0 !== (resource.state.loading & 4))\n workInProgress.flags &= -16777217;\n else if (((workInProgress.flags |= 16777216), !preloadResource(resource)))\n if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n}\nfunction scheduleRetryEffect(workInProgress, retryQueue) {\n null !== retryQueue && (workInProgress.flags |= 4);\n workInProgress.flags & 16384 &&\n ((retryQueue =\n 22 !== workInProgress.tag ? claimNextRetryLane() : 536870912),\n (workInProgress.lanes |= retryQueue),\n (workInProgressSuspendedRetryLanes |= retryQueue));\n}\nfunction cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {\n if (!isHydrating)\n switch (renderState.tailMode) {\n case \"hidden\":\n hasRenderedATailFallback = renderState.tail;\n for (var lastTailNode = null; null !== hasRenderedATailFallback; )\n null !== hasRenderedATailFallback.alternate &&\n (lastTailNode = hasRenderedATailFallback),\n (hasRenderedATailFallback = hasRenderedATailFallback.sibling);\n null === lastTailNode\n ? (renderState.tail = null)\n : (lastTailNode.sibling = null);\n break;\n case \"collapsed\":\n lastTailNode = renderState.tail;\n for (var lastTailNode$106 = null; null !== lastTailNode; )\n null !== lastTailNode.alternate && (lastTailNode$106 = lastTailNode),\n (lastTailNode = lastTailNode.sibling);\n null === lastTailNode$106\n ? hasRenderedATailFallback || null === renderState.tail\n ? (renderState.tail = null)\n : (renderState.tail.sibling = null)\n : (lastTailNode$106.sibling = null);\n }\n}\nfunction bubbleProperties(completedWork) {\n var didBailout =\n null !== completedWork.alternate &&\n completedWork.alternate.child === completedWork.child,\n newChildLanes = 0,\n subtreeFlags = 0;\n if (didBailout)\n for (var child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags & 65011712),\n (subtreeFlags |= child$107.flags & 65011712),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n else\n for (child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags),\n (subtreeFlags |= child$107.flags),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n completedWork.subtreeFlags |= subtreeFlags;\n completedWork.childLanes = newChildLanes;\n return didBailout;\n}\nfunction completeWork(current, workInProgress, renderLanes) {\n var newProps = workInProgress.pendingProps;\n popTreeContext(workInProgress);\n switch (workInProgress.tag) {\n case 16:\n case 15:\n case 0:\n case 11:\n case 7:\n case 8:\n case 12:\n case 9:\n case 14:\n return bubbleProperties(workInProgress), null;\n case 1:\n return bubbleProperties(workInProgress), null;\n case 3:\n renderLanes = workInProgress.stateNode;\n newProps = null;\n null !== current && (newProps = current.memoizedState.cache);\n workInProgress.memoizedState.cache !== newProps &&\n (workInProgress.flags |= 2048);\n popProvider(CacheContext);\n popHostContainer();\n renderLanes.pendingContext &&\n ((renderLanes.context = renderLanes.pendingContext),\n (renderLanes.pendingContext = null));\n if (null === current || null === current.child)\n popHydrationState(workInProgress)\n ? markUpdate(workInProgress)\n : null === current ||\n (current.memoizedState.isDehydrated &&\n 0 === (workInProgress.flags & 256)) ||\n ((workInProgress.flags |= 1024),\n upgradeHydrationErrorsToRecoverable());\n bubbleProperties(workInProgress);\n return null;\n case 26:\n var type = workInProgress.type,\n nextResource = workInProgress.memoizedState;\n null === current\n ? (markUpdate(workInProgress),\n null !== nextResource\n ? (bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n null,\n newProps,\n renderLanes\n )))\n : nextResource\n ? nextResource !== current.memoizedState\n ? (markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n (workInProgress.flags &= -16777217))\n : ((current = current.memoizedProps),\n current !== newProps && markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n current,\n newProps,\n renderLanes\n ));\n return null;\n case 27:\n popHostContext(workInProgress);\n renderLanes = rootInstanceStackCursor.current;\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n current = contextStackCursor.current;\n popHydrationState(workInProgress)\n ? prepareToHydrateHostInstance(workInProgress, current)\n : ((current = resolveSingletonInstance(type, newProps, renderLanes)),\n (workInProgress.stateNode = current),\n markUpdate(workInProgress));\n }\n bubbleProperties(workInProgress);\n return null;\n case 5:\n popHostContext(workInProgress);\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n nextResource = contextStackCursor.current;\n if (popHydrationState(workInProgress))\n prepareToHydrateHostInstance(workInProgress, nextResource);\n else {\n var ownerDocument = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n );\n switch (nextResource) {\n case 1:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case 2:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n default:\n switch (type) {\n case \"svg\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case \"math\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n case \"script\":\n nextResource = ownerDocument.createElement(\"div\");\n nextResource.innerHTML = \" + diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 1415969c..60b007a9 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.907", + "version": "1.0.910", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index c36bb720..cbe9603d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1432", + "version": "1.0.1435", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 3dfa61f0..b3ebbbe7 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index b1abb73e..b04a325c 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1437", + "version": "0.5.1440", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 05d6bb67..83b74e34 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 39340b1e..7c566288 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index f25961ec..1cfa0880 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 983f993c..2d6c7de3 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 66287cd6..4bd6f7f8 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 8101447d..57464b45 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 42711abd..9c52ac92 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 3c4b60b0..70c6beae 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index bf41af8d..04b40ba1 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1458", + "version": "0.5.1461", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index cf5ade72..f4c8ea72 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 132ac677..1e5ef84f 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index ff266d4a..5ee80022 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 8406147f..89d7fd4d 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 026dfde0..89ee7ccd 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index ddec6514..8c7c3e5f 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 4d5607c8..826ecd81 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1450", + "version": "0.5.1453", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx index 2cc3459d..3d0d1c57 100755 --- a/pages/options/src/components/LLMSelector.tsx +++ b/pages/options/src/components/LLMSelector.tsx @@ -65,9 +65,8 @@ const LLMSelector: React.FC = ({ : settings.deep_analysis[language].custom_prompt, }); - - // API-ключ передаем только для Default LLM - onLLMChange(newLLM, newLLM === 'default' ? apiKey : undefined); + // API-ключ передаем для всех LLM, включая кастомные + onLLMChange(newLLM, apiKey || undefined); }; // Сохраняем API ключ @@ -113,7 +112,6 @@ const LLMSelector: React.FC = ({ padding: '8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '14px', marginBottom: '8px' }} @@ -140,7 +138,6 @@ const LLMSelector: React.FC = ({ padding: '8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '14px' }} /> diff --git a/pages/options/src/components/PluginDetails.tsx b/pages/options/src/components/PluginDetails.tsx index c15c1cff..480011d2 100755 --- a/pages/options/src/components/PluginDetails.tsx +++ b/pages/options/src/components/PluginDetails.tsx @@ -182,7 +182,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK padding: '4px 8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '14px' }} > @@ -201,7 +200,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK padding: '4px 8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '14px' }} > @@ -211,32 +209,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK
- {/* LLM Selector для текущего промпта и языка */} - { - console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey); - // Сохраняем выбранную LLM в pluginSettings для передачи в mcp_server.py - const updatedPluginSettings = { ...pluginSettings } as any; - if (!updatedPluginSettings.selected_llms) { - updatedPluginSettings.selected_llms = {}; - } - if (!updatedPluginSettings.selected_llms[promptType]) { - updatedPluginSettings.selected_llms[promptType] = {}; - } - // Сохраняем только выбранную LLM (без api_key, так как он уже сохранен через APIKeyManager) - updatedPluginSettings.selected_llms[promptType][language] = llm; - // Обновляем pluginSettings через глобальный объект - if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) { - (window as any).pyodide.globals.pluginSettings = updatedPluginSettings; - } - }} - /> - {/* Textarea */}
@@ -293,7 +265,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK padding: '8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '12px', fontFamily: 'monospace', resize: 'vertical' @@ -302,6 +273,42 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK
+ {/* LLM Selector для текущего промпта и языка */} + { + console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey); + // Сохраняем выбранную LLM и API ключ в pluginSettings для передачи в mcp_server.py + const updatedPluginSettings = { ...pluginSettings } as any; + if (!updatedPluginSettings.selected_llms) { + updatedPluginSettings.selected_llms = {}; + } + if (!updatedPluginSettings.selected_llms[promptType]) { + updatedPluginSettings.selected_llms[promptType] = {}; + } + // Сохраняем выбранную LLM + updatedPluginSettings.selected_llms[promptType][language] = llm; + + // Сохраняем API ключ если он предоставлен + if (!updatedPluginSettings.api_keys) { + updatedPluginSettings.api_keys = {}; + } + if (apiKey) { + const keyId = `ozon-analyzer-${promptType}-${language}`; + updatedPluginSettings.api_keys[keyId] = apiKey; + } + + // Обновляем pluginSettings через глобальный объект + if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) { + (window as any).pyodide.globals.pluginSettings = updatedPluginSettings; + } + }} + /> + {/* Кнопка сохранения */}
}\n
\n);\n\nexport default ErrorDisplay;\n","import React from 'react';\nimport ErrorDisplay from './ErrorDisplay';\n\nconst LocalErrorBoundary: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [error, setError] = React.useState(null);\n const resetError = React.useCallback(() => setError(null), []);\n\n if (error) {\n return ;\n }\n\n try {\n return <>{children};\n } catch (err) {\n setError(err as Error);\n return null;\n }\n};\n\nexport default LocalErrorBoundary;\n","/**\n * Утилиты для шифрования API-ключей\n * Использует Web Crypto API для безопасного хранения в chrome.storage.local\n */\n\nexport class APIKeyEncryption {\n private static readonly ALGORITHM = 'AES-GCM';\n private static readonly KEY_LENGTH = 256;\n private static readonly IV_LENGTH = 12;\n\n /**\n * Получает или создает ключ шифрования из chrome.storage.local\n */\n private static async getEncryptionKey(): Promise {\n try {\n // Проверяем, есть ли уже ключ в хранилище\n const result = await chrome.storage.local.get(['encryptionKey']);\n if (result.encryptionKey) {\n // Восстанавливаем ключ из массива байтов\n const keyData = new Uint8Array(result.encryptionKey);\n return await crypto.subtle.importKey(\n 'raw',\n keyData,\n this.ALGORITHM,\n false,\n ['encrypt', 'decrypt']\n );\n }\n\n // Создаем новый ключ\n const key = await crypto.subtle.generateKey(\n {\n name: this.ALGORITHM,\n length: this.KEY_LENGTH,\n },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Сохраняем ключ в хранилище\n const exportedKey = await crypto.subtle.exportKey('raw', key);\n const keyArray = new Uint8Array(exportedKey);\n await chrome.storage.local.set({\n encryptionKey: Array.from(keyArray)\n });\n\n return key;\n } catch (error) {\n console.error('Failed to get/create encryption key:', error);\n throw new Error('Не удалось инициализировать шифрование');\n }\n }\n\n /**\n * Шифрует текст\n */\n static async encrypt(text: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n const encodedText = new TextEncoder().encode(text);\n\n const encrypted = await crypto.subtle.encrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n encodedText\n );\n\n // Объединяем IV и зашифрованные данные\n const encryptedArray = new Uint8Array(encrypted);\n const resultArray = new Uint8Array(iv.length + encryptedArray.length);\n resultArray.set(iv);\n resultArray.set(encryptedArray, iv.length);\n\n // Кодируем в base64 для хранения в storage\n return btoa(String.fromCharCode(...resultArray));\n } catch (error) {\n console.error('Encryption failed:', error);\n throw new Error('Ошибка шифрования');\n }\n }\n\n /**\n * Расшифровывает текст\n */\n static async decrypt(encryptedText: string): Promise {\n try {\n const key = await this.getEncryptionKey();\n\n // Декодируем из base64\n const encryptedArray = new Uint8Array(\n atob(encryptedText).split('').map(char => char.charCodeAt(0))\n );\n\n // Извлекаем IV и зашифрованные данные\n const iv = encryptedArray.slice(0, this.IV_LENGTH);\n const data = encryptedArray.slice(this.IV_LENGTH);\n\n const decrypted = await crypto.subtle.decrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n },\n key,\n data\n );\n\n return new TextDecoder().decode(decrypted);\n } catch (error) {\n console.error('Decryption failed:', error);\n throw new Error('Ошибка расшифрования');\n }\n }\n\n /**\n * Валидация API ключа\n */\n static validateAPIKey(key: string): { isValid: boolean; error?: string } {\n if (!key || typeof key !== 'string') {\n return { isValid: false, error: 'Ключ не может быть пустым' };\n }\n\n if (key.length < 10) {\n return { isValid: false, error: 'Ключ слишком короткий' };\n }\n\n if (key.length > 200) {\n return { isValid: false, error: 'Ключ слишком длинный' };\n }\n\n // Проверяем на наличие потенциально опасных символов\n if (/[<>\\\"'&]/.test(key)) {\n return { isValid: false, error: 'Ключ содержит недопустимые символы' };\n }\n\n return { isValid: true };\n }\n}\n\n/**\n * Утилиты для безопасной работы с API ключами\n */\nexport class APIKeyManager {\n /**\n * Сохраняет API ключ с шифрованием\n */\n static async saveEncryptedKey(keyId: string, apiKey: string): Promise {\n try {\n const validation = APIKeyEncryption.validateAPIKey(apiKey);\n if (!validation.isValid) {\n throw new Error(validation.error);\n }\n\n const encryptedKey = await APIKeyEncryption.encrypt(apiKey);\n const keys = await this.getAllEncryptedKeys();\n\n keys[keyId] = encryptedKey;\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to save encrypted API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает расшифрованный API ключ\n */\n static async getDecryptedKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n const encryptedKey = keys[keyId];\n\n if (!encryptedKey) {\n return null;\n }\n\n return await APIKeyEncryption.decrypt(encryptedKey);\n } catch (error) {\n console.error('Failed to get decrypted API key:', error);\n return null;\n }\n }\n\n /**\n * Удаляет API ключ\n */\n static async removeKey(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n delete keys[keyId];\n await chrome.storage.local.set({ encryptedApiKeys: keys });\n } catch (error) {\n console.error('Failed to remove API key:', error);\n throw error;\n }\n }\n\n /**\n * Получает все зашифрованные ключи\n */\n private static async getAllEncryptedKeys(): Promise> {\n try {\n const result = await chrome.storage.local.get(['encryptedApiKeys']);\n return result.encryptedApiKeys || {};\n } catch (error) {\n console.error('Failed to get encrypted keys:', error);\n return {};\n }\n }\n\n /**\n * Получает все ID ключей (без расшифровки)\n */\n static async getAllKeyIds(): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return Object.keys(keys);\n } catch (error) {\n console.error('Failed to get key IDs:', error);\n return [];\n }\n }\n\n /**\n * Проверяет, существует ли ключ\n */\n static async keyExists(keyId: string): Promise {\n try {\n const keys = await this.getAllEncryptedKeys();\n return keyId in keys;\n } catch (error) {\n console.error('Failed to check key existence:', error);\n return false;\n }\n }\n}","import * as React from 'react';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface PluginSettings {\n basic_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n deep_analysis: {\n ru: {\n llm: string;\n custom_prompt: string;\n };\n en: {\n llm: string;\n custom_prompt: string;\n };\n };\n api_keys: {\n default: string; // encrypted\n };\n}\n\nexport interface PluginPromptSettings {\n llm: string;\n custom_prompt: string;\n}\n\nconst STORAGE_KEY = 'plugin-ozon-analyzer-settings';\n\n// Function to load default prompts from manifest.json\nconst loadDefaultPromptsFromManifest = async (): Promise<{ basic_analysis: { ru: string; en: string }; deep_analysis: { ru: string; en: string } }> => {\n try {\n // Load manifest.json from the plugin directory\n const manifestResponse = await fetch(chrome.runtime.getURL('plugins/ozon-analyzer/manifest.json'));\n const manifest = await manifestResponse.json();\n\n const prompts = manifest.options?.prompts;\n if (!prompts) {\n throw new Error('Prompts not found in manifest');\n }\n\n return {\n basic_analysis: {\n ru: prompts.basic_analysis?.ru?.default || '',\n en: prompts.basic_analysis?.en?.default || '',\n },\n deep_analysis: {\n ru: prompts.deep_analysis?.ru?.default || '',\n en: prompts.deep_analysis?.en?.default || '',\n },\n };\n } catch (error) {\n console.error('Failed to load prompts from manifest:', error);\n // Fallback to hardcoded defaults if manifest loading fails\n return {\n basic_analysis: {\n ru: 'верни слово basic_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n en: 'верни слово basic_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n },\n deep_analysis: {\n ru: 'верни слово deep_analysis_ru_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n en: 'верни слово deep_analysis_en_default и полное название и версию твоей LLM (например, Gemini flash lite или Gemini 2.5 Pro)',\n },\n };\n }\n};\n\nconst DEFAULT_SETTINGS: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: '',\n },\n en: {\n llm: '',\n custom_prompt: '',\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: '',\n },\n en: {\n llm: '',\n custom_prompt: '',\n },\n },\n api_keys: {\n default: '',\n },\n};\n\nexport const usePluginSettings = () => {\n const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);\n const [isLoading, setIsLoading] = React.useState(true);\n\n React.useEffect(() => {\n initializeSettings();\n }, []);\n\n const initializeSettings = async () => {\n try {\n // Load default prompts from manifest first\n const defaultPrompts = await loadDefaultPromptsFromManifest();\n\n // Create initial settings with manifest defaults\n const initialSettings: PluginSettings = {\n basic_analysis: {\n ru: {\n llm: '',\n custom_prompt: defaultPrompts.basic_analysis.ru,\n },\n en: {\n llm: '',\n custom_prompt: defaultPrompts.basic_analysis.en,\n },\n },\n deep_analysis: {\n ru: {\n llm: '',\n custom_prompt: defaultPrompts.deep_analysis.ru,\n },\n en: {\n llm: '',\n custom_prompt: defaultPrompts.deep_analysis.en,\n },\n },\n api_keys: {\n default: '',\n },\n };\n\n // Then load user settings and merge with defaults\n await loadSettings(initialSettings);\n } catch (error) {\n console.error('Failed to initialize settings:', error);\n // Fallback to loading with empty defaults\n await loadSettings(DEFAULT_SETTINGS);\n }\n };\n\n const loadSettings = async (defaultSettings: PluginSettings = DEFAULT_SETTINGS) => {\n try {\n setIsLoading(true);\n const result = await chrome.storage.local.get([STORAGE_KEY]);\n const storedSettings = result[STORAGE_KEY];\n\n if (storedSettings) {\n // Расшифровываем API ключи\n const decryptedApiKeys = { ...storedSettings.api_keys };\n if (storedSettings.api_keys?.default) {\n decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || '';\n }\n\n setSettings({\n ...defaultSettings,\n ...storedSettings,\n basic_analysis: {\n ru: {\n ...defaultSettings.basic_analysis.ru,\n ...storedSettings.basic_analysis?.ru,\n },\n en: {\n ...defaultSettings.basic_analysis.en,\n ...storedSettings.basic_analysis?.en,\n },\n },\n deep_analysis: {\n ru: {\n ...defaultSettings.deep_analysis.ru,\n ...storedSettings.deep_analysis?.ru,\n },\n en: {\n ...defaultSettings.deep_analysis.en,\n ...storedSettings.deep_analysis?.en,\n },\n },\n api_keys: decryptedApiKeys,\n });\n } else {\n setSettings(defaultSettings);\n }\n } catch (error) {\n console.error('Failed to load plugin settings:', error);\n setSettings(defaultSettings);\n } finally {\n setIsLoading(false);\n }\n };\n\n const saveSettings = async (newSettings: PluginSettings) => {\n try {\n // Шифруем API ключи перед сохранением\n const settingsToSave = { ...newSettings };\n if (newSettings.api_keys?.default) {\n await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default);\n } else {\n await APIKeyManager.removeKey('ozon-analyzer-default');\n }\n\n // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON\n settingsToSave.api_keys = { default: '' };\n\n await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave });\n setSettings(newSettings);\n } catch (error) {\n console.error('Failed to save plugin settings:', error);\n throw error;\n }\n };\n\n const updateBasicAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n basic_analysis: {\n ...settings.basic_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateDeepAnalysisSettings = async (\n language: 'ru' | 'en',\n newPromptSettings: PluginPromptSettings\n ) => {\n const newSettings = {\n ...settings,\n deep_analysis: {\n ...settings.deep_analysis,\n [language]: newPromptSettings,\n },\n };\n await saveSettings(newSettings);\n };\n\n const updateAPIKey = async (key: string) => {\n const newSettings = {\n ...settings,\n api_keys: {\n default: key,\n },\n };\n await saveSettings(newSettings);\n };\n\n const getBasicAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.basic_analysis[language];\n };\n\n const getDeepAnalysisSettings = (language: 'ru' | 'en'): PluginPromptSettings => {\n return settings.deep_analysis[language];\n };\n\n const getAPIKey = (): string => {\n return settings.api_keys?.default || '';\n };\n\n const resetToDefaults = async () => {\n await saveSettings(DEFAULT_SETTINGS);\n };\n\n return {\n settings,\n isLoading,\n updateBasicAnalysisSettings,\n updateDeepAnalysisSettings,\n updateAPIKey,\n getBasicAnalysisSettings,\n getDeepAnalysisSettings,\n getAPIKey,\n resetToDefaults,\n saveSettings,\n loadSettings,\n };\n};","import React, { useState, useEffect, useRef } from 'react';\nimport { usePluginSettings } from '../hooks/usePluginSettings';\nimport { AIKey } from '../hooks/useAIKeys';\nimport { APIKeyManager } from '../utils/encryption';\n\ninterface LLMSelectorProps {\n promptType: 'basic_analysis' | 'deep_analysis';\n language: 'ru' | 'en';\n globalAIKeys: AIKey[];\n defaultLLMCurl: string;\n hasDefaultLLM: boolean;\n onLLMChange: (llm: string, apiKey?: string) => void;\n}\n\nconst LLMSelector: React.FC = ({\n promptType,\n language,\n globalAIKeys,\n defaultLLMCurl,\n hasDefaultLLM,\n onLLMChange,\n}) => {\n const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings();\n const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl);\n const [apiKey, setApiKey] = useState('');\n const saveTimeoutRef = useRef(null);\n\n // Загружаем API-ключ при монтировании компонента\n useEffect(() => {\n const loadApiKey = async () => {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n const key = await APIKeyManager.getDecryptedKey(keyId) || '';\n setApiKey(key);\n };\n loadApiKey();\n }, [promptType, language]);\n\n // Загружаем LLM при изменении promptType/language\n useEffect(() => {\n const currentSettings = promptType === 'basic_analysis'\n ? settings.basic_analysis[language]\n : settings.deep_analysis[language];\n\n let initialLLM = '';\n\n if (currentSettings) {\n const savedLLM = currentSettings.llm;\n initialLLM = savedLLM || (hasDefaultLLM ? 'default' : '');\n } else {\n initialLLM = hasDefaultLLM ? 'default' : '';\n }\n\n setSelectedLLM(initialLLM);\n }, [promptType, language, settings, hasDefaultLLM]);\n\n // Сохраняем выбор LLM\n const handleLLMChange = async (newLLM: string) => {\n setSelectedLLM(newLLM);\n\n const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings;\n await updateFn(language, {\n llm: newLLM,\n custom_prompt: promptType === 'basic_analysis'\n ? settings.basic_analysis[language].custom_prompt\n : settings.deep_analysis[language].custom_prompt,\n });\n\n // API-ключ передаем для всех LLM, включая кастомные\n onLLMChange(newLLM, apiKey || undefined);\n };\n\n // Сохраняем API ключ\n const handleApiKeyChange = (newApiKey: string) => {\n setApiKey(newApiKey);\n\n if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);\n\n saveTimeoutRef.current = setTimeout(async () => {\n try {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n await APIKeyManager.saveEncryptedKey(keyId, newApiKey);\n onLLMChange(selectedLLM, newApiKey);\n } catch (error) {\n console.error('Failed to save API key:', error);\n }\n }, 500);\n };\n\n // Получаем список опций для селекта\n const getLLMOptions = () => {\n const options = [\n { value: 'default', label: 'Default LLM' },\n ...globalAIKeys.map(key => ({\n value: key.id,\n label: key.name,\n })),\n ];\n return options;\n };\n\n return (\n
\n \n\n handleLLMChange(e.target.value)}\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n fontSize: '14px',\n marginBottom: '8px'\n }}\n >\n {getLLMOptions().map(option => (\n \n ))}\n \n\n {selectedLLM === 'default' && (\n
\n \n handleApiKeyChange(e.target.value)}\n placeholder=\"Введите API ключ\"\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n fontSize: '14px'\n }}\n />\n
\n )}\n
\n );\n};\n\nexport default LLMSelector;","import * as React from 'react';\nimport { useTranslations } from './useTranslations';\nimport { APIKeyManager } from '../utils/encryption';\n\nexport interface AIKey {\n id: string;\n name: string;\n key: string;\n status: 'configured' | 'not_configured' | 'testing';\n isFixed?: boolean;\n isFree?: boolean;\n}\n\nexport const useAIKeys = () => {\n const { t } = useTranslations('ru');\n const [aiKeys, setAiKeys] = React.useState([\n {\n id: 'gemini-flash-lite',\n name: 'Google Gemini Flash Lite - Базовый анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n {\n id: 'gemini-pro',\n name: 'Gemini 2.5 Pro - Глубокий анализ',\n key: '',\n status: 'not_configured',\n isFixed: true,\n isFree: true,\n },\n ]);\n const [customKeys, setCustomKeys] = React.useState([]);\n\n // Рефы для отложенного сохранения ключей\n const saveTimeoutsRef = React.useRef>({});\n\n // Load AI keys on mount and cleanup timeouts on unmount\n React.useEffect(() => {\n loadAIKeys();\n\n // Cleanup function\n return () => {\n // Очищаем все активные таймауты\n Object.values(saveTimeoutsRef.current).forEach(timeout => {\n clearTimeout(timeout);\n });\n saveTimeoutsRef.current = {};\n };\n }, []);\n\n const loadAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to load AI keys...');\n\n // Загружаем зашифрованные ключи\n const fixedKeyIds = ['gemini-flash-lite', 'gemini-pro'];\n const fixedKeysPromises = fixedKeyIds.map(async (keyId) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(keyId);\n console.log(`[useAIKeys] Loaded key ${keyId}:`, decryptedKey ? 'present' : 'empty');\n return { keyId, decryptedKey };\n });\n\n const fixedKeysResults = await Promise.all(fixedKeysPromises);\n console.log('[useAIKeys] Fixed keys results:', fixedKeysResults);\n\n setAiKeys(prev =>\n prev.map(key => {\n const result = fixedKeysResults.find(r => r.keyId === key.id);\n console.log(`[useAIKeys] Setting key ${key.id}:`, result?.decryptedKey ? 'configured' : 'not_configured');\n return {\n ...key,\n key: result?.decryptedKey || '',\n status: (result?.decryptedKey ? 'configured' : 'not_configured') as AIKey['status'],\n };\n }),\n );\n\n // Загружаем пользовательские ключи (метаданные)\n const result = await chrome.storage.local.get(['customKeys']);\n console.log('[useAIKeys] Custom keys metadata:', result.customKeys);\n if (result.customKeys) {\n // Для пользовательских ключей также используем шифрование\n const customKeysWithDecryption = await Promise.all(\n (result.customKeys as AIKey[]).map(async (key: AIKey) => {\n const decryptedKey = await APIKeyManager.getDecryptedKey(key.id);\n console.log(`[useAIKeys] Custom key ${key.id}:`, decryptedKey ? 'present' : 'empty');\n return {\n ...key,\n key: decryptedKey || '',\n status: (decryptedKey ? 'configured' : 'not_configured') as AIKey['status']\n };\n })\n );\n console.log('[useAIKeys] Setting custom keys:', customKeysWithDecryption);\n setCustomKeys(customKeysWithDecryption);\n } else {\n console.log('[useAIKeys] No custom keys found');\n setCustomKeys([]);\n }\n\n console.log('[useAIKeys] AI keys loaded successfully');\n } catch (error) {\n console.error('Failed to load AI keys:', error);\n // В случае ошибки шифрования показываем пустые ключи\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n key: '',\n status: 'not_configured' as AIKey['status'],\n })),\n );\n setCustomKeys([]);\n }\n };\n\n const saveAIKeys = async () => {\n try {\n console.log('[useAIKeys] Starting to save AI keys...');\n console.log('[useAIKeys] Current aiKeys:', aiKeys);\n console.log('[useAIKeys] Current customKeys:', customKeys);\n\n // Сохраняем фиксированные ключи с шифрованием\n const saveFixedKeysPromises = aiKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving fixed key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing fixed key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveFixedKeysPromises);\n\n // Сохраняем пользовательские ключи с шифрованием\n const saveCustomKeysPromises = customKeys.map(async (key) => {\n if (key.key) {\n console.log(`[useAIKeys] Saving custom key ${key.id}`);\n await APIKeyManager.saveEncryptedKey(key.id, key.key);\n } else {\n console.log(`[useAIKeys] Removing custom key ${key.id}`);\n await APIKeyManager.removeKey(key.id);\n }\n });\n\n await Promise.all(saveCustomKeysPromises);\n\n // Сохраняем метаданные пользовательских ключей (без самих ключей)\n const customKeysMetadata = customKeys.map(key => ({\n id: key.id,\n name: key.name,\n isFixed: false,\n isFree: false,\n // key и status не сохраняем в метаданных для безопасности\n }));\n\n console.log('[useAIKeys] Saving custom keys metadata:', customKeysMetadata);\n await chrome.storage.local.set({\n customKeys: customKeysMetadata,\n });\n\n // Обновляем статусы в состоянии\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status'],\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: (key.key ? 'configured' : 'not_configured') as AIKey['status']\n }))\n );\n\n console.log('[useAIKeys] AI keys saved successfully');\n alert(t('options.settings.aiKeys.messages.saved'));\n } catch (error) {\n console.error('Failed to save AI keys:', error);\n alert(t('options.settings.aiKeys.messages.saveError'));\n }\n };\n\n const testAIKeys = async () => {\n // Set testing status\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: 'testing' as const,\n })),\n );\n\n // Simulate API testing with timeout\n setTimeout(() => {\n setAiKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n setCustomKeys(prev =>\n prev.map(key => ({\n ...key,\n status: key.key ? 'configured' : 'not_configured',\n })),\n );\n\n alert(t('options.settings.aiKeys.messages.testComplete'));\n }, 2000);\n };\n\n const addCustomKey = () => {\n const newKey: AIKey = {\n id: `custom-${Date.now()}`,\n name: `Пользовательский ключ ${customKeys.length + 1}`,\n key: '',\n status: 'not_configured' as const,\n };\n setCustomKeys(prev => [...prev, newKey]);\n };\n\n const removeCustomKey = async (id: string) => {\n try {\n // Удаляем зашифрованный ключ\n await APIKeyManager.removeKey(id);\n\n // Удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n } catch (error) {\n console.error('Failed to remove custom key:', error);\n // Даже если удаление зашифрованного ключа не удалось, удаляем из состояния\n setCustomKeys(prev => prev.filter(key => key.id !== id));\n }\n };\n\n const updateKey = (id: string, value: string, isCustom = false) => {\n console.log(`[useAIKeys] Updating key ${id} with value:`, value ? 'present' : 'empty');\n\n // Обновляем состояние немедленно для лучшего UX\n if (isCustom) {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n } else {\n setAiKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n key: value,\n status: value ? 'configured' : 'not_configured'\n } : key)));\n }\n\n // Отменяем предыдущий таймаут для этого ключа\n if (saveTimeoutsRef.current[id]) {\n clearTimeout(saveTimeoutsRef.current[id]);\n }\n\n // Устанавливаем новый таймаут для отложенного сохранения\n saveTimeoutsRef.current[id] = setTimeout(async () => {\n try {\n console.log(`[useAIKeys] Auto-saving key ${id} after delay`);\n if (value) {\n await APIKeyManager.saveEncryptedKey(id, value);\n } else {\n await APIKeyManager.removeKey(id);\n }\n\n // Для пользовательских ключей также обновляем метаданные\n if (isCustom) {\n const result = await chrome.storage.local.get(['customKeys']);\n const customKeysMetadata = result.customKeys || [];\n const updatedMetadata = customKeysMetadata.map((key: any) =>\n key.id === id ? { ...key, name: key.name } : key\n );\n\n // Если ключ не существует в метаданных, добавляем его\n const existingKeyIndex = updatedMetadata.findIndex((key: any) => key.id === id);\n if (existingKeyIndex === -1 && value) {\n updatedMetadata.push({\n id,\n name: `Пользовательский ключ ${customKeysMetadata.length + 1}`,\n isFixed: false,\n isFree: false,\n });\n }\n\n await chrome.storage.local.set({\n customKeys: updatedMetadata.filter((key: any) => {\n // Убираем из метаданных ключи, которые были удалены\n return value || key.id !== id;\n }),\n });\n }\n\n console.log(`[useAIKeys] Key ${id} auto-saved successfully`);\n } catch (error) {\n console.error(`[useAIKeys] Failed to auto-save key ${id}:`, error);\n } finally {\n // Убираем таймаут из рефа\n delete saveTimeoutsRef.current[id];\n }\n }, 1000); // 1 секунда задержки перед сохранением\n };\n\n const updateCustomKeyName = (id: string, name: string) => {\n setCustomKeys(prev => prev.map(key => (key.id === id ? {\n ...key,\n name,\n status: key.key ? 'configured' : 'not_configured'\n } : key)));\n };\n\n // Функция для получения текста статуса с поддержкой локализации\n const getStatusText = (status: string) => {\n switch (status) {\n case 'configured':\n return t('options.settings.aiKeys.status.configured');\n case 'not_configured':\n return t('options.settings.aiKeys.status.notConfigured');\n case 'testing':\n return t('options.settings.aiKeys.status.testing');\n default:\n return t('options.settings.aiKeys.status.notConfigured');\n }\n };\n\n const getStatusClass = (status: string) => {\n switch (status) {\n case 'configured':\n return 'status-configured';\n case 'not_configured':\n return 'status-not-configured';\n case 'testing':\n return 'status-testing';\n default:\n return '';\n }\n };\n\n // Функция для получения API ключа для использования в MCP-серверах\n const getAPIKeyForMCP = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.getDecryptedKey(keyId);\n } catch (error) {\n console.error('Failed to get API key for MCP:', error);\n return null;\n }\n };\n\n // Функция для проверки, настроен ли определенный ключ\n const isKeyConfigured = async (keyId: string): Promise => {\n try {\n return await APIKeyManager.keyExists(keyId);\n } catch (error) {\n console.error('Failed to check if key is configured:', error);\n return false;\n }\n };\n\n return {\n aiKeys,\n customKeys,\n saveAIKeys,\n testAIKeys,\n addCustomKey,\n removeCustomKey,\n updateKey,\n updateCustomKeyName,\n getStatusText,\n getStatusClass,\n getAPIKeyForMCP,\n isKeyConfigured,\n };\n};\n","import { useTranslations } from '../hooks/useTranslations';\nimport type { Plugin } from '../hooks/usePlugins';\nimport type { PluginSettings } from '@extension/storage';\nimport ToggleButton from './ToggleButton';\nimport LocalErrorBoundary from './LocalErrorBoundary';\nimport LLMSelector from './LLMSelector';\nimport { useAIKeys, AIKey } from '../hooks/useAIKeys';\nimport { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings';\nimport { useState, useEffect } from 'react';\nimport type { ReactNode } from 'react';\n\n// Типы для структуры промптов\ninterface PromptData {\n [key: string]: any;\n}\n\ninterface LanguagePrompts {\n ru: PromptData;\n en: PromptData;\n}\n\ninterface PromptsStructure {\n basic_analysis: LanguagePrompts;\n deep_analysis: LanguagePrompts;\n}\n\nconst cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' ');\n\ninterface PluginDetailsProps {\n selectedPlugin: Plugin | null;\n locale?: 'en' | 'ru';\n onUpdateSetting?: (pluginId: string, setting: keyof PluginSettings, value: boolean) => Promise;\n}\n\ninterface CustomSetting {\n type: 'boolean' | 'select' | 'text' | 'number' | 'prompts';\n default: boolean | string | number | PromptsStructure;\n label: string | { ru: string; en: string };\n description?: string | { ru: string; en: string };\n values?: string[];\n labels?: Record;\n min?: number;\n max?: number;\n step?: number;\n}\n\n// Компонент для редактирования промптов\ninterface PromptsEditorProps {\n value: PromptsStructure;\n manifest: any; // manifest.json структура\n disabled: boolean;\n onSave: (value: PromptsStructure) => void;\n locale: 'en' | 'ru';\n t: (key: string) => string; // функция перевода\n globalAIKeys: AIKey[];\n pluginSettings: PluginSettingsType;\n}\n\nconst PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => {\n const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis');\n const [language, setLanguage] = useState<'ru' | 'en'>('ru');\n const [customPrompt, setCustomPrompt] = useState('');\n\n // Получить дефолтную LLM для конкретного промпта и языка\n const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return 'default';\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n const llmConfig = langPrompts.LLM?.default;\n\n if (!llmConfig) return 'default';\n\n // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro\n if (type === 'basic_analysis') {\n return 'gemini-flash-lite';\n } else if (type === 'deep_analysis') {\n return 'gemini-pro';\n }\n\n return 'default';\n } catch {\n return 'default';\n }\n };\n\n // Проверить наличие дефолтной LLM для конкретного промпта и языка\n const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return false;\n\n const typePrompts = promptsConfig[type] || {};\n const langPrompts = typePrompts[lang] || {};\n return !!langPrompts.LLM?.default;\n } catch {\n return false;\n }\n };\n\n // Получаем оригинальный промпт из manifest\n const getOriginalPrompt = (): string => {\n try {\n const promptsConfig = manifest?.options?.prompts;\n if (!promptsConfig) return '';\n\n const typePrompts = promptsConfig[promptType] || {};\n const langPrompts = typePrompts[language] || {};\n const defaultPrompt = langPrompts.default || '';\n\n // Если defaultPrompt - это объект, преобразуем его\n if (typeof defaultPrompt === 'object') {\n return JSON.stringify(defaultPrompt, null, 2);\n }\n\n return defaultPrompt;\n } catch {\n return '';\n }\n };\n\n // Получаем кастомный промпт\n const getCustomPrompt = (): string => {\n try {\n const prompts = value || {};\n const typePrompts = (prompts as any)[promptType] || {};\n const langPrompts = (typePrompts as any)[language];\n\n // If stored as plain text, show as-is. Only stringify objects.\n if (typeof langPrompts === 'string') return langPrompts;\n if (langPrompts == null) return '';\n return JSON.stringify(langPrompts, null, 2);\n } catch {\n return '';\n }\n };\n\n // Загружаем кастомный промпт при изменении типа или языка\n useEffect(() => {\n setCustomPrompt(getCustomPrompt());\n }, [promptType, language, value]);\n\n const handleCopyToCustom = () => {\n setCustomPrompt(getOriginalPrompt());\n };\n\n const handleSave = () => {\n try {\n const newValue: any = { ...value };\n\n // Ensure container objects exist\n if (!newValue[promptType]) newValue[promptType] = { ru: '', en: '' };\n\n // Store verbatim text (plain string), no JSON requirement\n newValue[promptType][language] = customPrompt;\n\n onSave(newValue);\n } catch (error) {\n console.error('Failed to save custom prompt:', error);\n // Можно добавить уведомление об ошибке\n }\n };\n\n return (\n
\n
\n \n\n {/* Переключатели */}\n
\n
\n \n setPromptType(e.target.value as 'basic_analysis' | 'deep_analysis')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n\n
\n \n setLanguage(e.target.value as 'ru' | 'en')}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n fontSize: '14px'\n }}\n >\n \n \n \n
\n
\n\n {/* Textarea */}\n
\n
\n \n \n
\n\n
\n \n {t('options.plugins.prompts.copyToCustom')}\n \n
\n\n
\n \n setCustomPrompt(e.target.value)}\n disabled={disabled}\n style={{\n width: '100%',\n height: '300px',\n padding: '8px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n fontSize: '12px',\n fontFamily: 'monospace',\n resize: 'vertical'\n }}\n />\n
\n
\n\n {/* LLM Selector для текущего промпта и языка */}\n {\n console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey);\n // Сохраняем выбранную LLM и API ключ в pluginSettings для передачи в mcp_server.py\n const updatedPluginSettings = { ...pluginSettings } as any;\n if (!updatedPluginSettings.selected_llms) {\n updatedPluginSettings.selected_llms = {};\n }\n if (!updatedPluginSettings.selected_llms[promptType]) {\n updatedPluginSettings.selected_llms[promptType] = {};\n }\n // Сохраняем выбранную LLM\n updatedPluginSettings.selected_llms[promptType][language] = llm;\n\n // Сохраняем API ключ если он предоставлен\n if (!updatedPluginSettings.api_keys) {\n updatedPluginSettings.api_keys = {};\n }\n if (apiKey) {\n const keyId = `ozon-analyzer-${promptType}-${language}`;\n updatedPluginSettings.api_keys[keyId] = apiKey;\n }\n\n // Обновляем pluginSettings через глобальный объект\n if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n }\n }}\n />\n\n {/* Кнопка сохранения */}\n
\n \n {t('options.plugins.prompts.save')}\n \n
\n
\n
\n );\n};\n\nconst PluginDetails = (props: PluginDetailsProps) => {\n const { selectedPlugin, locale = 'en', onUpdateSetting } = props;\n const { t } = useTranslations(locale);\n const { aiKeys } = useAIKeys();\n const { settings: pluginSettings } = usePluginSettings();\n const [isUpdating, setIsUpdating] = useState(null);\n const [customSettings, setCustomSettings] = useState | null>(null);\n\n // Хелперы для работы с локализацией\n const getLocalizedText = (text: string | { ru: string; en: string } | undefined): string => {\n if (!text) return '';\n if (typeof text === 'string') return text;\n return text[locale] || text.ru || text.en || '';\n };\n\n // Загружаем пользовательские настройки при выборе плагина\n useEffect(() => {\n if (selectedPlugin?.manifest?.options) {\n loadCustomSettings();\n }\n }, [selectedPlugin?.id]);\n\n // Передаем выбранные LLM в mcp_server.py через pyodide.globals\n useEffect(() => {\n if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) {\n // Создаем структуру selected_llms из pluginSettings\n const selected_llms = {\n basic_analysis: {\n ru: pluginSettings.basic_analysis?.ru?.llm || 'default',\n en: pluginSettings.basic_analysis?.en?.llm || 'default',\n },\n deep_analysis: {\n ru: pluginSettings.deep_analysis?.ru?.llm || 'default',\n en: pluginSettings.deep_analysis?.en?.llm || 'default',\n }\n };\n \n // Обновляем pluginSettings в pyodide.globals\n const updatedPluginSettings = {\n ...(window as any).pyodide.globals.pluginSettings || {},\n selected_llms: selected_llms\n };\n \n (window as any).pyodide.globals.pluginSettings = updatedPluginSettings;\n console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings);\n }\n }, [pluginSettings]);\n\n if (!selectedPlugin || typeof selectedPlugin !== 'object') {\n return (\n
\n

{t('options_plugins_details_title')}

\n

{t('options.plugins.details.selectPlugin')}

\n
\n );\n }\n\n const settings = selectedPlugin.settings || { enabled: true, autorun: false };\n const hostPermissions = selectedPlugin.manifest?.host_permissions || [];\n\n // Функции для работы с chrome.storage.local\n const loadCustomSettings = async () => {\n if (!selectedPlugin || !selectedPlugin.manifest?.options || customSettings !== null) return;\n\n try {\n // Проверяем доступность chrome.storage\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const optionKeys = Object.keys(selectedPlugin.manifest.options);\n const keys = optionKeys.map(key => `${selectedPlugin.id}_${key}`);\n\n const result = await chrome.storage.local.get(keys);\n const loadedSettings: Record = {};\n\n // Преобразуем ключи обратно и применяем значения\n Object.entries(result).forEach(([key, value]) => {\n const settingName = key.replace(`${selectedPlugin.id}_`, '');\n if (value !== undefined) {\n loadedSettings[settingName] = value;\n }\n });\n\n setCustomSettings(loadedSettings);\n }\n } catch (error) {\n console.warn('Failed to load custom settings from chrome.storage.local:', error);\n // Fallback: используем дефолтные значения из manifest\n }\n };\n\n const saveCustomSetting = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n try {\n if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {\n const key = `${selectedPlugin.id}_${setting}`;\n await chrome.storage.local.set({ [key]: value });\n\n // Обновляем локальное состояние\n setCustomSettings(prev => ({\n ...prev,\n [setting]: value\n }));\n\n // Диагностика: проверяем правильность сохранения промптов\n if (setting === 'prompts') {\n await verifyPromptStorage();\n }\n } else {\n console.warn('chrome.storage.local is not available');\n }\n } catch (error) {\n console.error(`Failed to save setting ${setting} to chrome.storage.local:`, error);\n throw error; // Пробрасываем ошибку для обработки в handleSettingChange\n }\n };\n\n // Диагностическая функция для проверки сохранения промптов\n const verifyPromptStorage = async () => {\n if (!selectedPlugin) return;\n\n try {\n const key = `${selectedPlugin.id}_prompts`;\n const stored = await chrome.storage.local.get([key]);\n console.log('🔍 Диагностика промптов:');\n console.log(` Plugin ID: ${selectedPlugin.id}`);\n console.log(` Storage key: ${key}`);\n console.log(' Сохраненные промпты:', stored[key]);\n\n if (stored[key]) {\n const prompts = stored[key] as PromptsStructure;\n console.log(' Структура промптов:');\n console.log(` - basic_analysis.ru: ${prompts.basic_analysis?.ru ? '✓' : '✗'} (${prompts.basic_analysis?.ru?.length || 0} символов)`);\n console.log(` - basic_analysis.en: ${prompts.basic_analysis?.en ? '✓' : '✗'} (${prompts.basic_analysis?.en?.length || 0} символов)`);\n console.log(` - deep_analysis.ru: ${prompts.deep_analysis?.ru ? '✓' : '✗'} (${prompts.deep_analysis?.ru?.length || 0} символов)`);\n console.log(` - deep_analysis.en: ${prompts.deep_analysis?.en ? '✓' : '✗'} (${prompts.deep_analysis?.en?.length || 0} символов)`);\n }\n } catch (error) {\n console.error('Ошибка диагностики промптов:', error);\n }\n };\n\n\n // Хелпер для получения значения настройки с приоритетом: chrome.storage -> manifest\n const getCustomSettingValue = (settingName: string, defaultValue: boolean | string | number | PromptsStructure): boolean | string | number | PromptsStructure => {\n if (customSettings && customSettings[settingName] !== undefined) {\n return customSettings[settingName];\n }\n\n // Специальная обработка для промптов: преобразуем структуру из manifest в PromptsStructure\n if (settingName === 'prompts' && typeof defaultValue === 'object' && defaultValue !== null) {\n const promptsConfig = defaultValue as any;\n const result: PromptsStructure = {\n basic_analysis: { ru: {}, en: {} },\n deep_analysis: { ru: {}, en: {} }\n };\n\n // Извлекаем default значения из структуры manifest\n if (promptsConfig.basic_analysis?.ru?.default) {\n result.basic_analysis.ru = promptsConfig.basic_analysis.ru.default;\n }\n if (promptsConfig.basic_analysis?.en?.default) {\n result.basic_analysis.en = promptsConfig.basic_analysis.en.default;\n }\n if (promptsConfig.deep_analysis?.ru?.default) {\n result.deep_analysis.ru = promptsConfig.deep_analysis.ru.default;\n }\n if (promptsConfig.deep_analysis?.en?.default) {\n result.deep_analysis.en = promptsConfig.deep_analysis.en.default;\n }\n\n return result;\n }\n\n return defaultValue;\n };\n\n const renderCustomSetting = (key: string, config: CustomSetting): ReactNode | null => {\n const value = getCustomSettingValue(key, config.default);\n const disabled = isUpdating === key || !(settings.enabled ?? true);\n const localizedLabel = getLocalizedText(config.label);\n const localizedDescription = getLocalizedText(config.description);\n\n // Специальная обработка для промптов\n if (key === 'prompts') {\n return (\n
\n handleSettingChange(key, newValue)}\n locale={locale}\n t={t}\n globalAIKeys={aiKeys}\n pluginSettings={pluginSettings}\n />\n
\n );\n }\n\n if (config.type === 'boolean') {\n return (\n
\n handleSettingChange(key, val)}\n label={\n <>\n {localizedLabel}\n {localizedDescription && (\n \n i\n \n )}\n \n }\n />\n
\n );\n }\n\n if (config.type === 'select') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'text') {\n return (\n
\n \n
\n );\n }\n\n if (config.type === 'number') {\n const handleNumberChange = (e: React.ChangeEvent) => {\n const numValue = parseFloat(e.target.value);\n if (isNaN(numValue)) return;\n\n // Валидация диапазона\n let validatedValue = numValue;\n if (config.min !== undefined && validatedValue < config.min) {\n validatedValue = config.min;\n }\n if (config.max !== undefined && validatedValue > config.max) {\n validatedValue = config.max;\n }\n\n handleSettingChange(key, validatedValue);\n };\n\n return (\n
\n \n
\n );\n }\n\n return null;\n };\n\n const handleSettingChange = async (setting: string, value: boolean | string | number | PromptsStructure) => {\n if (!selectedPlugin) return;\n\n // Проверяем, является ли настройка пользовательской\n const options = selectedPlugin.manifest?.options;\n if (options && options[setting as keyof typeof options]) {\n // Ленивая загрузка: загружаем настройки только при первом взаимодействии\n if (customSettings === null) {\n await loadCustomSettings();\n }\n\n try {\n setIsUpdating(setting);\n await saveCustomSetting(setting, value);\n } catch (error) {\n console.error(`Failed to update custom setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n } else {\n // Стандартные настройки (enabled/autorun) используем через callback\n if (onUpdateSetting) {\n try {\n setIsUpdating(setting);\n await onUpdateSetting(selectedPlugin.id, setting as keyof PluginSettings, value as boolean);\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n } finally {\n setIsUpdating(null);\n }\n }\n }\n };\n\n // Precompute custom settings elements to satisfy TypeScript\n const optionEntries = Object.entries(selectedPlugin.manifest?.options ?? {}) as [string, CustomSetting][];\n const customSettingElements: ReactNode[] = optionEntries\n .map(([key, config]) => renderCustomSetting(key, config))\n .filter((item): item is ReactNode => item !== null);\n\n // Render helper to avoid union/unknown in JSX for plugin settings section\n const PluginSettingsSection = () => (\n
\n

Настройки плагина

\n
\n handleSettingChange('enabled', val)}\n label={\n <>\n Включен\n \n i\n \n \n }\n />\n
\n
\n handleSettingChange('autorun', val)}\n label={\n <>\n Автоматический запуск\n \n i\n \n \n }\n />\n
\n
\n );\n\n return (\n \n
\n

{selectedPlugin.name}

\n
\n
\n
\n

\n Версия: v{selectedPlugin.version}\n

\n

\n Статус:\n \n {settings.enabled ? 'Активен' : 'Неактивен'}\n \n

\n

\n Автор: {selectedPlugin.manifest?.author || 'Не указан'}\n

\n

\n Последнее обновление: {selectedPlugin.manifest?.last_updated || 'Неизвестно'}\n

\n
\n\n
\n

Описание

\n

{selectedPlugin.description}

\n
\n\n {/* Сайты/домены, на которых работает плагин */}\n {hostPermissions.length > 0 && (\n
\n

Сайты/домены

\n
    \n {hostPermissions.map((host: string, idx: number) => (\n
  • {host}
  • \n ))}\n
\n
\n )}\n\n {Array.isArray(selectedPlugin.manifest?.permissions) ? (\n
\n

Разрешения

\n
    \n {(selectedPlugin.manifest?.permissions ?? []).map((permission: string, idx: number) => (\n
  • {permission}
  • \n ))}\n
\n
\n ) : null}\n\n \n\n {/* Пользовательские настройки */}\n {(selectedPlugin.manifest?.options && Object.keys(selectedPlugin.manifest.options).length > 0) ? (\n
\n

Дополнительные настройки

\n {customSettingElements}\n
\n ) : null}\n\n\n
\n
\n
\n );\n};\n\nexport { PluginDetails };\n\nexport default PluginDetails;\n","import '../../../options/src/Options.css'; // Импорт стилей из options для идентичности\nimport type React from 'react';\nimport OptionsPluginDetails from '../../../options/src/components/PluginDetails';\nimport type { PluginSettings } from '@extension/storage';\n\ninterface PluginDetailsProps {\n plugin: Plugin;\n onUpdateSetting?: (pluginId: string, setting: string, value: boolean) => Promise;\n}\n\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\nexport const PluginDetails: React.FC = ({ plugin, onUpdateSetting }) => {\n // Адаптируем onUpdateSetting для совместимости с OptionsPluginDetails\n const adaptedOnUpdateSetting = onUpdateSetting ? async (pluginId: string, setting: keyof PluginSettings, value: boolean): Promise => {\n try {\n await onUpdateSetting(pluginId, setting as string, value);\n return true; // Успешно\n } catch (error) {\n console.error(`Failed to update setting ${setting}:`, error);\n return false; // Неудача\n }\n } : undefined;\n\n const adaptedProps = {\n selectedPlugin: {\n ...plugin,\n description: plugin.description || 'Описание не указано',\n icon: plugin.icon || '',\n manifest: plugin.manifest || {} as any\n },\n locale: 'ru' as const,\n onUpdateSetting: adaptedOnUpdateSetting\n };\n\n return ;\n};\n","import type { ExcludeValuesFromBaseArrayType } from './types.js';\n\nexport const excludeValuesFromBaseArray = (\n baseArray: B,\n excludeArray: E,\n) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType;\n\nexport const sleep = async (time: number) => new Promise(r => setTimeout(r, time));\n\n/**\n * Унифицированное получение pageKey из URL страницы.\n * Удаляет search/hash, возвращает нормализованный URL или 'unknown-page'.\n */\nexport const getPageKey = function (currentTabUrl: string | null): string {\n if (!currentTabUrl) return 'unknown-page';\n try {\n const url = new URL(currentTabUrl);\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return currentTabUrl;\n }\n};\n","import { useState, useEffect, useRef, useCallback } from 'react';\n\ninterface UseLazyChatSyncOptions {\n pluginId: string;\n pageKey: string;\n debounceMs?: number; // Задержка перед синхронизацией (по умолчанию 1000ms)\n}\n\ninterface UseLazyChatSyncReturn {\n message: string;\n setMessage: (text: string) => void;\n isDraftSaved: boolean;\n isDraftLoading: boolean;\n draftError: string | null;\n loadDraft: () => Promise;\n clearDraft: () => Promise;\n draftText: string;\n}\n\nexport const useLazyChatSync = ({\n pluginId,\n pageKey,\n debounceMs = 1000,\n}: UseLazyChatSyncOptions): UseLazyChatSyncReturn => {\n const [message, setMessageState] = useState('');\n const [isDraftSaved, setIsDraftSaved] = useState(false);\n const [isDraftLoading, setIsDraftLoading] = useState(false);\n const [draftError, setDraftError] = useState(null);\n const [draftText, setDraftText] = useState('');\n\n const debounceRef = useRef(null);\n const lastSavedText = useRef('');\n\n // Функция для сохранения черновика\n const saveDraft = useCallback(\n async (text: string) => {\n if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился\n console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: text,\n });\n lastSavedText.current = text;\n setIsDraftSaved(true);\n setDraftError(null);\n setDraftText(text);\n console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text });\n } catch (error) {\n console.error('[useLazyChatSync] Error saving draft:', error);\n setDraftError('Ошибка сохранения черновика');\n setIsDraftSaved(false);\n }\n },\n [pluginId, pageKey],\n );\n\n // Функция для загрузки черновика\n const loadDraft = useCallback(async () => {\n setIsDraftLoading(true);\n setDraftError(null);\n console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey });\n try {\n const response = await chrome.runtime.sendMessage({\n type: 'GET_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n });\n if (response?.draftText) {\n setMessageState(response.draftText);\n lastSavedText.current = response.draftText;\n setIsDraftSaved(true);\n setDraftText(response.draftText);\n console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText });\n } else {\n setMessageState('');\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftText('');\n console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey });\n }\n } catch (error) {\n console.error('[useLazyChatSync] Error loading draft:', error);\n setDraftError('Ошибка загрузки черновика');\n } finally {\n setIsDraftLoading(false);\n }\n }, [pluginId, pageKey]);\n\n // Функция для очистки черновика\n const clearDraft = useCallback(async () => {\n console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey });\n try {\n await chrome.runtime.sendMessage({\n type: 'SAVE_PLUGIN_CHAT_DRAFT',\n pluginId,\n pageKey,\n draftText: '',\n });\n lastSavedText.current = '';\n setIsDraftSaved(false);\n setDraftError(null);\n setDraftText('');\n setMessageState('');\n console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey });\n } catch (error) {\n console.error('[useLazyChatSync] Error clearing draft:', error);\n setDraftError('Ошибка очистки черновика');\n }\n }, [pluginId, pageKey]);\n\n // Обновленная функция setMessage с ленивой синхронизацией\n const setMessage = useCallback(\n (text: string) => {\n setMessageState(text);\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (text.length === 0) {\n clearDraft();\n } else {\n debounceRef.current = setTimeout(() => {\n saveDraft(text);\n }, debounceMs);\n }\n console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text });\n },\n [debounceMs, saveDraft, clearDraft, pluginId, pageKey],\n );\n\n // Очистка таймера при размонтировании\n useEffect(\n () => () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n },\n [],\n );\n\n // Автоматическая загрузка черновика при монтировании\n useEffect(() => {\n loadDraft();\n }, [loadDraft]);\n\n // При смене pageKey всегда загружаем draft, не сбрасываем message вручную\n useEffect(() => {\n console.log('[useLazyChatSync] pageKey изменился:', pageKey);\n setIsDraftSaved(false);\n setIsDraftLoading(false);\n setDraftError(null);\n lastSavedText.current = '';\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n loadDraft();\n }, [pageKey, loadDraft]);\n\n return {\n message,\n setMessage,\n isDraftSaved,\n isDraftLoading,\n draftError,\n loadDraft,\n clearDraft,\n draftText,\n };\n};\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","/**\n * Storage area type for persisting and exchanging data.\n * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview\n */\nexport var StorageEnum;\n(function (StorageEnum) {\n /**\n * Persist data locally against browser restarts. Will be deleted by uninstalling the extension.\n * @default\n */\n StorageEnum[\"Local\"] = \"local\";\n /**\n * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply.\n */\n StorageEnum[\"Sync\"] = \"sync\";\n /**\n * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a\n * json schema for company wide config.\n */\n StorageEnum[\"Managed\"] = \"managed\";\n /**\n * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and\n * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access.\n * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session)\n */\n StorageEnum[\"Session\"] = \"session\";\n})(StorageEnum || (StorageEnum = {}));\n/**\n * Global access level requirement for the {@link StorageEnum.Session} Storage Area.\n * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel)\n */\nexport var SessionAccessLevelEnum;\n(function (SessionAccessLevelEnum) {\n /**\n * Storage can only be accessed by Extension pages (not Content scripts).\n * @default\n */\n SessionAccessLevelEnum[\"ExtensionPagesOnly\"] = \"TRUSTED_CONTEXTS\";\n /**\n * Storage can be accessed by both Extension pages and Content scripts.\n */\n SessionAccessLevelEnum[\"ExtensionPagesAndContentScripts\"] = \"TRUSTED_AND_UNTRUSTED_CONTEXTS\";\n})(SessionAccessLevelEnum || (SessionAccessLevelEnum = {}));\n","import { SessionAccessLevelEnum, StorageEnum } from './enums.js';\n/**\n * Chrome reference error while running `processTailwindFeatures` in tailwindcss.\n * To avoid this, we need to check if globalThis.chrome is available and add fallback logic.\n */\nconst chrome = globalThis.chrome;\n/**\n * Sets or updates an arbitrary cache with a new value or the result of an update function.\n */\nconst updateCache = async (valueOrUpdate, cache) => {\n // Type guard to check if our value or update is a function\n const isFunction = (value) => typeof value === 'function';\n // Type guard to check in case of a function if it's a Promise\n const returnsPromise = (func) => \n // Use ReturnType to infer the return type of the function and check if it's a Promise\n func instanceof Promise;\n if (isFunction(valueOrUpdate)) {\n // Check if the function returns a Promise\n if (returnsPromise(valueOrUpdate)) {\n return valueOrUpdate(cache);\n }\n else {\n return valueOrUpdate(cache);\n }\n }\n else {\n return valueOrUpdate;\n }\n};\n/**\n * If one session storage needs access from content scripts, we need to enable it globally.\n * @default false\n */\nlet globalSessionAccessLevelFlag = false;\n/**\n * Checks if the storage permission is granted in the manifest.json.\n */\nconst checkStoragePermission = (storageEnum) => {\n if (!chrome) {\n return;\n }\n if (!chrome.storage[storageEnum]) {\n throw new Error(`\"storage\" permission in manifest.ts: \"storage ${storageEnum}\" isn't defined`);\n }\n};\n/**\n * Creates a storage area for persisting and exchanging data.\n */\nexport const createStorage = (key, fallback, config) => {\n let cache = null;\n let initialCache = false;\n let listeners = [];\n const storageEnum = config?.storageEnum ?? StorageEnum.Local;\n const liveUpdate = config?.liveUpdate ?? false;\n const serialize = config?.serialization?.serialize ?? ((v) => v);\n const deserialize = config?.serialization?.deserialize ?? (v => v);\n // Set global session storage access level for StoryType.Session, only when not already done but needed.\n if (globalSessionAccessLevelFlag === false &&\n storageEnum === StorageEnum.Session &&\n config?.sessionAccessForContentScripts === true) {\n checkStoragePermission(storageEnum);\n chrome?.storage[storageEnum]\n .setAccessLevel({\n accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts,\n })\n .catch(error => {\n console.error(error);\n console.error('Please call .setAccessLevel() into different context, like a background script.');\n });\n globalSessionAccessLevelFlag = true;\n }\n // Register life cycle methods\n const get = async () => {\n checkStoragePermission(storageEnum);\n const value = await chrome?.storage[storageEnum].get([key]);\n if (!value) {\n return fallback;\n }\n return deserialize(value[key]) ?? fallback;\n };\n const set = async (valueOrUpdate) => {\n if (!initialCache) {\n cache = await get();\n }\n cache = await updateCache(valueOrUpdate, cache);\n await chrome?.storage[storageEnum].set({ [key]: serialize(cache) });\n _emitChange();\n };\n const subscribe = (listener) => {\n listeners = [...listeners, listener];\n return () => {\n listeners = listeners.filter(l => l !== listener);\n };\n };\n const getSnapshot = () => cache;\n const _emitChange = () => {\n listeners.forEach(listener => listener());\n };\n // Listener for live updates from the browser\n const _updateFromStorageOnChanged = async (changes) => {\n // Check if the key we are listening for is in the changes object\n if (changes[key] === undefined)\n return;\n const valueOrUpdate = deserialize(changes[key].newValue);\n if (cache === valueOrUpdate)\n return;\n cache = await updateCache(valueOrUpdate, cache);\n _emitChange();\n };\n get().then(data => {\n cache = data;\n initialCache = true;\n _emitChange();\n });\n // Register listener for live updates for our storage area\n if (liveUpdate) {\n chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);\n }\n return {\n get,\n set,\n getSnapshot,\n subscribe,\n };\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('theme-storage-key', {\n theme: 'system',\n isLight: getSystemTheme(),\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\n// Функция для определения системной темы\nfunction getSystemTheme() {\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: light)').matches;\n }\n return true; // По умолчанию светлая тема\n}\nexport const exampleThemeStorage = {\n ...storage,\n toggle: async () => {\n await storage.set(currentState => {\n let newTheme;\n switch (currentState.theme) {\n case 'light':\n newTheme = 'dark';\n break;\n case 'dark':\n newTheme = 'system';\n break;\n case 'system':\n default:\n newTheme = 'light';\n break;\n }\n const isLight = newTheme === 'system' ? getSystemTheme() : newTheme === 'light';\n return {\n theme: newTheme,\n isLight,\n };\n });\n },\n};\n","import { createStorage, StorageEnum } from '../base/index.js';\nconst storage = createStorage('chat-alignment-storage-key', {\n alignment: 'left',\n}, {\n storageEnum: StorageEnum.Local,\n liveUpdate: true,\n});\nexport const exampleChatAlignmentStorage = {\n ...storage,\n setAlignment: async (alignment) => {\n await storage.set({ alignment });\n },\n getAlignment: async () => {\n const state = await storage.get();\n return state.alignment;\n },\n};\n","/**\n * PluginControlPanel.tsx - Панель управления плагином с чатом\n *\n * ИСПРАВЛЕНИЕ ПРОБЛЕМЫ С ПОЛУЧЕНИЕМ ОТВЕТА GET_PLUGIN_CHAT:\n * ========================================================\n *\n * Проблема: Background отправлял ответ через sendResponse() callback, но компонент\n * ожидал ответ через chrome.runtime.sendMessage() с типом 'GET_PLUGIN_CHAT_RESPONSE'.\n *\n * Решение: Изменен механизм коммуникации на использование Promise-based подхода\n * с помощью sendMessageToBackgroundAsync(), который правильно работает с sendResponse().\n *\n * Теперь:\n * 1. Компонент отправляет GET_PLUGIN_CHAT через chrome.runtime.sendMessage()\n * 2. Background получает сообщение и отвечает через sendResponse()\n * 3. Компонент получает ответ через Promise и обрабатывает его\n *\n * Диагностика:\n * - Логи sendMessageToBackgroundAsync покажут отправку и получение ответа\n * - Логи loadChat покажут обработку ответа\n * - Логи processChatResponse покажут разбор данных чата\n */\n\nimport { DraftStatus } from './DraftStatus';\nimport { PluginDetails } from './PluginDetails';\nimport { getPageKey } from '../../../../packages/shared/lib/utils/helpers';\nimport { useLazyChatSync } from '../hooks/useLazyChatSync';\nimport { saveAs } from 'file-saver';\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport './PluginControlPanel.css';\nimport type React from 'react';\nimport { exampleChatAlignmentStorage, type ChatAlignment } from '@extension/storage';\n\n// Определение типа Plugin для PluginControlPanel\ntype Plugin = {\n id: string;\n name: string;\n version: string;\n description?: string;\n icon?: string;\n iconUrl?: string;\n manifest?: Record;\n host_permissions?: string[];\n settings?: {\n enabled?: boolean;\n autorun?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Новый тип для сообщений чата\ninterface ChatMessage {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: number;\n}\n\ninterface PluginControlPanelProps {\n plugin: Plugin;\n currentView: PanelView;\n isRunning: boolean;\n isPaused: boolean;\n currentTabUrl: string | null;\n onStart: () => void;\n onPause: () => void;\n onStop: () => void;\n onClose: () => void;\n}\n\nexport type PanelView = 'chat' | 'details';\n\nexport const PluginControlPanel: React.FC = ({\n plugin,\n currentView,\n isRunning,\n isPaused,\n currentTabUrl,\n onStart,\n onPause,\n onStop,\n onClose,\n}) => {\n // Состояние для активной вкладки в панели управления\n const [activeTab, setActiveTab] = useState('chat');\n // Состояние для текущего pageKey с динамическим обновлением\n const [currentPageKey, setCurrentPageKey] = useState(getPageKey(currentTabUrl));\n\n const [chatTextAlign, setChatTextAlign] = useState('left');\n\n // useEffect для обновления pageKey при изменении currentTabUrl\n useEffect(() => {\n const newPageKey = getPageKey(currentTabUrl);\n console.log('[PluginControlPanel] currentTabUrl изменился:', {\n oldPageKey: currentPageKey,\n newPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n setCurrentPageKey(newPageKey);\n }, [currentTabUrl]);\n\n useEffect(() => {\n const loadAlignment = async () => {\n const alignment = await exampleChatAlignmentStorage.getAlignment();\n setChatTextAlign(alignment);\n };\n\n loadAlignment();\n\n const unsubscribe = exampleChatAlignmentStorage.subscribe(() => {\n loadAlignment();\n });\n\n return unsubscribe;\n }, []);\n // Используем хук для ленивой синхронизации\n const { message, setMessage, isDraftSaved, isDraftLoading, draftError, loadDraft, clearDraft, draftText } =\n useLazyChatSync({\n pluginId: plugin.id,\n pageKey: currentPageKey, // <-- Теперь динамический pageKey\n debounceMs: 1000, // 1 секунда задержки\n });\n\n const [messages, setMessages] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n const [inputHeight, setInputHeight] = useState(60); // Начальная высота поля ввода\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef(null);\n const textareaRef = useRef(null);\n\n useEffect(() => {\n if (!isRunning) {\n // Удалить все вызовы setStopped(...)\n }\n }, [isRunning]);\n\n const handleStart = () => {\n // Удалить все вызовы setStopped(...)\n onStart();\n };\n\n const pluginName =\n plugin.name || (typeof plugin.manifest?.name === 'string' ? plugin.manifest.name : '') || plugin.id;\n\n // Получаем ключ чата для текущего плагина и страницы\n const pluginId = plugin.id;\n\n // Вспомогательная функция для отправки сообщений в background с ожиданием ответа\n const sendMessageToBackgroundAsync = useCallback(async (message: any): Promise => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n try {\n const response = await chrome.runtime.sendMessage(messageWithId);\n return response;\n } catch (error) {\n console.error('[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:', error);\n throw error;\n }\n }, []);\n\n // Вспомогательная функция для отправки сообщений в background без ожидания ответа (для обратной совместимости)\n const sendMessageToBackground = useCallback((message: any): void => {\n const messageId = Date.now().toString() + Math.random().toString(36).substr(2, 9);\n const messageWithId = { ...message, messageId };\n\n chrome.runtime.sendMessage(messageWithId);\n }, []);\n\n // Функция для тестирования обработки сообщений с проблемными данными\n const testMessageProcessing = useCallback(() => {\n console.log('[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными');\n\n // Тест 1: Сообщение с объектом вместо строки в text\n const testMessageWithObject = {\n messages: [{\n id: 'test_obj_1',\n text: { content: 'Это объект вместо строки', type: 'object' }, // Объект вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 2: Сообщение с null в text\n const testMessageWithNull = {\n messages: [{\n id: 'test_null_1',\n text: null, // null вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Тест 3: Сообщение с undefined в text\n const testMessageWithUndefined = {\n messages: [{\n id: 'test_undef_1',\n text: undefined, // undefined вместо строки\n role: 'user',\n timestamp: Date.now()\n }]\n };\n\n // Объявляем processChatResponse локально для избежания проблем с temporal dead zone\n const localProcessChatResponse = (response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(msg.timestamp || Date.now() + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: msg.timestamp || Date.now(),\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n };\n\n try {\n console.log('[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text');\n localProcessChatResponse(testMessageWithObject);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 1 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 2: Обработка сообщения с null в text');\n localProcessChatResponse(testMessageWithNull);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 2 провалился:', error);\n }\n\n try {\n console.log('[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text');\n localProcessChatResponse(testMessageWithUndefined);\n } catch (error) {\n console.error('[PluginControlPanel] ❌ Тест 3 провалился:', error);\n }\n\n console.log('[PluginControlPanel] ✅ Тестирование обработки сообщений завершено');\n }, []); // Убрали processChatResponse из зависимостей\n\n // Вспомогательная функция для обработки ответа чата\n const processChatResponse = useCallback((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n\n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n\n console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${index}:`, textContent);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n\n console.log(`[PluginControlPanel] TextContent after JSON parse for message ${index}:`, textContent);\n }\n\n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n console.log(`[PluginControlPanel] Final converted text for message ${index}:`, convertedMsg.text);\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n }, []);\n\n // Загрузка истории чата при монтировании или смене плагина/страницы\n const loadChat = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n console.log('[PluginControlPanel] ===== НАЧАЛО loadChat =====', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString(),\n isRunning,\n isPaused\n });\n\n try {\n console.log('[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT');\n const response = await sendMessageToBackgroundAsync({\n type: 'GET_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n console.log('[PluginControlPanel] loadChat - получен ответ от background:', response);\n console.log('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:', {\n loading,\n messagesCount: messages.length,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку при получении ответа\n console.log('[PluginControlPanel] loadChat - loading сброшен в false');\n\n if (response?.error) {\n console.error('[PluginControlPanel] loadChat - ошибка в ответе:', response.error);\n setError(`Ошибка загрузки чата: ${response.error}`);\n setMessages([]);\n } else {\n console.log('[PluginControlPanel] loadChat - обрабатываем успешный ответ');\n // Используем анонимную функцию вместо прямого вызова processChatResponse\n // чтобы избежать проблемы с temporal dead zone\n ((response: any) => {\n console.log('[PluginControlPanel] ===== НАЧАЛО processChatResponse =====');\n console.log('[PluginControlPanel] Анализ chatData:', {\n response,\n hasMessages: response && 'messages' in response,\n hasChat: response && 'chat' in response,\n messagesValue: response?.messages,\n chatValue: response?.chat,\n isMessagesArray: Array.isArray(response?.messages),\n isChatArray: Array.isArray(response?.chat),\n responseType: typeof response,\n responseKeys: response ? Object.keys(response) : 'response is null/undefined',\n timestamp: new Date().toISOString()\n });\n\n // Обработка случая пустого чата (background возвращает null)\n if (response === null) {\n console.log('[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений');\n setMessages([]);\n return;\n }\n\n // Обработка разных форматов ответа с дополнительной диагностикой\n let messagesArray = null;\n\n // Список возможных путей к массиву сообщений в приоритете\n const messagePaths = [\n { path: ['messages'], description: 'messages' },\n { path: ['chat'], description: 'chat' },\n { path: ['chat', 'messages'], description: 'chat.messages' },\n { path: ['data', 'messages'], description: 'data.messages' },\n { path: ['result', 'messages'], description: 'result.messages' },\n { path: ['items'], description: 'items' },\n { path: ['history'], description: 'history' },\n { path: ['logs'], description: 'logs' },\n ];\n\n // Функция для извлечения значения по пути\n const getValueByPath = (obj: any, path: string[]): any => {\n let current = obj;\n for (const key of path) {\n if (current && typeof current === 'object' && key in current) {\n current = current[key];\n } else {\n return undefined;\n }\n }\n return current;\n };\n\n // Если response является массивом напрямую\n if (Array.isArray(response)) {\n messagesArray = response;\n console.log('[PluginControlPanel] ✅ Ответ является массивом напрямую:', {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n } else if (response && typeof response === 'object') {\n // Обработка ошибок от background\n if (response.error) {\n console.error('[PluginControlPanel] ❌ Background вернул ошибку:', response.error);\n setError(`Ошибка от background: ${response.error}`);\n setMessages([]);\n return;\n }\n\n // Поиск массива сообщений по возможным путям\n for (const { path, description } of messagePaths) {\n const candidate = getValueByPath(response, path);\n if (Array.isArray(candidate)) {\n messagesArray = candidate;\n console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${description}':`, {\n length: messagesArray.length,\n firstMessage: messagesArray[0] ? {\n id: messagesArray[0].id,\n content: messagesArray[0].content || messagesArray[0].text,\n role: messagesArray[0].role,\n timestamp: messagesArray[0].timestamp,\n } : 'no messages'\n });\n break;\n }\n }\n\n // Если не нашли массив, логируем структуру объекта для диагностики\n if (!messagesArray) {\n console.warn('[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:', {\n responseType: typeof response,\n responseKeys: Object.keys(response),\n responseSample: JSON.stringify(response).substring(0, 500),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n } else {\n // Response не является объектом или массивом\n console.warn('[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:', {\n response,\n responseType: typeof response,\n responseStringified: JSON.stringify(response).substring(0, 200),\n timestamp: new Date().toISOString()\n });\n messagesArray = [];\n }\n\n console.log('[PluginControlPanel] Финальный messagesArray:', {\n messagesArray,\n isArray: Array.isArray(messagesArray),\n length: messagesArray?.length,\n firstMessage: messagesArray?.[0],\n firstMessageType: messagesArray?.[0] ? typeof messagesArray[0] : 'none',\n });\n\n // Конвертация сообщений из формата background в формат компонента\n if (Array.isArray(messagesArray) && messagesArray.length > 0) {\n console.log('[PluginControlPanel] Начинаем конвертацию сообщений:', messagesArray.length);\n\n // Конвертируем сообщения из формата background в формат компонента\n const convertedMessages: ChatMessage[] = messagesArray\n .filter((msg: any) => {\n if (!msg || typeof msg !== 'object') {\n console.warn('[PluginControlPanel] Фильтруем некорректное сообщение:', msg);\n return false;\n }\n return true;\n })\n .map((msg: any, index: number) => {\n try {\n // Строгая проверка и конвертация поля text\n let textContent = msg.content || msg.text || '';\n let messageTimestamp = msg.timestamp || Date.now();\n \n // Если text является объектом, конвертируем его в строку\n if (typeof textContent === 'object') {\n console.warn('[PluginControlPanel] text является объектом, конвертируем:', textContent);\n textContent = JSON.stringify(textContent);\n } else if (textContent === null || textContent === undefined) {\n console.warn('[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку');\n textContent = '';\n } else {\n // Убеждаемся, что это строка\n textContent = String(textContent);\n \n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(textContent);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распаршен JSON из content:', parsedContent);\n textContent = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] content не является JSON, оставляем как есть');\n }\n }\n \n const convertedMsg: ChatMessage = {\n id: msg.id || String(messageTimestamp + index),\n text: textContent,\n isUser: msg.role ? msg.role === 'user' : !!msg.isUser,\n timestamp: messageTimestamp,\n };\n\n console.log(`[PluginControlPanel] Конвертировано сообщение ${index}:`, {\n id: convertedMsg.id,\n textLength: convertedMsg.text.length,\n textType: typeof convertedMsg.text,\n isUser: convertedMsg.isUser\n });\n\n return convertedMsg;\n } catch (conversionError) {\n console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${index}:`, conversionError, msg);\n // Возвращаем безопасное сообщение в случае ошибки\n return {\n id: `error_${Date.now()}_${index}`,\n text: '[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]',\n isUser: false,\n timestamp: Date.now(),\n };\n }\n });\n\n console.log('[PluginControlPanel] ✅ Успешная конвертация сообщений:', {\n originalCount: messagesArray.length,\n convertedCount: convertedMessages.length,\n firstConverted: convertedMessages[0],\n // Безопасная обработка текста сообщений с проверкой типов\n allConverted: convertedMessages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 50) : String(m.text || '').substring(0, 50);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (textError) {\n console.warn('[PluginControlPanel] Error processing message text:', textError, m);\n return { id: m.id, text: '[ERROR: invalid text]', isUser: m.isUser, textType: typeof m.text };\n }\n })\n });\n\n setMessages(convertedMessages);\n } else {\n console.log('[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив');\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====');\n })(response);\n console.log('[PluginControlPanel] loadChat: чат успешно загружен');\n }\n\n } catch (error) {\n console.error('[PluginControlPanel] loadChat - ошибка при получении ответа:', error);\n console.error('[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:', {\n loading,\n messagesCount: messages.length,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: new Date().toISOString()\n });\n\n // Детальное логирование ошибки с трассировкой стека\n console.error('[PluginControlPanel] loadChat - ERROR DETAILS:', {\n error,\n errorType: typeof error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : 'No stack trace',\n errorName: error instanceof Error ? error.name : 'Unknown error type',\n timestamp: new Date().toISOString(),\n pluginId,\n pageKey: currentPageKey\n });\n\n setLoading(false);\n console.log('[PluginControlPanel] loadChat - loading сброшен в false в catch блоке');\n\n // Улучшенная обработка ошибок с проверкой типа\n let errorMessage = 'Ошибка связи с background';\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Специальная обработка для TypeError с substring\n if (error.name === 'TypeError' && error.message.includes('substring')) {\n errorMessage += ' (ошибка обработки текста - проверьте тип данных)';\n console.error('[PluginControlPanel] CRITICAL: substring error detected:', {\n originalError: error,\n stack: error.stack,\n context: { pluginId, pageKey: currentPageKey }\n });\n }\n } else {\n errorMessage += `: ${String(error)}`;\n }\n\n setError(errorMessage);\n setMessages([]);\n }\n\n console.log('[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====');\n }, [pluginId, currentPageKey, sendMessageToBackgroundAsync, currentTabUrl, isRunning, isPaused]);\n\n // Добавить useEffect для вызова loadChat при монтировании и смене pluginId/pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat', {\n pluginId,\n pageKey: currentPageKey,\n currentTabUrl,\n timestamp: new Date().toISOString()\n });\n\n // Вызываем асинхронную функцию без await, так как useEffect не может быть async\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:', error);\n });\n }, [loadChat]);\n\n // useEffect для перезагрузки чата при смене pageKey\n useEffect(() => {\n console.log('[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик');\n // Перезагружаем чат для новой страницы\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] Ошибка при перезагрузке чата:', error);\n });\n // Перезагружаем черновик для новой страницы\n loadDraft();\n }, [currentPageKey, loadChat, loadDraft]);\n\n // Событийная синхронизация чата между вкладками и обработка результатов сохранения сообщений\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений', {\n pluginId,\n pageKey: currentPageKey,\n timestamp: new Date().toISOString()\n });\n\n const handleChatUpdate = (event: { type: string; pluginId: string; pageKey: string; messages?: ChatMessage[]; messageId?: string; response?: any; success?: boolean; error?: string; message?: any; timestamp?: number }) => {\n console.log('[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====', {\n type: event?.type,\n pluginId: event?.pluginId,\n pageKey: event?.pageKey,\n messageId: event?.messageId,\n hasResponse: !!event?.response,\n responseType: event?.response ? typeof event.response : 'none',\n timestamp: new Date().toISOString()\n });\n\n // Обработка обновлений чата от других компонентов/вкладок\n if (event?.type === 'PLUGIN_CHAT_UPDATED' && event.pluginId === pluginId && event.pageKey === currentPageKey) {\n console.log('[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные');\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:', {\n loading,\n messagesCount: messages.length,\n currentPageKey,\n pluginId,\n timestamp: new Date().toISOString()\n });\n setLoading(false); // Гарантированный сброс loading перед загрузкой чата\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat');\n // Запрашиваем актуальные данные чата асинхронно\n loadChat().catch((error) => {\n console.error('[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:', error);\n });\n }\n\n // NOTE: GET_PLUGIN_CHAT_RESPONSE больше не обрабатывается здесь,\n // поскольку ответы на GET_PLUGIN_CHAT теперь обрабатываются через Promise в loadChat()\n\n // Логируем все сообщения, которые приходят, но не обрабатываются\n if (event?.type !== 'PLUGIN_CHAT_UPDATED' && event?.type !== 'GET_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:', {\n type: event?.type,\n fullEvent: event,\n timestamp: new Date().toISOString()\n });\n }\n\n // Обработка результатов сохранения сообщений\n if (event?.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:', event);\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения', event.error);\n setError(`Ошибка сохранения сообщения: ${event.error}`);\n }\n }\n\n // Обработка результатов удаления чата\n if (event?.type === 'DELETE_PLUGIN_CHAT_RESPONSE') {\n console.log('[PluginControlPanel] handleChatUpdate - результат удаления чата:', event);\n console.log('[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:', {\n loading,\n messagesCount: messages.length,\n eventSuccess: event.success,\n timestamp: new Date().toISOString()\n });\n\n setLoading(false); // Останавливаем загрузку\n console.log('[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE');\n\n if (event.success) {\n console.log('[PluginControlPanel] handleChatUpdate: чат успешно удален');\n } else {\n console.error('[PluginControlPanel] handleChatUpdate: ошибка удаления чата', event.error);\n setError(`Ошибка удаления чата: ${event.error}`);\n }\n }\n\n // === PYODIDE MESSAGE HANDLER ===\n if (event?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', event.message);\n\n if (event.message?.content) {\n try {\n // Строгая проверка типа content для Pyodide сообщений\n let content = event.message.content;\n let messageTimestamp = event.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] PYODIDE content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] PYODIDE content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из PYODIDE content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: event.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:', pyodideError, event);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:', event.message);\n }\n }\n };\n\n // Слушатель для обработки результатов операций с чатом\n const handleChatOperationResult = (message: any) => {\n if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE') {\n console.log('[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения', message);\n\n if (message.success) {\n console.log('[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено');\n // Не нужно ничего делать дополнительно - обновление придет через PLUGIN_CHAT_UPDATED\n } else {\n console.error('[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения', message.error);\n setError(`Ошибка сохранения сообщения: ${message.error}`);\n }\n }\n };\n\n chrome.runtime.onMessage.addListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы');\n\n return () => {\n console.log('[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений');\n chrome.runtime.onMessage.removeListener(handleChatUpdate);\n chrome.runtime.onMessage.removeListener(handleChatOperationResult);\n };\n }, [pluginId, currentPageKey, sendMessageToBackground, loadChat]);\n\n // Восстановление черновика при возврате на вкладку 'Чат'\n useEffect(() => {\n if (currentView === 'chat') {\n loadDraft(); // Явно загружаем черновик при возврате на вкладку чата\n }\n }, [currentView, loadDraft]);\n\n // Логирование каждого рендера и ключевых параметров\n useEffect(() => {\n console.log('[PluginControlPanel] === РЕНДЕР ===', {\n pluginId,\n pageKey: currentPageKey,\n draftText,\n message,\n currentView,\n isRunning,\n isPaused,\n });\n });\n\n // Глобальный обработчик ошибок для ловли проблем с substring\n useEffect(() => {\n const originalConsoleError = console.error;\n console.error = (...args) => {\n // Перехватываем ошибки substring\n const errorMessage = args.join(' ');\n if (errorMessage.includes('substring') && errorMessage.includes('is not a function')) {\n console.error('[PluginControlPanel] 🔴 CRITICAL: substring error detected!');\n console.error('[PluginControlPanel] Error details:', args);\n console.error('[PluginControlPanel] Stack trace:', new Error().stack);\n // Не блокируем оригинальную обработку ошибки\n }\n originalConsoleError.apply(console, args);\n };\n\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring активирован');\n\n return () => {\n console.error = originalConsoleError;\n console.log('[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован');\n };\n }, []);\n\n // Слушатель для Pyodide сообщений через custom events\n useEffect(() => {\n console.log('[PluginControlPanel] Настройка слушателя для pyodide messages');\n\n const handlePyodideCustomEvent = (event: any) => {\n const data = event.detail;\n console.log('[PluginControlPanel] Получен Pyodide custom event:', data);\n\n if (data?.type === 'PYODIDE_MESSAGE_UPDATE') {\n console.log('[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:', data.message);\n\n if (data.message?.content) {\n try {\n // Строгая проверка типа content\n let content = data.message.content;\n let messageTimestamp = data.timestamp || Date.now();\n\n // Если content является объектом, конвертируем в строку\n if (typeof content === 'object') {\n console.warn('[PluginControlPanel] Pyodide content является объектом, конвертируем:', content);\n content = JSON.stringify(content);\n } else if (content === null || content === undefined) {\n console.warn('[PluginControlPanel] Pyodide content равен null/undefined');\n content = 'Пустое сообщение от Pyodide';\n } else {\n content = String(content);\n\n // Проверяем, является ли строка JSON с сообщением плагина\n try {\n const parsedContent = JSON.parse(content);\n if (typeof parsedContent === 'object' && parsedContent !== null && 'content' in parsedContent) {\n console.log('[PluginControlPanel] Распарсен JSON из Pyodide content:', parsedContent);\n content = String(parsedContent.content || '');\n // Используем timestamp из распарсенного объекта, если он есть\n if (parsedContent.timestamp && typeof parsedContent.timestamp === 'number') {\n messageTimestamp = parsedContent.timestamp;\n }\n }\n } catch (jsonParseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Pyodide content не является JSON, оставляем как есть');\n }\n }\n\n const pyodideMessage: ChatMessage = {\n id: data.message.id || `pyodide_${messageTimestamp}_${Math.random()}`,\n text: content,\n isUser: false, // Python сообщения отображаем как от бота\n timestamp: messageTimestamp,\n };\n\n console.log('[PluginControlPanel] Adding Pyodide message to chat:', pyodideMessage);\n\n setMessages(prev => [...prev, pyodideMessage]);\n console.log('[PluginControlPanel] Pyodide message added to chat');\n } catch (pyodideError) {\n console.error('[PluginControlPanel] Ошибка обработки Pyodide сообщения:', pyodideError, data);\n // Добавляем сообщение об ошибке вместо падения\n const errorMessage: ChatMessage = {\n id: `pyodide_error_${Date.now()}`,\n text: `[ОШИБКА PYODIDE: ${pyodideError instanceof Error ? pyodideError.message : String(pyodideError)}]`,\n isUser: false,\n timestamp: Date.now(),\n };\n setMessages(prev => [...prev, errorMessage]);\n }\n } else {\n console.warn('[PluginControlPanel] Pyodide сообщение без content:', data.message);\n }\n }\n };\n\n window.addEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован');\n\n return () => {\n window.removeEventListener('PYODIDE_MESSAGE_UPDATE', handlePyodideCustomEvent);\n console.log('[PluginControlPanel] Слушатель для Pyodide custom events удален');\n };\n }, []);\n\n // --- Синхронизация message с draftText после загрузки черновика ---\n useEffect(() => {\n if (typeof draftText === 'string') {\n setMessage(draftText);\n console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText);\n }\n }, [draftText, setMessage]);\n\n const handleSendMessage = (): void => {\n console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message });\n if (!message.trim()) return;\n\n const newMessage: ChatMessage = {\n id: Date.now().toString(),\n text: message.trim(),\n isUser: true,\n timestamp: Date.now(),\n };\n\n // Очищаем сообщение через хук\n setMessage('');\n setError(null); // Сбрасываем предыдущие ошибки\n\n console.log('[PluginControlPanel] handleSendMessage: отправка сообщения в background');\n\n sendMessageToBackground({\n type: 'SAVE_PLUGIN_CHAT_MESSAGE',\n pluginId,\n pageKey: currentPageKey,\n message: {\n role: 'user',\n content: newMessage.text,\n timestamp: newMessage.timestamp,\n },\n });\n\n // Очищаем черновик сразу после отправки\n clearDraft();\n };\n\n // Обработка изменения размера разделителя\n useEffect(() => {\n const handleResizeMove = (event: MouseEvent): void => {\n if (!isResizing) return;\n\n const container = document.querySelector('.chat-view') as HTMLElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n const newHeight = containerRect.bottom - event.clientY;\n const minHeight = 100; // Минимальная высота чата\n const maxHeight = containerRect.height - 80; // Максимальная высота чата\n\n if (newHeight >= minHeight && newHeight <= maxHeight) {\n setInputHeight(containerRect.height - newHeight);\n }\n };\n\n const handleResizeEnd = (): void => {\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n if (isResizing) {\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n }\n\n return () => {\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isResizing]);\n\n // Автоскролл к последнему сообщению\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n useEffect(() => {\n console.log('[PluginControlPanel] useEffect[messages] - состояние messages обновлено:', {\n messagesCount: messages.length,\n firstMessage: messages[0] ? {\n id: messages[0].id,\n text: typeof messages[0].text === 'string' ? messages[0].text.substring(0, 50) : String(messages[0].text || '').substring(0, 50),\n isUser: messages[0].isUser,\n timestamp: messages[0].timestamp,\n textType: typeof messages[0].text\n } : null,\n // Безопасная обработка всех сообщений с проверкой типов\n allMessages: messages.map(m => {\n try {\n const textPreview = typeof m.text === 'string' ? m.text.substring(0, 30) : String(m.text || '').substring(0, 30);\n return { id: m.id, text: textPreview, isUser: m.isUser, textType: typeof m.text };\n } catch (msgError) {\n console.warn('[PluginControlPanel] Error in message logging:', msgError, m);\n return { id: m.id, text: '[LOGGING ERROR]', isUser: m.isUser, textType: typeof m.text };\n }\n }),\n timestamp: new Date().toISOString()\n });\n }, [messages]);\n\n // Фокус на поле ввода при открытии чата\n useEffect(() => {\n if (currentView === 'chat') {\n setTimeout(() => textareaRef.current?.focus(), 100);\n }\n }, [currentView]);\n\n const handleTextareaChange = (event: React.ChangeEvent): void => {\n setMessage(event.target.value); // Используем хук вместо setMessage\n // Автоматическое изменение высоты\n const textarea = event.target;\n textarea.style.height = 'auto';\n const newHeight = Math.min(Math.max(textarea.scrollHeight, 60), 200); // Минимум 60px, максимум 200px\n textarea.style.height = `${newHeight}px`;\n setInputHeight(newHeight);\n };\n\n const handleKeyPress = (event: React.KeyboardEvent): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n // Очистка чата (удаление всей истории)\n const handleClearChat = (): void => {\n setLoading(true);\n setError(null);\n\n sendMessageToBackground({\n type: 'DELETE_PLUGIN_CHAT',\n pluginId,\n pageKey: currentPageKey,\n });\n\n // Очищаем локальное состояние сразу\n setMessages([]);\n clearDraft(); // Очищаем черновик\n };\n\n // Экспорт чата в JSON\n const handleExportChat = (): void => {\n const data = JSON.stringify(messages, null, 2);\n const blob = new Blob([data], { type: 'application/json' });\n saveAs(blob, `plugin-chat-${pluginId}.json`);\n };\n\n return (\n
\n
\n
\n {\n const firstChar = typeof pluginName === 'string' && pluginName.length > 0 ? pluginName.charAt(0) : 'P';\n (event.currentTarget as HTMLImageElement).src =\n `data:image/svg+xml;utf8,${firstChar}`;\n }}\n />\n

{pluginName}

\n
\n
\n \n {isRunning ? '⏹️' : '▶️'}\n \n \n ⏸️\n \n \n ⏹️\n \n \n ✕\n \n
\n
\n
\n setActiveTab('chat')}\n >\n Чат\n \n setActiveTab('details')}\n >\n Детали\n \n
\n
\n {activeTab === 'chat' && (\n
\n
\n

Чат

\n
\n \n \n \n 🧪 Тест\n \n
\n
\n
\n {loading &&
Загрузка сообщений...
}\n {error &&
{error}
}\n {!loading && !error && messages.length === 0 && (\n
\n

Нет сообщений

\n

Напишите первое сообщение!

\n
\n )}\n {/* Отображение сообщений чата */}\n
\n {messages.map((msg, idx) => {\n console.log('[PluginControlPanel] render message:', idx, msg);\n\n // Парсинг JSON в тексте сообщения перед рендерингом\n let displayText = msg.text;\n let displayTimestamp = msg.timestamp;\n\n console.log('[PluginControlPanel] Raw message text before parsing for message', idx, ':', msg.text);\n\n try {\n const parsed = JSON.parse(displayText);\n if (typeof parsed === 'object' && parsed !== null && 'content' in parsed) {\n console.log('[PluginControlPanel] Парсинг JSON в рендере:', parsed);\n let content = parsed.content;\n if (typeof content === 'object') {\n displayText = JSON.stringify(content);\n } else {\n displayText = String(content || '');\n }\n if (parsed.timestamp && typeof parsed.timestamp === 'number') {\n displayTimestamp = parsed.timestamp;\n }\n } else if (typeof parsed === 'string') {\n // Если JSON содержит просто строку\n displayText = parsed;\n } else if (typeof parsed === 'object' && parsed !== null) {\n // Если JSON содержит объект без поля content, берем первое строковое поле\n const stringFields = Object.values(parsed).filter(val => typeof val === 'string');\n if (stringFields.length > 0) {\n displayText = String(stringFields[0]);\n } else {\n displayText = JSON.stringify(parsed);\n }\n }\n } catch (parseError) {\n // Не JSON, оставляем как есть\n console.log('[PluginControlPanel] Текст не является JSON, рендерим как есть');\n }\n\n console.log('[PluginControlPanel] Display text after parsing for message', idx, ':', displayText);\n\n return (\n \n
\n {displayText}\n \n {new Date(displayTimestamp).toLocaleTimeString()}\n \n
\n
\n );\n })}\n
\n
\n
\n
\n \n \n 📤\n \n \n
\n
\n )}\n {activeTab === 'details' && (\n \n )}\n
\n
\n );\n};","import React, { useState, useEffect, useCallback } from 'react';\nimport './ToastNotifications.css';\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n message: string;\n type: ToastType;\n duration: number;\n timestamp: number;\n}\n\ninterface ToastNotificationsProps {\n toasts: Toast[];\n onRemove: (id: string) => void;\n}\n\nexport const ToastNotifications: React.FC = ({ toasts, onRemove }) => {\n return (\n
\n {toasts.map((toast) => (\n \n ))}\n
\n );\n};\n\ninterface ToastItemProps {\n toast: Toast;\n onRemove: (id: string) => void;\n}\n\nconst ToastItem: React.FC = ({ toast, onRemove }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [isHiding, setIsHiding] = useState(false);\n\n useEffect(() => {\n // Animation in\n requestAnimationFrame(() => {\n setIsVisible(true);\n });\n\n // Auto remove\n if (toast.duration > 0) {\n const timer = setTimeout(() => {\n handleRemove();\n }, toast.duration);\n\n return () => clearTimeout(timer);\n }\n\n return undefined; // Explicitly return undefined when no timer is set\n }, [toast.duration]);\n\n const handleRemove = useCallback(() => {\n setIsHiding(true);\n setTimeout(() => {\n onRemove(toast.id);\n }, 300); // Animation duration\n }, [toast.id, onRemove]);\n\n return (\n
\n
\n {toast.message}\n \n
\n
\n );\n};\n\n// Toast manager hook\nexport function useToastManager() {\n const [toasts, setToasts] = useState([]);\n\n const addToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {\n const id = `toast-${Date.now()}-${Math.random()}`;\n const newToast: Toast = {\n id,\n message,\n type,\n duration,\n timestamp: Date.now()\n };\n\n setToasts(prev => {\n const updated = [...prev, newToast];\n // Limit to 3 toasts\n return updated.slice(-3);\n });\n\n return id;\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts(prev => prev.filter(toast => toast.id !== id));\n }, []);\n\n const clearAll = useCallback(() => {\n setToasts([]);\n }, []);\n\n return {\n toasts,\n addToast,\n removeToast,\n clearAll\n };\n}\n\n// Convenience functions\nexport const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {\n // This will be used with the toast manager\n console.log(`[Toast] ${type}: ${message}`);\n};\n\nexport const showSuccessToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'success', duration);\n};\n\nexport const showErrorToast = (message: string, duration: number = 5000) => {\n return showToast(message, 'error', duration);\n};\n\nexport const showWarningToast = (message: string, duration: number = 4000) => {\n return showToast(message, 'warning', duration);\n};\n\nexport const showInfoToast = (message: string, duration: number = 3000) => {\n return showToast(message, 'info', duration);\n}; ","import React from 'react';\n\ninterface ThemeSwitcherProps {\n theme: 'light' | 'dark' | 'system';\n isLight: boolean;\n onToggle: () => void;\n isInSidebar?: boolean; // Контекст использования: sidebar или options\n}\n\nconst ThemeSwitcher: React.FC = ({ theme, isLight, onToggle, isInSidebar = false }) => {\n const getIcon = () => {\n switch (theme) {\n case 'light':\n return '🌙'; // Moon - to switch to dark\n case 'dark':\n return '💻'; // System icon - to switch to system\n case 'system':\n return '☀️'; // Sun - to switch to light\n default:\n return '🌙';\n }\n };\n\n const getTitle = () => {\n switch (theme) {\n case 'light':\n return 'Переключить на темную тему';\n case 'dark':\n return 'Переключить на системную тему';\n case 'system':\n return 'Переключить на светлую тему';\n default:\n return 'Переключить тему';\n }\n };\n\n const buttonStyle: React.CSSProperties = {\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: '50%',\n width: '40px',\n height: '40px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '20px',\n // marginTop только для Options (не в sidebar)\n ...(isInSidebar ? {} : { marginTop: '20px' })\n };\n\n return (\n \n );\n};\n\nexport default ThemeSwitcher;","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t {\n const classMap = createClassMap(config);\n const {\n conflictingClassGroups,\n conflictingClassGroupModifiers\n } = config;\n const getClassGroupId = className => {\n const classParts = className.split(CLASS_PART_SEPARATOR);\n // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.\n if (classParts[0] === '' && classParts.length !== 1) {\n classParts.shift();\n }\n return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);\n };\n const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {\n const conflicts = conflictingClassGroups[classGroupId] || [];\n if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {\n return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];\n }\n return conflicts;\n };\n return {\n getClassGroupId,\n getConflictingClassGroupIds\n };\n};\nconst getGroupRecursive = (classParts, classPartObject) => {\n if (classParts.length === 0) {\n return classPartObject.classGroupId;\n }\n const currentClassPart = classParts[0];\n const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);\n const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;\n if (classGroupFromNextClassPart) {\n return classGroupFromNextClassPart;\n }\n if (classPartObject.validators.length === 0) {\n return undefined;\n }\n const classRest = classParts.join(CLASS_PART_SEPARATOR);\n return classPartObject.validators.find(({\n validator\n }) => validator(classRest))?.classGroupId;\n};\nconst arbitraryPropertyRegex = /^\\[(.+)\\]$/;\nconst getGroupIdForArbitraryProperty = className => {\n if (arbitraryPropertyRegex.test(className)) {\n const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];\n const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(':'));\n if (property) {\n // I use two dots here because one dot is used as prefix for class groups in plugins\n return 'arbitrary..' + property;\n }\n }\n};\n/**\n * Exported for testing only\n */\nconst createClassMap = config => {\n const {\n theme,\n classGroups\n } = config;\n const classMap = {\n nextPart: new Map(),\n validators: []\n };\n for (const classGroupId in classGroups) {\n processClassesRecursively(classGroups[classGroupId], classMap, classGroupId, theme);\n }\n return classMap;\n};\nconst processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {\n classGroup.forEach(classDefinition => {\n if (typeof classDefinition === 'string') {\n const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);\n classPartObjectToEdit.classGroupId = classGroupId;\n return;\n }\n if (typeof classDefinition === 'function') {\n if (isThemeGetter(classDefinition)) {\n processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);\n return;\n }\n classPartObject.validators.push({\n validator: classDefinition,\n classGroupId\n });\n return;\n }\n Object.entries(classDefinition).forEach(([key, classGroup]) => {\n processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);\n });\n });\n};\nconst getPart = (classPartObject, path) => {\n let currentClassPartObject = classPartObject;\n path.split(CLASS_PART_SEPARATOR).forEach(pathPart => {\n if (!currentClassPartObject.nextPart.has(pathPart)) {\n currentClassPartObject.nextPart.set(pathPart, {\n nextPart: new Map(),\n validators: []\n });\n }\n currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);\n });\n return currentClassPartObject;\n};\nconst isThemeGetter = func => func.isThemeGetter;\n\n// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance\nconst createLruCache = maxCacheSize => {\n if (maxCacheSize < 1) {\n return {\n get: () => undefined,\n set: () => {}\n };\n }\n let cacheSize = 0;\n let cache = new Map();\n let previousCache = new Map();\n const update = (key, value) => {\n cache.set(key, value);\n cacheSize++;\n if (cacheSize > maxCacheSize) {\n cacheSize = 0;\n previousCache = cache;\n cache = new Map();\n }\n };\n return {\n get(key) {\n let value = cache.get(key);\n if (value !== undefined) {\n return value;\n }\n if ((value = previousCache.get(key)) !== undefined) {\n update(key, value);\n return value;\n }\n },\n set(key, value) {\n if (cache.has(key)) {\n cache.set(key, value);\n } else {\n update(key, value);\n }\n }\n };\n};\nconst IMPORTANT_MODIFIER = '!';\nconst MODIFIER_SEPARATOR = ':';\nconst MODIFIER_SEPARATOR_LENGTH = MODIFIER_SEPARATOR.length;\nconst createParseClassName = config => {\n const {\n prefix,\n experimentalParseClassName\n } = config;\n /**\n * Parse class name into parts.\n *\n * Inspired by `splitAtTopLevelOnly` used in Tailwind CSS\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js\n */\n let parseClassName = className => {\n const modifiers = [];\n let bracketDepth = 0;\n let parenDepth = 0;\n let modifierStart = 0;\n let postfixModifierPosition;\n for (let index = 0; index < className.length; index++) {\n let currentCharacter = className[index];\n if (bracketDepth === 0 && parenDepth === 0) {\n if (currentCharacter === MODIFIER_SEPARATOR) {\n modifiers.push(className.slice(modifierStart, index));\n modifierStart = index + MODIFIER_SEPARATOR_LENGTH;\n continue;\n }\n if (currentCharacter === '/') {\n postfixModifierPosition = index;\n continue;\n }\n }\n if (currentCharacter === '[') {\n bracketDepth++;\n } else if (currentCharacter === ']') {\n bracketDepth--;\n } else if (currentCharacter === '(') {\n parenDepth++;\n } else if (currentCharacter === ')') {\n parenDepth--;\n }\n }\n const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);\n const baseClassName = stripImportantModifier(baseClassNameWithImportantModifier);\n const hasImportantModifier = baseClassName !== baseClassNameWithImportantModifier;\n const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;\n return {\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n };\n };\n if (prefix) {\n const fullPrefix = prefix + MODIFIER_SEPARATOR;\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.substring(fullPrefix.length)) : {\n isExternal: true,\n modifiers: [],\n hasImportantModifier: false,\n baseClassName: className,\n maybePostfixModifierPosition: undefined\n };\n }\n if (experimentalParseClassName) {\n const parseClassNameOriginal = parseClassName;\n parseClassName = className => experimentalParseClassName({\n className,\n parseClassName: parseClassNameOriginal\n });\n }\n return parseClassName;\n};\nconst stripImportantModifier = baseClassName => {\n if (baseClassName.endsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(0, baseClassName.length - 1);\n }\n /**\n * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.\n * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864\n */\n if (baseClassName.startsWith(IMPORTANT_MODIFIER)) {\n return baseClassName.substring(1);\n }\n return baseClassName;\n};\n\n/**\n * Sorts modifiers according to following schema:\n * - Predefined modifiers are sorted alphabetically\n * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it\n */\nconst createSortModifiers = config => {\n const orderSensitiveModifiers = Object.fromEntries(config.orderSensitiveModifiers.map(modifier => [modifier, true]));\n const sortModifiers = modifiers => {\n if (modifiers.length <= 1) {\n return modifiers;\n }\n const sortedModifiers = [];\n let unsortedModifiers = [];\n modifiers.forEach(modifier => {\n const isPositionSensitive = modifier[0] === '[' || orderSensitiveModifiers[modifier];\n if (isPositionSensitive) {\n sortedModifiers.push(...unsortedModifiers.sort(), modifier);\n unsortedModifiers = [];\n } else {\n unsortedModifiers.push(modifier);\n }\n });\n sortedModifiers.push(...unsortedModifiers.sort());\n return sortedModifiers;\n };\n return sortModifiers;\n};\nconst createConfigUtils = config => ({\n cache: createLruCache(config.cacheSize),\n parseClassName: createParseClassName(config),\n sortModifiers: createSortModifiers(config),\n ...createClassGroupUtils(config)\n});\nconst SPLIT_CLASSES_REGEX = /\\s+/;\nconst mergeClassList = (classList, configUtils) => {\n const {\n parseClassName,\n getClassGroupId,\n getConflictingClassGroupIds,\n sortModifiers\n } = configUtils;\n /**\n * Set of classGroupIds in following format:\n * `{importantModifier}{variantModifiers}{classGroupId}`\n * @example 'float'\n * @example 'hover:focus:bg-color'\n * @example 'md:!pr'\n */\n const classGroupsInConflict = [];\n const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);\n let result = '';\n for (let index = classNames.length - 1; index >= 0; index -= 1) {\n const originalClassName = classNames[index];\n const {\n isExternal,\n modifiers,\n hasImportantModifier,\n baseClassName,\n maybePostfixModifierPosition\n } = parseClassName(originalClassName);\n if (isExternal) {\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n let hasPostfixModifier = !!maybePostfixModifierPosition;\n let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);\n if (!classGroupId) {\n if (!hasPostfixModifier) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n classGroupId = getClassGroupId(baseClassName);\n if (!classGroupId) {\n // Not a Tailwind class\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n continue;\n }\n hasPostfixModifier = false;\n }\n const variantModifier = sortModifiers(modifiers).join(':');\n const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;\n const classId = modifierId + classGroupId;\n if (classGroupsInConflict.includes(classId)) {\n // Tailwind class omitted due to conflict\n continue;\n }\n classGroupsInConflict.push(classId);\n const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);\n for (let i = 0; i < conflictGroups.length; ++i) {\n const group = conflictGroups[i];\n classGroupsInConflict.push(modifierId + group);\n }\n // Tailwind class not in conflict\n result = originalClassName + (result.length > 0 ? ' ' + result : result);\n }\n return result;\n};\n\n/**\n * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.\n *\n * Specifically:\n * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js\n * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts\n *\n * Original code has MIT license: Copyright (c) Luke Edwards (lukeed.com)\n */\nfunction twJoin() {\n let index = 0;\n let argument;\n let resolvedValue;\n let string = '';\n while (index < arguments.length) {\n if (argument = arguments[index++]) {\n if (resolvedValue = toValue(argument)) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n}\nconst toValue = mix => {\n if (typeof mix === 'string') {\n return mix;\n }\n let resolvedValue;\n let string = '';\n for (let k = 0; k < mix.length; k++) {\n if (mix[k]) {\n if (resolvedValue = toValue(mix[k])) {\n string && (string += ' ');\n string += resolvedValue;\n }\n }\n }\n return string;\n};\nfunction createTailwindMerge(createConfigFirst, ...createConfigRest) {\n let configUtils;\n let cacheGet;\n let cacheSet;\n let functionToCall = initTailwindMerge;\n function initTailwindMerge(classList) {\n const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());\n configUtils = createConfigUtils(config);\n cacheGet = configUtils.cache.get;\n cacheSet = configUtils.cache.set;\n functionToCall = tailwindMerge;\n return tailwindMerge(classList);\n }\n function tailwindMerge(classList) {\n const cachedResult = cacheGet(classList);\n if (cachedResult) {\n return cachedResult;\n }\n const result = mergeClassList(classList, configUtils);\n cacheSet(classList, result);\n return result;\n }\n return function callTailwindMerge() {\n return functionToCall(twJoin.apply(null, arguments));\n };\n}\nconst fromTheme = key => {\n const themeGetter = theme => theme[key] || [];\n themeGetter.isThemeGetter = true;\n return themeGetter;\n};\nconst arbitraryValueRegex = /^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i;\nconst arbitraryVariableRegex = /^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i;\nconst fractionRegex = /^\\d+\\/\\d+$/;\nconst tshirtUnitRegex = /^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/;\nconst lengthUnitRegex = /\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/;\nconst colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/;\n// Shadow always begins with x and y offset separated by underscore optionally prepended by inset\nconst shadowRegex = /^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/;\nconst imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/;\nconst isFraction = value => fractionRegex.test(value);\nconst isNumber = value => !!value && !Number.isNaN(Number(value));\nconst isInteger = value => !!value && Number.isInteger(Number(value));\nconst isPercent = value => value.endsWith('%') && isNumber(value.slice(0, -1));\nconst isTshirtSize = value => tshirtUnitRegex.test(value);\nconst isAny = () => true;\nconst isLengthOnly = value =>\n// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.\n// For example, `hsl(0 0% 0%)` would be classified as a length without this check.\n// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.\nlengthUnitRegex.test(value) && !colorFunctionRegex.test(value);\nconst isNever = () => false;\nconst isShadow = value => shadowRegex.test(value);\nconst isImage = value => imageRegex.test(value);\nconst isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);\nconst isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);\nconst isArbitraryValue = value => arbitraryValueRegex.test(value);\nconst isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);\nconst isArbitraryNumber = value => getIsArbitraryValue(value, isLabelNumber, isNumber);\nconst isArbitraryPosition = value => getIsArbitraryValue(value, isLabelPosition, isNever);\nconst isArbitraryImage = value => getIsArbitraryValue(value, isLabelImage, isImage);\nconst isArbitraryShadow = value => getIsArbitraryValue(value, isLabelShadow, isShadow);\nconst isArbitraryVariable = value => arbitraryVariableRegex.test(value);\nconst isArbitraryVariableLength = value => getIsArbitraryVariable(value, isLabelLength);\nconst isArbitraryVariableFamilyName = value => getIsArbitraryVariable(value, isLabelFamilyName);\nconst isArbitraryVariablePosition = value => getIsArbitraryVariable(value, isLabelPosition);\nconst isArbitraryVariableSize = value => getIsArbitraryVariable(value, isLabelSize);\nconst isArbitraryVariableImage = value => getIsArbitraryVariable(value, isLabelImage);\nconst isArbitraryVariableShadow = value => getIsArbitraryVariable(value, isLabelShadow, true);\n// Helpers\nconst getIsArbitraryValue = (value, testLabel, testValue) => {\n const result = arbitraryValueRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return testValue(result[2]);\n }\n return false;\n};\nconst getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {\n const result = arbitraryVariableRegex.exec(value);\n if (result) {\n if (result[1]) {\n return testLabel(result[1]);\n }\n return shouldMatchNoLabel;\n }\n return false;\n};\n// Labels\nconst isLabelPosition = label => label === 'position' || label === 'percentage';\nconst isLabelImage = label => label === 'image' || label === 'url';\nconst isLabelSize = label => label === 'length' || label === 'size' || label === 'bg-size';\nconst isLabelLength = label => label === 'length';\nconst isLabelNumber = label => label === 'number';\nconst isLabelFamilyName = label => label === 'family-name';\nconst isLabelShadow = label => label === 'shadow';\nconst validators = /*#__PURE__*/Object.defineProperty({\n __proto__: null,\n isAny,\n isAnyNonArbitrary,\n isArbitraryImage,\n isArbitraryLength,\n isArbitraryNumber,\n isArbitraryPosition,\n isArbitraryShadow,\n isArbitrarySize,\n isArbitraryValue,\n isArbitraryVariable,\n isArbitraryVariableFamilyName,\n isArbitraryVariableImage,\n isArbitraryVariableLength,\n isArbitraryVariablePosition,\n isArbitraryVariableShadow,\n isArbitraryVariableSize,\n isFraction,\n isInteger,\n isNumber,\n isPercent,\n isTshirtSize\n}, Symbol.toStringTag, {\n value: 'Module'\n});\nconst getDefaultConfig = () => {\n /**\n * Theme getters for theme variable namespaces\n * @see https://tailwindcss.com/docs/theme#theme-variable-namespaces\n */\n /***/\n const themeColor = fromTheme('color');\n const themeFont = fromTheme('font');\n const themeText = fromTheme('text');\n const themeFontWeight = fromTheme('font-weight');\n const themeTracking = fromTheme('tracking');\n const themeLeading = fromTheme('leading');\n const themeBreakpoint = fromTheme('breakpoint');\n const themeContainer = fromTheme('container');\n const themeSpacing = fromTheme('spacing');\n const themeRadius = fromTheme('radius');\n const themeShadow = fromTheme('shadow');\n const themeInsetShadow = fromTheme('inset-shadow');\n const themeTextShadow = fromTheme('text-shadow');\n const themeDropShadow = fromTheme('drop-shadow');\n const themeBlur = fromTheme('blur');\n const themePerspective = fromTheme('perspective');\n const themeAspect = fromTheme('aspect');\n const themeEase = fromTheme('ease');\n const themeAnimate = fromTheme('animate');\n /**\n * Helpers to avoid repeating the same scales\n *\n * We use functions that create a new array every time they're called instead of static arrays.\n * This ensures that users who modify any scale by mutating the array (e.g. with `array.push(element)`) don't accidentally mutate arrays in other parts of the config.\n */\n /***/\n const scaleBreak = () => ['auto', 'avoid', 'all', 'avoid-page', 'page', 'left', 'right', 'column'];\n const scalePosition = () => ['center', 'top', 'bottom', 'left', 'right', 'top-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-top', 'top-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-top', 'bottom-right',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'right-bottom', 'bottom-left',\n // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378\n 'left-bottom'];\n const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];\n const scaleOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'];\n const scaleOverscroll = () => ['auto', 'contain', 'none'];\n const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];\n const scaleInset = () => [isFraction, 'full', 'auto', ...scaleUnambiguousSpacing()];\n const scaleGridTemplateColsRows = () => [isInteger, 'none', 'subgrid', isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartAndEnd = () => ['auto', {\n span: ['full', isInteger, isArbitraryVariable, isArbitraryValue]\n }, isInteger, isArbitraryVariable, isArbitraryValue];\n const scaleGridColRowStartOrEnd = () => [isInteger, 'auto', isArbitraryVariable, isArbitraryValue];\n const scaleGridAutoColsRows = () => ['auto', 'min', 'max', 'fr', isArbitraryVariable, isArbitraryValue];\n const scaleAlignPrimaryAxis = () => ['start', 'end', 'center', 'between', 'around', 'evenly', 'stretch', 'baseline', 'center-safe', 'end-safe'];\n const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];\n const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];\n const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];\n const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];\n const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {\n position: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleBgRepeat = () => ['no-repeat', {\n repeat: ['', 'x', 'y', 'space', 'round']\n }];\n const scaleBgSize = () => ['auto', 'cover', 'contain', isArbitraryVariableSize, isArbitrarySize, {\n size: [isArbitraryVariable, isArbitraryValue]\n }];\n const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];\n const scaleRadius = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', 'full', themeRadius, isArbitraryVariable, isArbitraryValue];\n const scaleBorderWidth = () => ['', isNumber, isArbitraryVariableLength, isArbitraryLength];\n const scaleLineStyle = () => ['solid', 'dashed', 'dotted', 'double'];\n const scaleBlendMode = () => ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];\n const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];\n const scaleBlur = () => [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeBlur, isArbitraryVariable, isArbitraryValue];\n const scaleRotate = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleScale = () => ['none', isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];\n const scaleTranslate = () => [isFraction, 'full', ...scaleUnambiguousSpacing()];\n return {\n cacheSize: 500,\n theme: {\n animate: ['spin', 'ping', 'pulse', 'bounce'],\n aspect: ['video'],\n blur: [isTshirtSize],\n breakpoint: [isTshirtSize],\n color: [isAny],\n container: [isTshirtSize],\n 'drop-shadow': [isTshirtSize],\n ease: ['in', 'out', 'in-out'],\n font: [isAnyNonArbitrary],\n 'font-weight': ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black'],\n 'inset-shadow': [isTshirtSize],\n leading: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'],\n perspective: ['dramatic', 'near', 'normal', 'midrange', 'distant', 'none'],\n radius: [isTshirtSize],\n shadow: [isTshirtSize],\n spacing: ['px', isNumber],\n text: [isTshirtSize],\n 'text-shadow': [isTshirtSize],\n tracking: ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest']\n },\n classGroups: {\n // --------------\n // --- Layout ---\n // --------------\n /**\n * Aspect Ratio\n * @see https://tailwindcss.com/docs/aspect-ratio\n */\n aspect: [{\n aspect: ['auto', 'square', isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]\n }],\n /**\n * Container\n * @see https://tailwindcss.com/docs/container\n * @deprecated since Tailwind CSS v4.0.0\n */\n container: ['container'],\n /**\n * Columns\n * @see https://tailwindcss.com/docs/columns\n */\n columns: [{\n columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]\n }],\n /**\n * Break After\n * @see https://tailwindcss.com/docs/break-after\n */\n 'break-after': [{\n 'break-after': scaleBreak()\n }],\n /**\n * Break Before\n * @see https://tailwindcss.com/docs/break-before\n */\n 'break-before': [{\n 'break-before': scaleBreak()\n }],\n /**\n * Break Inside\n * @see https://tailwindcss.com/docs/break-inside\n */\n 'break-inside': [{\n 'break-inside': ['auto', 'avoid', 'avoid-page', 'avoid-column']\n }],\n /**\n * Box Decoration Break\n * @see https://tailwindcss.com/docs/box-decoration-break\n */\n 'box-decoration': [{\n 'box-decoration': ['slice', 'clone']\n }],\n /**\n * Box Sizing\n * @see https://tailwindcss.com/docs/box-sizing\n */\n box: [{\n box: ['border', 'content']\n }],\n /**\n * Display\n * @see https://tailwindcss.com/docs/display\n */\n display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],\n /**\n * Screen Reader Only\n * @see https://tailwindcss.com/docs/display#screen-reader-only\n */\n sr: ['sr-only', 'not-sr-only'],\n /**\n * Floats\n * @see https://tailwindcss.com/docs/float\n */\n float: [{\n float: ['right', 'left', 'none', 'start', 'end']\n }],\n /**\n * Clear\n * @see https://tailwindcss.com/docs/clear\n */\n clear: [{\n clear: ['left', 'right', 'both', 'none', 'start', 'end']\n }],\n /**\n * Isolation\n * @see https://tailwindcss.com/docs/isolation\n */\n isolation: ['isolate', 'isolation-auto'],\n /**\n * Object Fit\n * @see https://tailwindcss.com/docs/object-fit\n */\n 'object-fit': [{\n object: ['contain', 'cover', 'fill', 'none', 'scale-down']\n }],\n /**\n * Object Position\n * @see https://tailwindcss.com/docs/object-position\n */\n 'object-position': [{\n object: scalePositionWithArbitrary()\n }],\n /**\n * Overflow\n * @see https://tailwindcss.com/docs/overflow\n */\n overflow: [{\n overflow: scaleOverflow()\n }],\n /**\n * Overflow X\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-x': [{\n 'overflow-x': scaleOverflow()\n }],\n /**\n * Overflow Y\n * @see https://tailwindcss.com/docs/overflow\n */\n 'overflow-y': [{\n 'overflow-y': scaleOverflow()\n }],\n /**\n * Overscroll Behavior\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n overscroll: [{\n overscroll: scaleOverscroll()\n }],\n /**\n * Overscroll Behavior X\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-x': [{\n 'overscroll-x': scaleOverscroll()\n }],\n /**\n * Overscroll Behavior Y\n * @see https://tailwindcss.com/docs/overscroll-behavior\n */\n 'overscroll-y': [{\n 'overscroll-y': scaleOverscroll()\n }],\n /**\n * Position\n * @see https://tailwindcss.com/docs/position\n */\n position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],\n /**\n * Top / Right / Bottom / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n inset: [{\n inset: scaleInset()\n }],\n /**\n * Right / Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-x': [{\n 'inset-x': scaleInset()\n }],\n /**\n * Top / Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n 'inset-y': [{\n 'inset-y': scaleInset()\n }],\n /**\n * Start\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n start: [{\n start: scaleInset()\n }],\n /**\n * End\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n end: [{\n end: scaleInset()\n }],\n /**\n * Top\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n top: [{\n top: scaleInset()\n }],\n /**\n * Right\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n right: [{\n right: scaleInset()\n }],\n /**\n * Bottom\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n bottom: [{\n bottom: scaleInset()\n }],\n /**\n * Left\n * @see https://tailwindcss.com/docs/top-right-bottom-left\n */\n left: [{\n left: scaleInset()\n }],\n /**\n * Visibility\n * @see https://tailwindcss.com/docs/visibility\n */\n visibility: ['visible', 'invisible', 'collapse'],\n /**\n * Z-Index\n * @see https://tailwindcss.com/docs/z-index\n */\n z: [{\n z: [isInteger, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------------\n // --- Flexbox and Grid ---\n // ------------------------\n /**\n * Flex Basis\n * @see https://tailwindcss.com/docs/flex-basis\n */\n basis: [{\n basis: [isFraction, 'full', 'auto', themeContainer, ...scaleUnambiguousSpacing()]\n }],\n /**\n * Flex Direction\n * @see https://tailwindcss.com/docs/flex-direction\n */\n 'flex-direction': [{\n flex: ['row', 'row-reverse', 'col', 'col-reverse']\n }],\n /**\n * Flex Wrap\n * @see https://tailwindcss.com/docs/flex-wrap\n */\n 'flex-wrap': [{\n flex: ['nowrap', 'wrap', 'wrap-reverse']\n }],\n /**\n * Flex\n * @see https://tailwindcss.com/docs/flex\n */\n flex: [{\n flex: [isNumber, isFraction, 'auto', 'initial', 'none', isArbitraryValue]\n }],\n /**\n * Flex Grow\n * @see https://tailwindcss.com/docs/flex-grow\n */\n grow: [{\n grow: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Flex Shrink\n * @see https://tailwindcss.com/docs/flex-shrink\n */\n shrink: [{\n shrink: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Order\n * @see https://tailwindcss.com/docs/order\n */\n order: [{\n order: [isInteger, 'first', 'last', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Grid Template Columns\n * @see https://tailwindcss.com/docs/grid-template-columns\n */\n 'grid-cols': [{\n 'grid-cols': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Column Start / End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start-end': [{\n col: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Column Start\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-start': [{\n 'col-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Column End\n * @see https://tailwindcss.com/docs/grid-column\n */\n 'col-end': [{\n 'col-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Template Rows\n * @see https://tailwindcss.com/docs/grid-template-rows\n */\n 'grid-rows': [{\n 'grid-rows': scaleGridTemplateColsRows()\n }],\n /**\n * Grid Row Start / End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start-end': [{\n row: scaleGridColRowStartAndEnd()\n }],\n /**\n * Grid Row Start\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-start': [{\n 'row-start': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Row End\n * @see https://tailwindcss.com/docs/grid-row\n */\n 'row-end': [{\n 'row-end': scaleGridColRowStartOrEnd()\n }],\n /**\n * Grid Auto Flow\n * @see https://tailwindcss.com/docs/grid-auto-flow\n */\n 'grid-flow': [{\n 'grid-flow': ['row', 'col', 'dense', 'row-dense', 'col-dense']\n }],\n /**\n * Grid Auto Columns\n * @see https://tailwindcss.com/docs/grid-auto-columns\n */\n 'auto-cols': [{\n 'auto-cols': scaleGridAutoColsRows()\n }],\n /**\n * Grid Auto Rows\n * @see https://tailwindcss.com/docs/grid-auto-rows\n */\n 'auto-rows': [{\n 'auto-rows': scaleGridAutoColsRows()\n }],\n /**\n * Gap\n * @see https://tailwindcss.com/docs/gap\n */\n gap: [{\n gap: scaleUnambiguousSpacing()\n }],\n /**\n * Gap X\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-x': [{\n 'gap-x': scaleUnambiguousSpacing()\n }],\n /**\n * Gap Y\n * @see https://tailwindcss.com/docs/gap\n */\n 'gap-y': [{\n 'gap-y': scaleUnambiguousSpacing()\n }],\n /**\n * Justify Content\n * @see https://tailwindcss.com/docs/justify-content\n */\n 'justify-content': [{\n justify: [...scaleAlignPrimaryAxis(), 'normal']\n }],\n /**\n * Justify Items\n * @see https://tailwindcss.com/docs/justify-items\n */\n 'justify-items': [{\n 'justify-items': [...scaleAlignSecondaryAxis(), 'normal']\n }],\n /**\n * Justify Self\n * @see https://tailwindcss.com/docs/justify-self\n */\n 'justify-self': [{\n 'justify-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n /**\n * Align Content\n * @see https://tailwindcss.com/docs/align-content\n */\n 'align-content': [{\n content: ['normal', ...scaleAlignPrimaryAxis()]\n }],\n /**\n * Align Items\n * @see https://tailwindcss.com/docs/align-items\n */\n 'align-items': [{\n items: [...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Align Self\n * @see https://tailwindcss.com/docs/align-self\n */\n 'align-self': [{\n self: ['auto', ...scaleAlignSecondaryAxis(), {\n baseline: ['', 'last']\n }]\n }],\n /**\n * Place Content\n * @see https://tailwindcss.com/docs/place-content\n */\n 'place-content': [{\n 'place-content': scaleAlignPrimaryAxis()\n }],\n /**\n * Place Items\n * @see https://tailwindcss.com/docs/place-items\n */\n 'place-items': [{\n 'place-items': [...scaleAlignSecondaryAxis(), 'baseline']\n }],\n /**\n * Place Self\n * @see https://tailwindcss.com/docs/place-self\n */\n 'place-self': [{\n 'place-self': ['auto', ...scaleAlignSecondaryAxis()]\n }],\n // Spacing\n /**\n * Padding\n * @see https://tailwindcss.com/docs/padding\n */\n p: [{\n p: scaleUnambiguousSpacing()\n }],\n /**\n * Padding X\n * @see https://tailwindcss.com/docs/padding\n */\n px: [{\n px: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Y\n * @see https://tailwindcss.com/docs/padding\n */\n py: [{\n py: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Start\n * @see https://tailwindcss.com/docs/padding\n */\n ps: [{\n ps: scaleUnambiguousSpacing()\n }],\n /**\n * Padding End\n * @see https://tailwindcss.com/docs/padding\n */\n pe: [{\n pe: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Top\n * @see https://tailwindcss.com/docs/padding\n */\n pt: [{\n pt: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Right\n * @see https://tailwindcss.com/docs/padding\n */\n pr: [{\n pr: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Bottom\n * @see https://tailwindcss.com/docs/padding\n */\n pb: [{\n pb: scaleUnambiguousSpacing()\n }],\n /**\n * Padding Left\n * @see https://tailwindcss.com/docs/padding\n */\n pl: [{\n pl: scaleUnambiguousSpacing()\n }],\n /**\n * Margin\n * @see https://tailwindcss.com/docs/margin\n */\n m: [{\n m: scaleMargin()\n }],\n /**\n * Margin X\n * @see https://tailwindcss.com/docs/margin\n */\n mx: [{\n mx: scaleMargin()\n }],\n /**\n * Margin Y\n * @see https://tailwindcss.com/docs/margin\n */\n my: [{\n my: scaleMargin()\n }],\n /**\n * Margin Start\n * @see https://tailwindcss.com/docs/margin\n */\n ms: [{\n ms: scaleMargin()\n }],\n /**\n * Margin End\n * @see https://tailwindcss.com/docs/margin\n */\n me: [{\n me: scaleMargin()\n }],\n /**\n * Margin Top\n * @see https://tailwindcss.com/docs/margin\n */\n mt: [{\n mt: scaleMargin()\n }],\n /**\n * Margin Right\n * @see https://tailwindcss.com/docs/margin\n */\n mr: [{\n mr: scaleMargin()\n }],\n /**\n * Margin Bottom\n * @see https://tailwindcss.com/docs/margin\n */\n mb: [{\n mb: scaleMargin()\n }],\n /**\n * Margin Left\n * @see https://tailwindcss.com/docs/margin\n */\n ml: [{\n ml: scaleMargin()\n }],\n /**\n * Space Between X\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x': [{\n 'space-x': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between X Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-x-reverse': ['space-x-reverse'],\n /**\n * Space Between Y\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y': [{\n 'space-y': scaleUnambiguousSpacing()\n }],\n /**\n * Space Between Y Reverse\n * @see https://tailwindcss.com/docs/margin#adding-space-between-children\n */\n 'space-y-reverse': ['space-y-reverse'],\n // --------------\n // --- Sizing ---\n // --------------\n /**\n * Size\n * @see https://tailwindcss.com/docs/width#setting-both-width-and-height\n */\n size: [{\n size: scaleSizing()\n }],\n /**\n * Width\n * @see https://tailwindcss.com/docs/width\n */\n w: [{\n w: [themeContainer, 'screen', ...scaleSizing()]\n }],\n /**\n * Min-Width\n * @see https://tailwindcss.com/docs/min-width\n */\n 'min-w': [{\n 'min-w': [themeContainer, 'screen', /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'none', ...scaleSizing()]\n }],\n /**\n * Max-Width\n * @see https://tailwindcss.com/docs/max-width\n */\n 'max-w': [{\n 'max-w': [themeContainer, 'screen', 'none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n 'prose', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n {\n screen: [themeBreakpoint]\n }, ...scaleSizing()]\n }],\n /**\n * Height\n * @see https://tailwindcss.com/docs/height\n */\n h: [{\n h: ['screen', 'lh', ...scaleSizing()]\n }],\n /**\n * Min-Height\n * @see https://tailwindcss.com/docs/min-height\n */\n 'min-h': [{\n 'min-h': ['screen', 'lh', 'none', ...scaleSizing()]\n }],\n /**\n * Max-Height\n * @see https://tailwindcss.com/docs/max-height\n */\n 'max-h': [{\n 'max-h': ['screen', 'lh', ...scaleSizing()]\n }],\n // ------------------\n // --- Typography ---\n // ------------------\n /**\n * Font Size\n * @see https://tailwindcss.com/docs/font-size\n */\n 'font-size': [{\n text: ['base', themeText, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Font Smoothing\n * @see https://tailwindcss.com/docs/font-smoothing\n */\n 'font-smoothing': ['antialiased', 'subpixel-antialiased'],\n /**\n * Font Style\n * @see https://tailwindcss.com/docs/font-style\n */\n 'font-style': ['italic', 'not-italic'],\n /**\n * Font Weight\n * @see https://tailwindcss.com/docs/font-weight\n */\n 'font-weight': [{\n font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Font Stretch\n * @see https://tailwindcss.com/docs/font-stretch\n */\n 'font-stretch': [{\n 'font-stretch': ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', isPercent, isArbitraryValue]\n }],\n /**\n * Font Family\n * @see https://tailwindcss.com/docs/font-family\n */\n 'font-family': [{\n font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont]\n }],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-normal': ['normal-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-ordinal': ['ordinal'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-slashed-zero': ['slashed-zero'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-figure': ['lining-nums', 'oldstyle-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-spacing': ['proportional-nums', 'tabular-nums'],\n /**\n * Font Variant Numeric\n * @see https://tailwindcss.com/docs/font-variant-numeric\n */\n 'fvn-fraction': ['diagonal-fractions', 'stacked-fractions'],\n /**\n * Letter Spacing\n * @see https://tailwindcss.com/docs/letter-spacing\n */\n tracking: [{\n tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Line Clamp\n * @see https://tailwindcss.com/docs/line-clamp\n */\n 'line-clamp': [{\n 'line-clamp': [isNumber, 'none', isArbitraryVariable, isArbitraryNumber]\n }],\n /**\n * Line Height\n * @see https://tailwindcss.com/docs/line-height\n */\n leading: [{\n leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */\n themeLeading, ...scaleUnambiguousSpacing()]\n }],\n /**\n * List Style Image\n * @see https://tailwindcss.com/docs/list-style-image\n */\n 'list-image': [{\n 'list-image': ['none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * List Style Position\n * @see https://tailwindcss.com/docs/list-style-position\n */\n 'list-style-position': [{\n list: ['inside', 'outside']\n }],\n /**\n * List Style Type\n * @see https://tailwindcss.com/docs/list-style-type\n */\n 'list-style-type': [{\n list: ['disc', 'decimal', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Alignment\n * @see https://tailwindcss.com/docs/text-align\n */\n 'text-alignment': [{\n text: ['left', 'center', 'right', 'justify', 'start', 'end']\n }],\n /**\n * Placeholder Color\n * @deprecated since Tailwind CSS v3.0.0\n * @see https://v3.tailwindcss.com/docs/placeholder-color\n */\n 'placeholder-color': [{\n placeholder: scaleColor()\n }],\n /**\n * Text Color\n * @see https://tailwindcss.com/docs/text-color\n */\n 'text-color': [{\n text: scaleColor()\n }],\n /**\n * Text Decoration\n * @see https://tailwindcss.com/docs/text-decoration\n */\n 'text-decoration': ['underline', 'overline', 'line-through', 'no-underline'],\n /**\n * Text Decoration Style\n * @see https://tailwindcss.com/docs/text-decoration-style\n */\n 'text-decoration-style': [{\n decoration: [...scaleLineStyle(), 'wavy']\n }],\n /**\n * Text Decoration Thickness\n * @see https://tailwindcss.com/docs/text-decoration-thickness\n */\n 'text-decoration-thickness': [{\n decoration: [isNumber, 'from-font', 'auto', isArbitraryVariable, isArbitraryLength]\n }],\n /**\n * Text Decoration Color\n * @see https://tailwindcss.com/docs/text-decoration-color\n */\n 'text-decoration-color': [{\n decoration: scaleColor()\n }],\n /**\n * Text Underline Offset\n * @see https://tailwindcss.com/docs/text-underline-offset\n */\n 'underline-offset': [{\n 'underline-offset': [isNumber, 'auto', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Text Transform\n * @see https://tailwindcss.com/docs/text-transform\n */\n 'text-transform': ['uppercase', 'lowercase', 'capitalize', 'normal-case'],\n /**\n * Text Overflow\n * @see https://tailwindcss.com/docs/text-overflow\n */\n 'text-overflow': ['truncate', 'text-ellipsis', 'text-clip'],\n /**\n * Text Wrap\n * @see https://tailwindcss.com/docs/text-wrap\n */\n 'text-wrap': [{\n text: ['wrap', 'nowrap', 'balance', 'pretty']\n }],\n /**\n * Text Indent\n * @see https://tailwindcss.com/docs/text-indent\n */\n indent: [{\n indent: scaleUnambiguousSpacing()\n }],\n /**\n * Vertical Alignment\n * @see https://tailwindcss.com/docs/vertical-align\n */\n 'vertical-align': [{\n align: ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Whitespace\n * @see https://tailwindcss.com/docs/whitespace\n */\n whitespace: [{\n whitespace: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces']\n }],\n /**\n * Word Break\n * @see https://tailwindcss.com/docs/word-break\n */\n break: [{\n break: ['normal', 'words', 'all', 'keep']\n }],\n /**\n * Overflow Wrap\n * @see https://tailwindcss.com/docs/overflow-wrap\n */\n wrap: [{\n wrap: ['break-word', 'anywhere', 'normal']\n }],\n /**\n * Hyphens\n * @see https://tailwindcss.com/docs/hyphens\n */\n hyphens: [{\n hyphens: ['none', 'manual', 'auto']\n }],\n /**\n * Content\n * @see https://tailwindcss.com/docs/content\n */\n content: [{\n content: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // -------------------\n // --- Backgrounds ---\n // -------------------\n /**\n * Background Attachment\n * @see https://tailwindcss.com/docs/background-attachment\n */\n 'bg-attachment': [{\n bg: ['fixed', 'local', 'scroll']\n }],\n /**\n * Background Clip\n * @see https://tailwindcss.com/docs/background-clip\n */\n 'bg-clip': [{\n 'bg-clip': ['border', 'padding', 'content', 'text']\n }],\n /**\n * Background Origin\n * @see https://tailwindcss.com/docs/background-origin\n */\n 'bg-origin': [{\n 'bg-origin': ['border', 'padding', 'content']\n }],\n /**\n * Background Position\n * @see https://tailwindcss.com/docs/background-position\n */\n 'bg-position': [{\n bg: scaleBgPosition()\n }],\n /**\n * Background Repeat\n * @see https://tailwindcss.com/docs/background-repeat\n */\n 'bg-repeat': [{\n bg: scaleBgRepeat()\n }],\n /**\n * Background Size\n * @see https://tailwindcss.com/docs/background-size\n */\n 'bg-size': [{\n bg: scaleBgSize()\n }],\n /**\n * Background Image\n * @see https://tailwindcss.com/docs/background-image\n */\n 'bg-image': [{\n bg: ['none', {\n linear: [{\n to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']\n }, isInteger, isArbitraryVariable, isArbitraryValue],\n radial: ['', isArbitraryVariable, isArbitraryValue],\n conic: [isInteger, isArbitraryVariable, isArbitraryValue]\n }, isArbitraryVariableImage, isArbitraryImage]\n }],\n /**\n * Background Color\n * @see https://tailwindcss.com/docs/background-color\n */\n 'bg-color': [{\n bg: scaleColor()\n }],\n /**\n * Gradient Color Stops From Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from-pos': [{\n from: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops Via Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via-pos': [{\n via: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops To Position\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to-pos': [{\n to: scaleGradientStopPosition()\n }],\n /**\n * Gradient Color Stops From\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-from': [{\n from: scaleColor()\n }],\n /**\n * Gradient Color Stops Via\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-via': [{\n via: scaleColor()\n }],\n /**\n * Gradient Color Stops To\n * @see https://tailwindcss.com/docs/gradient-color-stops\n */\n 'gradient-to': [{\n to: scaleColor()\n }],\n // ---------------\n // --- Borders ---\n // ---------------\n /**\n * Border Radius\n * @see https://tailwindcss.com/docs/border-radius\n */\n rounded: [{\n rounded: scaleRadius()\n }],\n /**\n * Border Radius Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-s': [{\n 'rounded-s': scaleRadius()\n }],\n /**\n * Border Radius End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-e': [{\n 'rounded-e': scaleRadius()\n }],\n /**\n * Border Radius Top\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-t': [{\n 'rounded-t': scaleRadius()\n }],\n /**\n * Border Radius Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-r': [{\n 'rounded-r': scaleRadius()\n }],\n /**\n * Border Radius Bottom\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-b': [{\n 'rounded-b': scaleRadius()\n }],\n /**\n * Border Radius Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-l': [{\n 'rounded-l': scaleRadius()\n }],\n /**\n * Border Radius Start Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ss': [{\n 'rounded-ss': scaleRadius()\n }],\n /**\n * Border Radius Start End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-se': [{\n 'rounded-se': scaleRadius()\n }],\n /**\n * Border Radius End End\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-ee': [{\n 'rounded-ee': scaleRadius()\n }],\n /**\n * Border Radius End Start\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-es': [{\n 'rounded-es': scaleRadius()\n }],\n /**\n * Border Radius Top Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tl': [{\n 'rounded-tl': scaleRadius()\n }],\n /**\n * Border Radius Top Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-tr': [{\n 'rounded-tr': scaleRadius()\n }],\n /**\n * Border Radius Bottom Right\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-br': [{\n 'rounded-br': scaleRadius()\n }],\n /**\n * Border Radius Bottom Left\n * @see https://tailwindcss.com/docs/border-radius\n */\n 'rounded-bl': [{\n 'rounded-bl': scaleRadius()\n }],\n /**\n * Border Width\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w': [{\n border: scaleBorderWidth()\n }],\n /**\n * Border Width X\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-x': [{\n 'border-x': scaleBorderWidth()\n }],\n /**\n * Border Width Y\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-y': [{\n 'border-y': scaleBorderWidth()\n }],\n /**\n * Border Width Start\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-s': [{\n 'border-s': scaleBorderWidth()\n }],\n /**\n * Border Width End\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-e': [{\n 'border-e': scaleBorderWidth()\n }],\n /**\n * Border Width Top\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-t': [{\n 'border-t': scaleBorderWidth()\n }],\n /**\n * Border Width Right\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-r': [{\n 'border-r': scaleBorderWidth()\n }],\n /**\n * Border Width Bottom\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-b': [{\n 'border-b': scaleBorderWidth()\n }],\n /**\n * Border Width Left\n * @see https://tailwindcss.com/docs/border-width\n */\n 'border-w-l': [{\n 'border-l': scaleBorderWidth()\n }],\n /**\n * Divide Width X\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x': [{\n 'divide-x': scaleBorderWidth()\n }],\n /**\n * Divide Width X Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-x-reverse': ['divide-x-reverse'],\n /**\n * Divide Width Y\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y': [{\n 'divide-y': scaleBorderWidth()\n }],\n /**\n * Divide Width Y Reverse\n * @see https://tailwindcss.com/docs/border-width#between-children\n */\n 'divide-y-reverse': ['divide-y-reverse'],\n /**\n * Border Style\n * @see https://tailwindcss.com/docs/border-style\n */\n 'border-style': [{\n border: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Divide Style\n * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style\n */\n 'divide-style': [{\n divide: [...scaleLineStyle(), 'hidden', 'none']\n }],\n /**\n * Border Color\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color': [{\n border: scaleColor()\n }],\n /**\n * Border Color X\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-x': [{\n 'border-x': scaleColor()\n }],\n /**\n * Border Color Y\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-y': [{\n 'border-y': scaleColor()\n }],\n /**\n * Border Color S\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-s': [{\n 'border-s': scaleColor()\n }],\n /**\n * Border Color E\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-e': [{\n 'border-e': scaleColor()\n }],\n /**\n * Border Color Top\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-t': [{\n 'border-t': scaleColor()\n }],\n /**\n * Border Color Right\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-r': [{\n 'border-r': scaleColor()\n }],\n /**\n * Border Color Bottom\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-b': [{\n 'border-b': scaleColor()\n }],\n /**\n * Border Color Left\n * @see https://tailwindcss.com/docs/border-color\n */\n 'border-color-l': [{\n 'border-l': scaleColor()\n }],\n /**\n * Divide Color\n * @see https://tailwindcss.com/docs/divide-color\n */\n 'divide-color': [{\n divide: scaleColor()\n }],\n /**\n * Outline Style\n * @see https://tailwindcss.com/docs/outline-style\n */\n 'outline-style': [{\n outline: [...scaleLineStyle(), 'none', 'hidden']\n }],\n /**\n * Outline Offset\n * @see https://tailwindcss.com/docs/outline-offset\n */\n 'outline-offset': [{\n 'outline-offset': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Outline Width\n * @see https://tailwindcss.com/docs/outline-width\n */\n 'outline-w': [{\n outline: ['', isNumber, isArbitraryVariableLength, isArbitraryLength]\n }],\n /**\n * Outline Color\n * @see https://tailwindcss.com/docs/outline-color\n */\n 'outline-color': [{\n outline: scaleColor()\n }],\n // ---------------\n // --- Effects ---\n // ---------------\n /**\n * Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow\n */\n shadow: [{\n shadow: [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color\n */\n 'shadow-color': [{\n shadow: scaleColor()\n }],\n /**\n * Inset Box Shadow\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow\n */\n 'inset-shadow': [{\n 'inset-shadow': ['none', themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Inset Box Shadow Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color\n */\n 'inset-shadow-color': [{\n 'inset-shadow': scaleColor()\n }],\n /**\n * Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring\n */\n 'ring-w': [{\n ring: scaleBorderWidth()\n }],\n /**\n * Ring Width Inset\n * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-w-inset': ['ring-inset'],\n /**\n * Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color\n */\n 'ring-color': [{\n ring: scaleColor()\n }],\n /**\n * Ring Offset Width\n * @see https://v3.tailwindcss.com/docs/ring-offset-width\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-w': [{\n 'ring-offset': [isNumber, isArbitraryLength]\n }],\n /**\n * Ring Offset Color\n * @see https://v3.tailwindcss.com/docs/ring-offset-color\n * @deprecated since Tailwind CSS v4.0.0\n * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158\n */\n 'ring-offset-color': [{\n 'ring-offset': scaleColor()\n }],\n /**\n * Inset Ring Width\n * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring\n */\n 'inset-ring-w': [{\n 'inset-ring': scaleBorderWidth()\n }],\n /**\n * Inset Ring Color\n * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color\n */\n 'inset-ring-color': [{\n 'inset-ring': scaleColor()\n }],\n /**\n * Text Shadow\n * @see https://tailwindcss.com/docs/text-shadow\n */\n 'text-shadow': [{\n 'text-shadow': ['none', themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Text Shadow Color\n * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color\n */\n 'text-shadow-color': [{\n 'text-shadow': scaleColor()\n }],\n /**\n * Opacity\n * @see https://tailwindcss.com/docs/opacity\n */\n opacity: [{\n opacity: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Mix Blend Mode\n * @see https://tailwindcss.com/docs/mix-blend-mode\n */\n 'mix-blend': [{\n 'mix-blend': [...scaleBlendMode(), 'plus-darker', 'plus-lighter']\n }],\n /**\n * Background Blend Mode\n * @see https://tailwindcss.com/docs/background-blend-mode\n */\n 'bg-blend': [{\n 'bg-blend': scaleBlendMode()\n }],\n /**\n * Mask Clip\n * @see https://tailwindcss.com/docs/mask-clip\n */\n 'mask-clip': [{\n 'mask-clip': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }, 'mask-no-clip'],\n /**\n * Mask Composite\n * @see https://tailwindcss.com/docs/mask-composite\n */\n 'mask-composite': [{\n mask: ['add', 'subtract', 'intersect', 'exclude']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image-linear-pos': [{\n 'mask-linear': [isNumber]\n }],\n 'mask-image-linear-from-pos': [{\n 'mask-linear-from': scaleMaskImagePosition()\n }],\n 'mask-image-linear-to-pos': [{\n 'mask-linear-to': scaleMaskImagePosition()\n }],\n 'mask-image-linear-from-color': [{\n 'mask-linear-from': scaleColor()\n }],\n 'mask-image-linear-to-color': [{\n 'mask-linear-to': scaleColor()\n }],\n 'mask-image-t-from-pos': [{\n 'mask-t-from': scaleMaskImagePosition()\n }],\n 'mask-image-t-to-pos': [{\n 'mask-t-to': scaleMaskImagePosition()\n }],\n 'mask-image-t-from-color': [{\n 'mask-t-from': scaleColor()\n }],\n 'mask-image-t-to-color': [{\n 'mask-t-to': scaleColor()\n }],\n 'mask-image-r-from-pos': [{\n 'mask-r-from': scaleMaskImagePosition()\n }],\n 'mask-image-r-to-pos': [{\n 'mask-r-to': scaleMaskImagePosition()\n }],\n 'mask-image-r-from-color': [{\n 'mask-r-from': scaleColor()\n }],\n 'mask-image-r-to-color': [{\n 'mask-r-to': scaleColor()\n }],\n 'mask-image-b-from-pos': [{\n 'mask-b-from': scaleMaskImagePosition()\n }],\n 'mask-image-b-to-pos': [{\n 'mask-b-to': scaleMaskImagePosition()\n }],\n 'mask-image-b-from-color': [{\n 'mask-b-from': scaleColor()\n }],\n 'mask-image-b-to-color': [{\n 'mask-b-to': scaleColor()\n }],\n 'mask-image-l-from-pos': [{\n 'mask-l-from': scaleMaskImagePosition()\n }],\n 'mask-image-l-to-pos': [{\n 'mask-l-to': scaleMaskImagePosition()\n }],\n 'mask-image-l-from-color': [{\n 'mask-l-from': scaleColor()\n }],\n 'mask-image-l-to-color': [{\n 'mask-l-to': scaleColor()\n }],\n 'mask-image-x-from-pos': [{\n 'mask-x-from': scaleMaskImagePosition()\n }],\n 'mask-image-x-to-pos': [{\n 'mask-x-to': scaleMaskImagePosition()\n }],\n 'mask-image-x-from-color': [{\n 'mask-x-from': scaleColor()\n }],\n 'mask-image-x-to-color': [{\n 'mask-x-to': scaleColor()\n }],\n 'mask-image-y-from-pos': [{\n 'mask-y-from': scaleMaskImagePosition()\n }],\n 'mask-image-y-to-pos': [{\n 'mask-y-to': scaleMaskImagePosition()\n }],\n 'mask-image-y-from-color': [{\n 'mask-y-from': scaleColor()\n }],\n 'mask-image-y-to-color': [{\n 'mask-y-to': scaleColor()\n }],\n 'mask-image-radial': [{\n 'mask-radial': [isArbitraryVariable, isArbitraryValue]\n }],\n 'mask-image-radial-from-pos': [{\n 'mask-radial-from': scaleMaskImagePosition()\n }],\n 'mask-image-radial-to-pos': [{\n 'mask-radial-to': scaleMaskImagePosition()\n }],\n 'mask-image-radial-from-color': [{\n 'mask-radial-from': scaleColor()\n }],\n 'mask-image-radial-to-color': [{\n 'mask-radial-to': scaleColor()\n }],\n 'mask-image-radial-shape': [{\n 'mask-radial': ['circle', 'ellipse']\n }],\n 'mask-image-radial-size': [{\n 'mask-radial': [{\n closest: ['side', 'corner'],\n farthest: ['side', 'corner']\n }]\n }],\n 'mask-image-radial-pos': [{\n 'mask-radial-at': scalePosition()\n }],\n 'mask-image-conic-pos': [{\n 'mask-conic': [isNumber]\n }],\n 'mask-image-conic-from-pos': [{\n 'mask-conic-from': scaleMaskImagePosition()\n }],\n 'mask-image-conic-to-pos': [{\n 'mask-conic-to': scaleMaskImagePosition()\n }],\n 'mask-image-conic-from-color': [{\n 'mask-conic-from': scaleColor()\n }],\n 'mask-image-conic-to-color': [{\n 'mask-conic-to': scaleColor()\n }],\n /**\n * Mask Mode\n * @see https://tailwindcss.com/docs/mask-mode\n */\n 'mask-mode': [{\n mask: ['alpha', 'luminance', 'match']\n }],\n /**\n * Mask Origin\n * @see https://tailwindcss.com/docs/mask-origin\n */\n 'mask-origin': [{\n 'mask-origin': ['border', 'padding', 'content', 'fill', 'stroke', 'view']\n }],\n /**\n * Mask Position\n * @see https://tailwindcss.com/docs/mask-position\n */\n 'mask-position': [{\n mask: scaleBgPosition()\n }],\n /**\n * Mask Repeat\n * @see https://tailwindcss.com/docs/mask-repeat\n */\n 'mask-repeat': [{\n mask: scaleBgRepeat()\n }],\n /**\n * Mask Size\n * @see https://tailwindcss.com/docs/mask-size\n */\n 'mask-size': [{\n mask: scaleBgSize()\n }],\n /**\n * Mask Type\n * @see https://tailwindcss.com/docs/mask-type\n */\n 'mask-type': [{\n 'mask-type': ['alpha', 'luminance']\n }],\n /**\n * Mask Image\n * @see https://tailwindcss.com/docs/mask-image\n */\n 'mask-image': [{\n mask: ['none', isArbitraryVariable, isArbitraryValue]\n }],\n // ---------------\n // --- Filters ---\n // ---------------\n /**\n * Filter\n * @see https://tailwindcss.com/docs/filter\n */\n filter: [{\n filter: [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Blur\n * @see https://tailwindcss.com/docs/blur\n */\n blur: [{\n blur: scaleBlur()\n }],\n /**\n * Brightness\n * @see https://tailwindcss.com/docs/brightness\n */\n brightness: [{\n brightness: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Contrast\n * @see https://tailwindcss.com/docs/contrast\n */\n contrast: [{\n contrast: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Drop Shadow\n * @see https://tailwindcss.com/docs/drop-shadow\n */\n 'drop-shadow': [{\n 'drop-shadow': [\n // Deprecated since Tailwind CSS v4.0.0\n '', 'none', themeDropShadow, isArbitraryVariableShadow, isArbitraryShadow]\n }],\n /**\n * Drop Shadow Color\n * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color\n */\n 'drop-shadow-color': [{\n 'drop-shadow': scaleColor()\n }],\n /**\n * Grayscale\n * @see https://tailwindcss.com/docs/grayscale\n */\n grayscale: [{\n grayscale: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Hue Rotate\n * @see https://tailwindcss.com/docs/hue-rotate\n */\n 'hue-rotate': [{\n 'hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Invert\n * @see https://tailwindcss.com/docs/invert\n */\n invert: [{\n invert: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Saturate\n * @see https://tailwindcss.com/docs/saturate\n */\n saturate: [{\n saturate: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Sepia\n * @see https://tailwindcss.com/docs/sepia\n */\n sepia: [{\n sepia: ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Filter\n * @see https://tailwindcss.com/docs/backdrop-filter\n */\n 'backdrop-filter': [{\n 'backdrop-filter': [\n // Deprecated since Tailwind CSS v3.0.0\n '', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Blur\n * @see https://tailwindcss.com/docs/backdrop-blur\n */\n 'backdrop-blur': [{\n 'backdrop-blur': scaleBlur()\n }],\n /**\n * Backdrop Brightness\n * @see https://tailwindcss.com/docs/backdrop-brightness\n */\n 'backdrop-brightness': [{\n 'backdrop-brightness': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Contrast\n * @see https://tailwindcss.com/docs/backdrop-contrast\n */\n 'backdrop-contrast': [{\n 'backdrop-contrast': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Grayscale\n * @see https://tailwindcss.com/docs/backdrop-grayscale\n */\n 'backdrop-grayscale': [{\n 'backdrop-grayscale': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Hue Rotate\n * @see https://tailwindcss.com/docs/backdrop-hue-rotate\n */\n 'backdrop-hue-rotate': [{\n 'backdrop-hue-rotate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Invert\n * @see https://tailwindcss.com/docs/backdrop-invert\n */\n 'backdrop-invert': [{\n 'backdrop-invert': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Opacity\n * @see https://tailwindcss.com/docs/backdrop-opacity\n */\n 'backdrop-opacity': [{\n 'backdrop-opacity': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Saturate\n * @see https://tailwindcss.com/docs/backdrop-saturate\n */\n 'backdrop-saturate': [{\n 'backdrop-saturate': [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Backdrop Sepia\n * @see https://tailwindcss.com/docs/backdrop-sepia\n */\n 'backdrop-sepia': [{\n 'backdrop-sepia': ['', isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n // --------------\n // --- Tables ---\n // --------------\n /**\n * Border Collapse\n * @see https://tailwindcss.com/docs/border-collapse\n */\n 'border-collapse': [{\n border: ['collapse', 'separate']\n }],\n /**\n * Border Spacing\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing': [{\n 'border-spacing': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing X\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-x': [{\n 'border-spacing-x': scaleUnambiguousSpacing()\n }],\n /**\n * Border Spacing Y\n * @see https://tailwindcss.com/docs/border-spacing\n */\n 'border-spacing-y': [{\n 'border-spacing-y': scaleUnambiguousSpacing()\n }],\n /**\n * Table Layout\n * @see https://tailwindcss.com/docs/table-layout\n */\n 'table-layout': [{\n table: ['auto', 'fixed']\n }],\n /**\n * Caption Side\n * @see https://tailwindcss.com/docs/caption-side\n */\n caption: [{\n caption: ['top', 'bottom']\n }],\n // ---------------------------------\n // --- Transitions and Animation ---\n // ---------------------------------\n /**\n * Transition Property\n * @see https://tailwindcss.com/docs/transition-property\n */\n transition: [{\n transition: ['', 'all', 'colors', 'opacity', 'shadow', 'transform', 'none', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Behavior\n * @see https://tailwindcss.com/docs/transition-behavior\n */\n 'transition-behavior': [{\n transition: ['normal', 'discrete']\n }],\n /**\n * Transition Duration\n * @see https://tailwindcss.com/docs/transition-duration\n */\n duration: [{\n duration: [isNumber, 'initial', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Timing Function\n * @see https://tailwindcss.com/docs/transition-timing-function\n */\n ease: [{\n ease: ['linear', 'initial', themeEase, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Transition Delay\n * @see https://tailwindcss.com/docs/transition-delay\n */\n delay: [{\n delay: [isNumber, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Animation\n * @see https://tailwindcss.com/docs/animation\n */\n animate: [{\n animate: ['none', themeAnimate, isArbitraryVariable, isArbitraryValue]\n }],\n // ------------------\n // --- Transforms ---\n // ------------------\n /**\n * Backface Visibility\n * @see https://tailwindcss.com/docs/backface-visibility\n */\n backface: [{\n backface: ['hidden', 'visible']\n }],\n /**\n * Perspective\n * @see https://tailwindcss.com/docs/perspective\n */\n perspective: [{\n perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Perspective Origin\n * @see https://tailwindcss.com/docs/perspective-origin\n */\n 'perspective-origin': [{\n 'perspective-origin': scalePositionWithArbitrary()\n }],\n /**\n * Rotate\n * @see https://tailwindcss.com/docs/rotate\n */\n rotate: [{\n rotate: scaleRotate()\n }],\n /**\n * Rotate X\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-x': [{\n 'rotate-x': scaleRotate()\n }],\n /**\n * Rotate Y\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-y': [{\n 'rotate-y': scaleRotate()\n }],\n /**\n * Rotate Z\n * @see https://tailwindcss.com/docs/rotate\n */\n 'rotate-z': [{\n 'rotate-z': scaleRotate()\n }],\n /**\n * Scale\n * @see https://tailwindcss.com/docs/scale\n */\n scale: [{\n scale: scaleScale()\n }],\n /**\n * Scale X\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-x': [{\n 'scale-x': scaleScale()\n }],\n /**\n * Scale Y\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-y': [{\n 'scale-y': scaleScale()\n }],\n /**\n * Scale Z\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-z': [{\n 'scale-z': scaleScale()\n }],\n /**\n * Scale 3D\n * @see https://tailwindcss.com/docs/scale\n */\n 'scale-3d': ['scale-3d'],\n /**\n * Skew\n * @see https://tailwindcss.com/docs/skew\n */\n skew: [{\n skew: scaleSkew()\n }],\n /**\n * Skew X\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-x': [{\n 'skew-x': scaleSkew()\n }],\n /**\n * Skew Y\n * @see https://tailwindcss.com/docs/skew\n */\n 'skew-y': [{\n 'skew-y': scaleSkew()\n }],\n /**\n * Transform\n * @see https://tailwindcss.com/docs/transform\n */\n transform: [{\n transform: [isArbitraryVariable, isArbitraryValue, '', 'none', 'gpu', 'cpu']\n }],\n /**\n * Transform Origin\n * @see https://tailwindcss.com/docs/transform-origin\n */\n 'transform-origin': [{\n origin: scalePositionWithArbitrary()\n }],\n /**\n * Transform Style\n * @see https://tailwindcss.com/docs/transform-style\n */\n 'transform-style': [{\n transform: ['3d', 'flat']\n }],\n /**\n * Translate\n * @see https://tailwindcss.com/docs/translate\n */\n translate: [{\n translate: scaleTranslate()\n }],\n /**\n * Translate X\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-x': [{\n 'translate-x': scaleTranslate()\n }],\n /**\n * Translate Y\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-y': [{\n 'translate-y': scaleTranslate()\n }],\n /**\n * Translate Z\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-z': [{\n 'translate-z': scaleTranslate()\n }],\n /**\n * Translate None\n * @see https://tailwindcss.com/docs/translate\n */\n 'translate-none': ['translate-none'],\n // ---------------------\n // --- Interactivity ---\n // ---------------------\n /**\n * Accent Color\n * @see https://tailwindcss.com/docs/accent-color\n */\n accent: [{\n accent: scaleColor()\n }],\n /**\n * Appearance\n * @see https://tailwindcss.com/docs/appearance\n */\n appearance: [{\n appearance: ['none', 'auto']\n }],\n /**\n * Caret Color\n * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities\n */\n 'caret-color': [{\n caret: scaleColor()\n }],\n /**\n * Color Scheme\n * @see https://tailwindcss.com/docs/color-scheme\n */\n 'color-scheme': [{\n scheme: ['normal', 'dark', 'light', 'light-dark', 'only-dark', 'only-light']\n }],\n /**\n * Cursor\n * @see https://tailwindcss.com/docs/cursor\n */\n cursor: [{\n cursor: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'help', 'not-allowed', 'none', 'context-menu', 'progress', 'cell', 'crosshair', 'vertical-text', 'alias', 'copy', 'no-drop', 'grab', 'grabbing', 'all-scroll', 'col-resize', 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', isArbitraryVariable, isArbitraryValue]\n }],\n /**\n * Field Sizing\n * @see https://tailwindcss.com/docs/field-sizing\n */\n 'field-sizing': [{\n 'field-sizing': ['fixed', 'content']\n }],\n /**\n * Pointer Events\n * @see https://tailwindcss.com/docs/pointer-events\n */\n 'pointer-events': [{\n 'pointer-events': ['auto', 'none']\n }],\n /**\n * Resize\n * @see https://tailwindcss.com/docs/resize\n */\n resize: [{\n resize: ['none', '', 'y', 'x']\n }],\n /**\n * Scroll Behavior\n * @see https://tailwindcss.com/docs/scroll-behavior\n */\n 'scroll-behavior': [{\n scroll: ['auto', 'smooth']\n }],\n /**\n * Scroll Margin\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-m': [{\n 'scroll-m': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin X\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mx': [{\n 'scroll-mx': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Y\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-my': [{\n 'scroll-my': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Start\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ms': [{\n 'scroll-ms': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin End\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-me': [{\n 'scroll-me': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Top\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mt': [{\n 'scroll-mt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Right\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mr': [{\n 'scroll-mr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Bottom\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-mb': [{\n 'scroll-mb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Margin Left\n * @see https://tailwindcss.com/docs/scroll-margin\n */\n 'scroll-ml': [{\n 'scroll-ml': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-p': [{\n 'scroll-p': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding X\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-px': [{\n 'scroll-px': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Y\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-py': [{\n 'scroll-py': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Start\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-ps': [{\n 'scroll-ps': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding End\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pe': [{\n 'scroll-pe': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Top\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pt': [{\n 'scroll-pt': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Right\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pr': [{\n 'scroll-pr': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Bottom\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pb': [{\n 'scroll-pb': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Padding Left\n * @see https://tailwindcss.com/docs/scroll-padding\n */\n 'scroll-pl': [{\n 'scroll-pl': scaleUnambiguousSpacing()\n }],\n /**\n * Scroll Snap Align\n * @see https://tailwindcss.com/docs/scroll-snap-align\n */\n 'snap-align': [{\n snap: ['start', 'end', 'center', 'align-none']\n }],\n /**\n * Scroll Snap Stop\n * @see https://tailwindcss.com/docs/scroll-snap-stop\n */\n 'snap-stop': [{\n snap: ['normal', 'always']\n }],\n /**\n * Scroll Snap Type\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-type': [{\n snap: ['none', 'x', 'y', 'both']\n }],\n /**\n * Scroll Snap Type Strictness\n * @see https://tailwindcss.com/docs/scroll-snap-type\n */\n 'snap-strictness': [{\n snap: ['mandatory', 'proximity']\n }],\n /**\n * Touch Action\n * @see https://tailwindcss.com/docs/touch-action\n */\n touch: [{\n touch: ['auto', 'none', 'manipulation']\n }],\n /**\n * Touch Action X\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-x': [{\n 'touch-pan': ['x', 'left', 'right']\n }],\n /**\n * Touch Action Y\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-y': [{\n 'touch-pan': ['y', 'up', 'down']\n }],\n /**\n * Touch Action Pinch Zoom\n * @see https://tailwindcss.com/docs/touch-action\n */\n 'touch-pz': ['touch-pinch-zoom'],\n /**\n * User Select\n * @see https://tailwindcss.com/docs/user-select\n */\n select: [{\n select: ['none', 'text', 'all', 'auto']\n }],\n /**\n * Will Change\n * @see https://tailwindcss.com/docs/will-change\n */\n 'will-change': [{\n 'will-change': ['auto', 'scroll', 'contents', 'transform', isArbitraryVariable, isArbitraryValue]\n }],\n // -----------\n // --- SVG ---\n // -----------\n /**\n * Fill\n * @see https://tailwindcss.com/docs/fill\n */\n fill: [{\n fill: ['none', ...scaleColor()]\n }],\n /**\n * Stroke Width\n * @see https://tailwindcss.com/docs/stroke-width\n */\n 'stroke-w': [{\n stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]\n }],\n /**\n * Stroke\n * @see https://tailwindcss.com/docs/stroke\n */\n stroke: [{\n stroke: ['none', ...scaleColor()]\n }],\n // ---------------------\n // --- Accessibility ---\n // ---------------------\n /**\n * Forced Color Adjust\n * @see https://tailwindcss.com/docs/forced-color-adjust\n */\n 'forced-color-adjust': [{\n 'forced-color-adjust': ['auto', 'none']\n }]\n },\n conflictingClassGroups: {\n overflow: ['overflow-x', 'overflow-y'],\n overscroll: ['overscroll-x', 'overscroll-y'],\n inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],\n 'inset-x': ['right', 'left'],\n 'inset-y': ['top', 'bottom'],\n flex: ['basis', 'grow', 'shrink'],\n gap: ['gap-x', 'gap-y'],\n p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],\n px: ['pr', 'pl'],\n py: ['pt', 'pb'],\n m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],\n mx: ['mr', 'ml'],\n my: ['mt', 'mb'],\n size: ['w', 'h'],\n 'font-size': ['leading'],\n 'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],\n 'fvn-ordinal': ['fvn-normal'],\n 'fvn-slashed-zero': ['fvn-normal'],\n 'fvn-figure': ['fvn-normal'],\n 'fvn-spacing': ['fvn-normal'],\n 'fvn-fraction': ['fvn-normal'],\n 'line-clamp': ['display', 'overflow'],\n rounded: ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],\n 'rounded-s': ['rounded-ss', 'rounded-es'],\n 'rounded-e': ['rounded-se', 'rounded-ee'],\n 'rounded-t': ['rounded-tl', 'rounded-tr'],\n 'rounded-r': ['rounded-tr', 'rounded-br'],\n 'rounded-b': ['rounded-br', 'rounded-bl'],\n 'rounded-l': ['rounded-tl', 'rounded-bl'],\n 'border-spacing': ['border-spacing-x', 'border-spacing-y'],\n 'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],\n 'border-w-x': ['border-w-r', 'border-w-l'],\n 'border-w-y': ['border-w-t', 'border-w-b'],\n 'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],\n 'border-color-x': ['border-color-r', 'border-color-l'],\n 'border-color-y': ['border-color-t', 'border-color-b'],\n translate: ['translate-x', 'translate-y', 'translate-none'],\n 'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],\n 'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],\n 'scroll-mx': ['scroll-mr', 'scroll-ml'],\n 'scroll-my': ['scroll-mt', 'scroll-mb'],\n 'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],\n 'scroll-px': ['scroll-pr', 'scroll-pl'],\n 'scroll-py': ['scroll-pt', 'scroll-pb'],\n touch: ['touch-x', 'touch-y', 'touch-pz'],\n 'touch-x': ['touch'],\n 'touch-y': ['touch'],\n 'touch-pz': ['touch']\n },\n conflictingClassGroupModifiers: {\n 'font-size': ['leading']\n },\n orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']\n };\n};\n\n/**\n * @param baseConfig Config where other config will be merged into. This object will be mutated.\n * @param configExtension Partial config to merge into the `baseConfig`.\n */\nconst mergeConfigs = (baseConfig, {\n cacheSize,\n prefix,\n experimentalParseClassName,\n extend = {},\n override = {}\n}) => {\n overrideProperty(baseConfig, 'cacheSize', cacheSize);\n overrideProperty(baseConfig, 'prefix', prefix);\n overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName);\n overrideConfigProperties(baseConfig.theme, override.theme);\n overrideConfigProperties(baseConfig.classGroups, override.classGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups);\n overrideConfigProperties(baseConfig.conflictingClassGroupModifiers, override.conflictingClassGroupModifiers);\n overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers);\n mergeConfigProperties(baseConfig.theme, extend.theme);\n mergeConfigProperties(baseConfig.classGroups, extend.classGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups);\n mergeConfigProperties(baseConfig.conflictingClassGroupModifiers, extend.conflictingClassGroupModifiers);\n mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers');\n return baseConfig;\n};\nconst overrideProperty = (baseObject, overrideKey, overrideValue) => {\n if (overrideValue !== undefined) {\n baseObject[overrideKey] = overrideValue;\n }\n};\nconst overrideConfigProperties = (baseObject, overrideObject) => {\n if (overrideObject) {\n for (const key in overrideObject) {\n overrideProperty(baseObject, key, overrideObject[key]);\n }\n }\n};\nconst mergeConfigProperties = (baseObject, mergeObject) => {\n if (mergeObject) {\n for (const key in mergeObject) {\n mergeArrayProperties(baseObject, mergeObject, key);\n }\n }\n};\nconst mergeArrayProperties = (baseObject, mergeObject, key) => {\n const mergeValue = mergeObject[key];\n if (mergeValue !== undefined) {\n baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue;\n }\n};\nconst extendTailwindMerge = (configExtension, ...createConfig) => typeof configExtension === 'function' ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig) : createTailwindMerge(() => mergeConfigs(getDefaultConfig(), configExtension), ...createConfig);\nconst twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);\nexport { createTailwindMerge, extendTailwindMerge, fromTheme, getDefaultConfig, mergeConfigs, twJoin, twMerge, validators };\n//# sourceMappingURL=bundle-mjs.mjs.map\n","import { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nexport const cn = (...inputs) => twMerge(clsx(inputs));\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","/**\n * @license React\n * scheduler.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nfunction push(heap, node) {\n var index = heap.length;\n heap.push(node);\n a: for (; 0 < index; ) {\n var parentIndex = (index - 1) >>> 1,\n parent = heap[parentIndex];\n if (0 < compare(parent, node))\n (heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);\n else break a;\n }\n}\nfunction peek(heap) {\n return 0 === heap.length ? null : heap[0];\n}\nfunction pop(heap) {\n if (0 === heap.length) return null;\n var first = heap[0],\n last = heap.pop();\n if (last !== first) {\n heap[0] = last;\n a: for (\n var index = 0, length = heap.length, halfLength = length >>> 1;\n index < halfLength;\n\n ) {\n var leftIndex = 2 * (index + 1) - 1,\n left = heap[leftIndex],\n rightIndex = leftIndex + 1,\n right = heap[rightIndex];\n if (0 > compare(left, last))\n rightIndex < length && 0 > compare(right, left)\n ? ((heap[index] = right),\n (heap[rightIndex] = last),\n (index = rightIndex))\n : ((heap[index] = left),\n (heap[leftIndex] = last),\n (index = leftIndex));\n else if (rightIndex < length && 0 > compare(right, last))\n (heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);\n else break a;\n }\n }\n return first;\n}\nfunction compare(a, b) {\n var diff = a.sortIndex - b.sortIndex;\n return 0 !== diff ? diff : a.id - b.id;\n}\nexports.unstable_now = void 0;\nif (\"object\" === typeof performance && \"function\" === typeof performance.now) {\n var localPerformance = performance;\n exports.unstable_now = function () {\n return localPerformance.now();\n };\n} else {\n var localDate = Date,\n initialTime = localDate.now();\n exports.unstable_now = function () {\n return localDate.now() - initialTime;\n };\n}\nvar taskQueue = [],\n timerQueue = [],\n taskIdCounter = 1,\n currentTask = null,\n currentPriorityLevel = 3,\n isPerformingWork = !1,\n isHostCallbackScheduled = !1,\n isHostTimeoutScheduled = !1,\n needsPaint = !1,\n localSetTimeout = \"function\" === typeof setTimeout ? setTimeout : null,\n localClearTimeout = \"function\" === typeof clearTimeout ? clearTimeout : null,\n localSetImmediate = \"undefined\" !== typeof setImmediate ? setImmediate : null;\nfunction advanceTimers(currentTime) {\n for (var timer = peek(timerQueue); null !== timer; ) {\n if (null === timer.callback) pop(timerQueue);\n else if (timer.startTime <= currentTime)\n pop(timerQueue),\n (timer.sortIndex = timer.expirationTime),\n push(taskQueue, timer);\n else break;\n timer = peek(timerQueue);\n }\n}\nfunction handleTimeout(currentTime) {\n isHostTimeoutScheduled = !1;\n advanceTimers(currentTime);\n if (!isHostCallbackScheduled)\n if (null !== peek(taskQueue))\n (isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);\n }\n}\nvar isMessageLoopRunning = !1,\n taskTimeoutID = -1,\n frameInterval = 5,\n startTime = -1;\nfunction shouldYieldToHost() {\n return needsPaint\n ? !0\n : exports.unstable_now() - startTime < frameInterval\n ? !1\n : !0;\n}\nfunction performWorkUntilDeadline() {\n needsPaint = !1;\n if (isMessageLoopRunning) {\n var currentTime = exports.unstable_now();\n startTime = currentTime;\n var hasMoreWork = !0;\n try {\n a: {\n isHostCallbackScheduled = !1;\n isHostTimeoutScheduled &&\n ((isHostTimeoutScheduled = !1),\n localClearTimeout(taskTimeoutID),\n (taskTimeoutID = -1));\n isPerformingWork = !0;\n var previousPriorityLevel = currentPriorityLevel;\n try {\n b: {\n advanceTimers(currentTime);\n for (\n currentTask = peek(taskQueue);\n null !== currentTask &&\n !(\n currentTask.expirationTime > currentTime && shouldYieldToHost()\n );\n\n ) {\n var callback = currentTask.callback;\n if (\"function\" === typeof callback) {\n currentTask.callback = null;\n currentPriorityLevel = currentTask.priorityLevel;\n var continuationCallback = callback(\n currentTask.expirationTime <= currentTime\n );\n currentTime = exports.unstable_now();\n if (\"function\" === typeof continuationCallback) {\n currentTask.callback = continuationCallback;\n advanceTimers(currentTime);\n hasMoreWork = !0;\n break b;\n }\n currentTask === peek(taskQueue) && pop(taskQueue);\n advanceTimers(currentTime);\n } else pop(taskQueue);\n currentTask = peek(taskQueue);\n }\n if (null !== currentTask) hasMoreWork = !0;\n else {\n var firstTimer = peek(timerQueue);\n null !== firstTimer &&\n requestHostTimeout(\n handleTimeout,\n firstTimer.startTime - currentTime\n );\n hasMoreWork = !1;\n }\n }\n break a;\n } finally {\n (currentTask = null),\n (currentPriorityLevel = previousPriorityLevel),\n (isPerformingWork = !1);\n }\n hasMoreWork = void 0;\n }\n } finally {\n hasMoreWork\n ? schedulePerformWorkUntilDeadline()\n : (isMessageLoopRunning = !1);\n }\n }\n}\nvar schedulePerformWorkUntilDeadline;\nif (\"function\" === typeof localSetImmediate)\n schedulePerformWorkUntilDeadline = function () {\n localSetImmediate(performWorkUntilDeadline);\n };\nelse if (\"undefined\" !== typeof MessageChannel) {\n var channel = new MessageChannel(),\n port = channel.port2;\n channel.port1.onmessage = performWorkUntilDeadline;\n schedulePerformWorkUntilDeadline = function () {\n port.postMessage(null);\n };\n} else\n schedulePerformWorkUntilDeadline = function () {\n localSetTimeout(performWorkUntilDeadline, 0);\n };\nfunction requestHostTimeout(callback, ms) {\n taskTimeoutID = localSetTimeout(function () {\n callback(exports.unstable_now());\n }, ms);\n}\nexports.unstable_IdlePriority = 5;\nexports.unstable_ImmediatePriority = 1;\nexports.unstable_LowPriority = 4;\nexports.unstable_NormalPriority = 3;\nexports.unstable_Profiling = null;\nexports.unstable_UserBlockingPriority = 2;\nexports.unstable_cancelCallback = function (task) {\n task.callback = null;\n};\nexports.unstable_forceFrameRate = function (fps) {\n 0 > fps || 125 < fps\n ? console.error(\n \"forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported\"\n )\n : (frameInterval = 0 < fps ? Math.floor(1e3 / fps) : 5);\n};\nexports.unstable_getCurrentPriorityLevel = function () {\n return currentPriorityLevel;\n};\nexports.unstable_next = function (eventHandler) {\n switch (currentPriorityLevel) {\n case 1:\n case 2:\n case 3:\n var priorityLevel = 3;\n break;\n default:\n priorityLevel = currentPriorityLevel;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_requestPaint = function () {\n needsPaint = !0;\n};\nexports.unstable_runWithPriority = function (priorityLevel, eventHandler) {\n switch (priorityLevel) {\n case 1:\n case 2:\n case 3:\n case 4:\n case 5:\n break;\n default:\n priorityLevel = 3;\n }\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = priorityLevel;\n try {\n return eventHandler();\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n};\nexports.unstable_scheduleCallback = function (\n priorityLevel,\n callback,\n options\n) {\n var currentTime = exports.unstable_now();\n \"object\" === typeof options && null !== options\n ? ((options = options.delay),\n (options =\n \"number\" === typeof options && 0 < options\n ? currentTime + options\n : currentTime))\n : (options = currentTime);\n switch (priorityLevel) {\n case 1:\n var timeout = -1;\n break;\n case 2:\n timeout = 250;\n break;\n case 5:\n timeout = 1073741823;\n break;\n case 4:\n timeout = 1e4;\n break;\n default:\n timeout = 5e3;\n }\n timeout = options + timeout;\n priorityLevel = {\n id: taskIdCounter++,\n callback: callback,\n priorityLevel: priorityLevel,\n startTime: options,\n expirationTime: timeout,\n sortIndex: -1\n };\n options > currentTime\n ? ((priorityLevel.sortIndex = options),\n push(timerQueue, priorityLevel),\n null === peek(taskQueue) &&\n priorityLevel === peek(timerQueue) &&\n (isHostTimeoutScheduled\n ? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))\n : (isHostTimeoutScheduled = !0),\n requestHostTimeout(handleTimeout, options - currentTime)))\n : ((priorityLevel.sortIndex = timeout),\n push(taskQueue, priorityLevel),\n isHostCallbackScheduled ||\n isPerformingWork ||\n ((isHostCallbackScheduled = !0),\n isMessageLoopRunning ||\n ((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));\n return priorityLevel;\n};\nexports.unstable_shouldYield = shouldYieldToHost;\nexports.unstable_wrapCallback = function (callback) {\n var parentPriorityLevel = currentPriorityLevel;\n return function () {\n var previousPriorityLevel = currentPriorityLevel;\n currentPriorityLevel = parentPriorityLevel;\n try {\n return callback.apply(this, arguments);\n } finally {\n currentPriorityLevel = previousPriorityLevel;\n }\n };\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction noop() {}\nvar Internals = {\n d: {\n f: noop,\n r: function () {\n throw Error(formatProdErrorMessage(522));\n },\n D: noop,\n C: noop,\n L: noop,\n m: noop,\n X: noop,\n S: noop,\n M: noop\n },\n p: 0,\n findDOMNode: null\n },\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\");\nfunction createPortal$1(children, containerInfo, implementation) {\n var key =\n 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;\n return {\n $$typeof: REACT_PORTAL_TYPE,\n key: null == key ? null : \"\" + key,\n children: children,\n containerInfo: containerInfo,\n implementation: implementation\n };\n}\nvar ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\nfunction getCrossOriginStringAs(as, input) {\n if (\"font\" === as) return \"\";\n if (\"string\" === typeof input)\n return \"use-credentials\" === input ? input : \"\";\n}\nexports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =\n Internals;\nexports.createPortal = function (children, container) {\n var key =\n 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;\n if (\n !container ||\n (1 !== container.nodeType &&\n 9 !== container.nodeType &&\n 11 !== container.nodeType)\n )\n throw Error(formatProdErrorMessage(299));\n return createPortal$1(children, container, null, key);\n};\nexports.flushSync = function (fn) {\n var previousTransition = ReactSharedInternals.T,\n previousUpdatePriority = Internals.p;\n try {\n if (((ReactSharedInternals.T = null), (Internals.p = 2), fn)) return fn();\n } finally {\n (ReactSharedInternals.T = previousTransition),\n (Internals.p = previousUpdatePriority),\n Internals.d.f();\n }\n};\nexports.preconnect = function (href, options) {\n \"string\" === typeof href &&\n (options\n ? ((options = options.crossOrigin),\n (options =\n \"string\" === typeof options\n ? \"use-credentials\" === options\n ? options\n : \"\"\n : void 0))\n : (options = null),\n Internals.d.C(href, options));\n};\nexports.prefetchDNS = function (href) {\n \"string\" === typeof href && Internals.d.D(href);\n};\nexports.preinit = function (href, options) {\n if (\"string\" === typeof href && options && \"string\" === typeof options.as) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin),\n integrity =\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n fetchPriority =\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0;\n \"style\" === as\n ? Internals.d.S(\n href,\n \"string\" === typeof options.precedence ? options.precedence : void 0,\n {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority\n }\n )\n : \"script\" === as &&\n Internals.d.X(href, {\n crossOrigin: crossOrigin,\n integrity: integrity,\n fetchPriority: fetchPriority,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n};\nexports.preinitModule = function (href, options) {\n if (\"string\" === typeof href)\n if (\"object\" === typeof options && null !== options) {\n if (null == options.as || \"script\" === options.as) {\n var crossOrigin = getCrossOriginStringAs(\n options.as,\n options.crossOrigin\n );\n Internals.d.M(href, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0\n });\n }\n } else null == options && Internals.d.M(href);\n};\nexports.preload = function (href, options) {\n if (\n \"string\" === typeof href &&\n \"object\" === typeof options &&\n null !== options &&\n \"string\" === typeof options.as\n ) {\n var as = options.as,\n crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);\n Internals.d.L(href, as, {\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0,\n nonce: \"string\" === typeof options.nonce ? options.nonce : void 0,\n type: \"string\" === typeof options.type ? options.type : void 0,\n fetchPriority:\n \"string\" === typeof options.fetchPriority\n ? options.fetchPriority\n : void 0,\n referrerPolicy:\n \"string\" === typeof options.referrerPolicy\n ? options.referrerPolicy\n : void 0,\n imageSrcSet:\n \"string\" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,\n imageSizes:\n \"string\" === typeof options.imageSizes ? options.imageSizes : void 0,\n media: \"string\" === typeof options.media ? options.media : void 0\n });\n }\n};\nexports.preloadModule = function (href, options) {\n if (\"string\" === typeof href)\n if (options) {\n var crossOrigin = getCrossOriginStringAs(options.as, options.crossOrigin);\n Internals.d.m(href, {\n as:\n \"string\" === typeof options.as && \"script\" !== options.as\n ? options.as\n : void 0,\n crossOrigin: crossOrigin,\n integrity:\n \"string\" === typeof options.integrity ? options.integrity : void 0\n });\n } else Internals.d.m(href);\n};\nexports.requestFormReset = function (form) {\n Internals.d.r(form);\n};\nexports.unstable_batchedUpdates = function (fn, a) {\n return fn(a);\n};\nexports.useFormState = function (action, initialState, permalink) {\n return ReactSharedInternals.H.useFormState(action, initialState, permalink);\n};\nexports.useFormStatus = function () {\n return ReactSharedInternals.H.useHostTransitionStatus();\n};\nexports.version = \"19.2.0\";\n","'use strict';\n\nfunction checkDCE() {\n /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\n if (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' ||\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function'\n ) {\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // This branch is unreachable because this function is only called\n // in production, but the condition is true only in development.\n // Therefore if the branch is still here, dead code elimination wasn't\n // properly applied.\n // Don't change the message. React DevTools relies on it. Also make sure\n // this message doesn't occur elsewhere in this function, or it will cause\n // a false positive.\n throw new Error('^_^');\n }\n try {\n // Verify that the code above has been dead code eliminated (DCE'd).\n __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);\n } catch (err) {\n // DevTools shouldn't crash React, no matter what.\n // We should still report in case we break this code.\n console.error(err);\n }\n}\n\nif (process.env.NODE_ENV === 'production') {\n // DCE check should happen before ReactDOM bundle executes so that\n // DevTools can report bad minification during injection.\n checkDCE();\n module.exports = require('./cjs/react-dom.production.js');\n} else {\n module.exports = require('./cjs/react-dom.development.js');\n}\n","/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n\"use strict\";\nvar Scheduler = require(\"scheduler\"),\n React = require(\"react\"),\n ReactDOM = require(\"react-dom\");\nfunction formatProdErrorMessage(code) {\n var url = \"https://react.dev/errors/\" + code;\n if (1 < arguments.length) {\n url += \"?args[]=\" + encodeURIComponent(arguments[1]);\n for (var i = 2; i < arguments.length; i++)\n url += \"&args[]=\" + encodeURIComponent(arguments[i]);\n }\n return (\n \"Minified React error #\" +\n code +\n \"; visit \" +\n url +\n \" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"\n );\n}\nfunction isValidContainer(node) {\n return !(\n !node ||\n (1 !== node.nodeType && 9 !== node.nodeType && 11 !== node.nodeType)\n );\n}\nfunction getNearestMountedFiber(fiber) {\n var node = fiber,\n nearestMounted = fiber;\n if (fiber.alternate) for (; node.return; ) node = node.return;\n else {\n fiber = node;\n do\n (node = fiber),\n 0 !== (node.flags & 4098) && (nearestMounted = node.return),\n (fiber = node.return);\n while (fiber);\n }\n return 3 === node.tag ? nearestMounted : null;\n}\nfunction getSuspenseInstanceFromFiber(fiber) {\n if (13 === fiber.tag) {\n var suspenseState = fiber.memoizedState;\n null === suspenseState &&\n ((fiber = fiber.alternate),\n null !== fiber && (suspenseState = fiber.memoizedState));\n if (null !== suspenseState) return suspenseState.dehydrated;\n }\n return null;\n}\nfunction getActivityInstanceFromFiber(fiber) {\n if (31 === fiber.tag) {\n var activityState = fiber.memoizedState;\n null === activityState &&\n ((fiber = fiber.alternate),\n null !== fiber && (activityState = fiber.memoizedState));\n if (null !== activityState) return activityState.dehydrated;\n }\n return null;\n}\nfunction assertIsMounted(fiber) {\n if (getNearestMountedFiber(fiber) !== fiber)\n throw Error(formatProdErrorMessage(188));\n}\nfunction findCurrentFiberUsingSlowPath(fiber) {\n var alternate = fiber.alternate;\n if (!alternate) {\n alternate = getNearestMountedFiber(fiber);\n if (null === alternate) throw Error(formatProdErrorMessage(188));\n return alternate !== fiber ? null : fiber;\n }\n for (var a = fiber, b = alternate; ; ) {\n var parentA = a.return;\n if (null === parentA) break;\n var parentB = parentA.alternate;\n if (null === parentB) {\n b = parentA.return;\n if (null !== b) {\n a = b;\n continue;\n }\n break;\n }\n if (parentA.child === parentB.child) {\n for (parentB = parentA.child; parentB; ) {\n if (parentB === a) return assertIsMounted(parentA), fiber;\n if (parentB === b) return assertIsMounted(parentA), alternate;\n parentB = parentB.sibling;\n }\n throw Error(formatProdErrorMessage(188));\n }\n if (a.return !== b.return) (a = parentA), (b = parentB);\n else {\n for (var didFindChild = !1, child$0 = parentA.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentA;\n b = parentB;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentA;\n a = parentB;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) {\n for (child$0 = parentB.child; child$0; ) {\n if (child$0 === a) {\n didFindChild = !0;\n a = parentB;\n b = parentA;\n break;\n }\n if (child$0 === b) {\n didFindChild = !0;\n b = parentB;\n a = parentA;\n break;\n }\n child$0 = child$0.sibling;\n }\n if (!didFindChild) throw Error(formatProdErrorMessage(189));\n }\n }\n if (a.alternate !== b) throw Error(formatProdErrorMessage(190));\n }\n if (3 !== a.tag) throw Error(formatProdErrorMessage(188));\n return a.stateNode.current === a ? fiber : alternate;\n}\nfunction findCurrentHostFiberImpl(node) {\n var tag = node.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return node;\n for (node = node.child; null !== node; ) {\n tag = findCurrentHostFiberImpl(node);\n if (null !== tag) return tag;\n node = node.sibling;\n }\n return null;\n}\nvar assign = Object.assign,\n REACT_LEGACY_ELEMENT_TYPE = Symbol.for(\"react.element\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\nSymbol.for(\"react.scope\");\nvar REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\");\nSymbol.for(\"react.legacy_hidden\");\nSymbol.for(\"react.tracing_marker\");\nvar REACT_MEMO_CACHE_SENTINEL = Symbol.for(\"react.memo_cache_sentinel\");\nSymbol.for(\"react.view_transition\");\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nfunction getIteratorFn(maybeIterable) {\n if (null === maybeIterable || \"object\" !== typeof maybeIterable) return null;\n maybeIterable =\n (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||\n maybeIterable[\"@@iterator\"];\n return \"function\" === typeof maybeIterable ? maybeIterable : null;\n}\nvar REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\");\nfunction getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (type.$$typeof) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n}\nvar isArrayImpl = Array.isArray,\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n ReactDOMSharedInternals =\n ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n sharedNotPendingObject = {\n pending: !1,\n data: null,\n method: null,\n action: null\n },\n valueStack = [],\n index = -1;\nfunction createCursor(defaultValue) {\n return { current: defaultValue };\n}\nfunction pop(cursor) {\n 0 > index ||\n ((cursor.current = valueStack[index]), (valueStack[index] = null), index--);\n}\nfunction push(cursor, value) {\n index++;\n valueStack[index] = cursor.current;\n cursor.current = value;\n}\nvar contextStackCursor = createCursor(null),\n contextFiberStackCursor = createCursor(null),\n rootInstanceStackCursor = createCursor(null),\n hostTransitionProviderCursor = createCursor(null);\nfunction pushHostContainer(fiber, nextRootInstance) {\n push(rootInstanceStackCursor, nextRootInstance);\n push(contextFiberStackCursor, fiber);\n push(contextStackCursor, null);\n switch (nextRootInstance.nodeType) {\n case 9:\n case 11:\n fiber = (fiber = nextRootInstance.documentElement)\n ? (fiber = fiber.namespaceURI)\n ? getOwnHostContext(fiber)\n : 0\n : 0;\n break;\n default:\n if (\n ((fiber = nextRootInstance.tagName),\n (nextRootInstance = nextRootInstance.namespaceURI))\n )\n (nextRootInstance = getOwnHostContext(nextRootInstance)),\n (fiber = getChildHostContextProd(nextRootInstance, fiber));\n else\n switch (fiber) {\n case \"svg\":\n fiber = 1;\n break;\n case \"math\":\n fiber = 2;\n break;\n default:\n fiber = 0;\n }\n }\n pop(contextStackCursor);\n push(contextStackCursor, fiber);\n}\nfunction popHostContainer() {\n pop(contextStackCursor);\n pop(contextFiberStackCursor);\n pop(rootInstanceStackCursor);\n}\nfunction pushHostContext(fiber) {\n null !== fiber.memoizedState && push(hostTransitionProviderCursor, fiber);\n var context = contextStackCursor.current;\n var JSCompiler_inline_result = getChildHostContextProd(context, fiber.type);\n context !== JSCompiler_inline_result &&\n (push(contextFiberStackCursor, fiber),\n push(contextStackCursor, JSCompiler_inline_result));\n}\nfunction popHostContext(fiber) {\n contextFiberStackCursor.current === fiber &&\n (pop(contextStackCursor), pop(contextFiberStackCursor));\n hostTransitionProviderCursor.current === fiber &&\n (pop(hostTransitionProviderCursor),\n (HostTransitionContext._currentValue = sharedNotPendingObject));\n}\nvar prefix, suffix;\nfunction describeBuiltInComponentFrame(name) {\n if (void 0 === prefix)\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = (match && match[1]) || \"\";\n suffix =\n -1 < x.stack.indexOf(\"\\n at\")\n ? \" ()\"\n : -1 < x.stack.indexOf(\"@\")\n ? \"@unknown:0:0\"\n : \"\";\n }\n return \"\\n\" + prefix + name + suffix;\n}\nvar reentry = !1;\nfunction describeNativeComponentFrame(fn, construct) {\n if (!fn || reentry) return \"\";\n reentry = !0;\n var previousPrepareStackTrace = Error.prepareStackTrace;\n Error.prepareStackTrace = void 0;\n try {\n var RunInRootFrame = {\n DetermineComponentFrameRoot: function () {\n try {\n if (construct) {\n var Fake = function () {\n throw Error();\n };\n Object.defineProperty(Fake.prototype, \"props\", {\n set: function () {\n throw Error();\n }\n });\n if (\"object\" === typeof Reflect && Reflect.construct) {\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n var control = x;\n }\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x$1) {\n control = x$1;\n }\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x$2) {\n control = x$2;\n }\n (Fake = fn()) &&\n \"function\" === typeof Fake.catch &&\n Fake.catch(function () {});\n }\n } catch (sample) {\n if (sample && control && \"string\" === typeof sample.stack)\n return [sample.stack, control.stack];\n }\n return [null, null];\n }\n };\n RunInRootFrame.DetermineComponentFrameRoot.displayName =\n \"DetermineComponentFrameRoot\";\n var namePropDescriptor = Object.getOwnPropertyDescriptor(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\"\n );\n namePropDescriptor &&\n namePropDescriptor.configurable &&\n Object.defineProperty(\n RunInRootFrame.DetermineComponentFrameRoot,\n \"name\",\n { value: \"DetermineComponentFrameRoot\" }\n );\n var _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(),\n sampleStack = _RunInRootFrame$Deter[0],\n controlStack = _RunInRootFrame$Deter[1];\n if (sampleStack && controlStack) {\n var sampleLines = sampleStack.split(\"\\n\"),\n controlLines = controlStack.split(\"\\n\");\n for (\n namePropDescriptor = RunInRootFrame = 0;\n RunInRootFrame < sampleLines.length &&\n !sampleLines[RunInRootFrame].includes(\"DetermineComponentFrameRoot\");\n\n )\n RunInRootFrame++;\n for (\n ;\n namePropDescriptor < controlLines.length &&\n !controlLines[namePropDescriptor].includes(\n \"DetermineComponentFrameRoot\"\n );\n\n )\n namePropDescriptor++;\n if (\n RunInRootFrame === sampleLines.length ||\n namePropDescriptor === controlLines.length\n )\n for (\n RunInRootFrame = sampleLines.length - 1,\n namePropDescriptor = controlLines.length - 1;\n 1 <= RunInRootFrame &&\n 0 <= namePropDescriptor &&\n sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor];\n\n )\n namePropDescriptor--;\n for (\n ;\n 1 <= RunInRootFrame && 0 <= namePropDescriptor;\n RunInRootFrame--, namePropDescriptor--\n )\n if (sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {\n if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {\n do\n if (\n (RunInRootFrame--,\n namePropDescriptor--,\n 0 > namePropDescriptor ||\n sampleLines[RunInRootFrame] !==\n controlLines[namePropDescriptor])\n ) {\n var frame =\n \"\\n\" +\n sampleLines[RunInRootFrame].replace(\" at new \", \" at \");\n fn.displayName &&\n frame.includes(\"\") &&\n (frame = frame.replace(\"\", fn.displayName));\n return frame;\n }\n while (1 <= RunInRootFrame && 0 <= namePropDescriptor);\n }\n break;\n }\n }\n } finally {\n (reentry = !1), (Error.prepareStackTrace = previousPrepareStackTrace);\n }\n return (previousPrepareStackTrace = fn ? fn.displayName || fn.name : \"\")\n ? describeBuiltInComponentFrame(previousPrepareStackTrace)\n : \"\";\n}\nfunction describeFiber(fiber, childFiber) {\n switch (fiber.tag) {\n case 26:\n case 27:\n case 5:\n return describeBuiltInComponentFrame(fiber.type);\n case 16:\n return describeBuiltInComponentFrame(\"Lazy\");\n case 13:\n return fiber.child !== childFiber && null !== childFiber\n ? describeBuiltInComponentFrame(\"Suspense Fallback\")\n : describeBuiltInComponentFrame(\"Suspense\");\n case 19:\n return describeBuiltInComponentFrame(\"SuspenseList\");\n case 0:\n case 15:\n return describeNativeComponentFrame(fiber.type, !1);\n case 11:\n return describeNativeComponentFrame(fiber.type.render, !1);\n case 1:\n return describeNativeComponentFrame(fiber.type, !0);\n case 31:\n return describeBuiltInComponentFrame(\"Activity\");\n default:\n return \"\";\n }\n}\nfunction getStackByFiberInDevAndProd(workInProgress) {\n try {\n var info = \"\",\n previous = null;\n do\n (info += describeFiber(workInProgress, previous)),\n (previous = workInProgress),\n (workInProgress = workInProgress.return);\n while (workInProgress);\n return info;\n } catch (x) {\n return \"\\nError generating stack: \" + x.message + \"\\n\" + x.stack;\n }\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty,\n scheduleCallback$3 = Scheduler.unstable_scheduleCallback,\n cancelCallback$1 = Scheduler.unstable_cancelCallback,\n shouldYield = Scheduler.unstable_shouldYield,\n requestPaint = Scheduler.unstable_requestPaint,\n now = Scheduler.unstable_now,\n getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel,\n ImmediatePriority = Scheduler.unstable_ImmediatePriority,\n UserBlockingPriority = Scheduler.unstable_UserBlockingPriority,\n NormalPriority$1 = Scheduler.unstable_NormalPriority,\n LowPriority = Scheduler.unstable_LowPriority,\n IdlePriority = Scheduler.unstable_IdlePriority,\n log$1 = Scheduler.log,\n unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue,\n rendererID = null,\n injectedHook = null;\nfunction setIsStrictModeForDevtools(newIsStrictMode) {\n \"function\" === typeof log$1 && unstable_setDisableYieldValue(newIsStrictMode);\n if (injectedHook && \"function\" === typeof injectedHook.setStrictMode)\n try {\n injectedHook.setStrictMode(rendererID, newIsStrictMode);\n } catch (err) {}\n}\nvar clz32 = Math.clz32 ? Math.clz32 : clz32Fallback,\n log = Math.log,\n LN2 = Math.LN2;\nfunction clz32Fallback(x) {\n x >>>= 0;\n return 0 === x ? 32 : (31 - ((log(x) / LN2) | 0)) | 0;\n}\nvar nextTransitionUpdateLane = 256,\n nextTransitionDeferredLane = 262144,\n nextRetryLane = 4194304;\nfunction getHighestPriorityLanes(lanes) {\n var pendingSyncLanes = lanes & 42;\n if (0 !== pendingSyncLanes) return pendingSyncLanes;\n switch (lanes & -lanes) {\n case 1:\n return 1;\n case 2:\n return 2;\n case 4:\n return 4;\n case 8:\n return 8;\n case 16:\n return 16;\n case 32:\n return 32;\n case 64:\n return 64;\n case 128:\n return 128;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n return lanes & 261888;\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return lanes & 3932160;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return lanes & 62914560;\n case 67108864:\n return 67108864;\n case 134217728:\n return 134217728;\n case 268435456:\n return 268435456;\n case 536870912:\n return 536870912;\n case 1073741824:\n return 0;\n default:\n return lanes;\n }\n}\nfunction getNextLanes(root, wipLanes, rootHasPendingCommit) {\n var pendingLanes = root.pendingLanes;\n if (0 === pendingLanes) return 0;\n var nextLanes = 0,\n suspendedLanes = root.suspendedLanes,\n pingedLanes = root.pingedLanes;\n root = root.warmLanes;\n var nonIdlePendingLanes = pendingLanes & 134217727;\n 0 !== nonIdlePendingLanes\n ? ((pendingLanes = nonIdlePendingLanes & ~suspendedLanes),\n 0 !== pendingLanes\n ? (nextLanes = getHighestPriorityLanes(pendingLanes))\n : ((pingedLanes &= nonIdlePendingLanes),\n 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = nonIdlePendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit)))))\n : ((nonIdlePendingLanes = pendingLanes & ~suspendedLanes),\n 0 !== nonIdlePendingLanes\n ? (nextLanes = getHighestPriorityLanes(nonIdlePendingLanes))\n : 0 !== pingedLanes\n ? (nextLanes = getHighestPriorityLanes(pingedLanes))\n : rootHasPendingCommit ||\n ((rootHasPendingCommit = pendingLanes & ~root),\n 0 !== rootHasPendingCommit &&\n (nextLanes = getHighestPriorityLanes(rootHasPendingCommit))));\n return 0 === nextLanes\n ? 0\n : 0 !== wipLanes &&\n wipLanes !== nextLanes &&\n 0 === (wipLanes & suspendedLanes) &&\n ((suspendedLanes = nextLanes & -nextLanes),\n (rootHasPendingCommit = wipLanes & -wipLanes),\n suspendedLanes >= rootHasPendingCommit ||\n (32 === suspendedLanes && 0 !== (rootHasPendingCommit & 4194048)))\n ? wipLanes\n : nextLanes;\n}\nfunction checkIfRootIsPrerendering(root, renderLanes) {\n return (\n 0 ===\n (root.pendingLanes &\n ~(root.suspendedLanes & ~root.pingedLanes) &\n renderLanes)\n );\n}\nfunction computeExpirationTime(lane, currentTime) {\n switch (lane) {\n case 1:\n case 2:\n case 4:\n case 8:\n case 64:\n return currentTime + 250;\n case 16:\n case 32:\n case 128:\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n return currentTime + 5e3;\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n return -1;\n case 67108864:\n case 134217728:\n case 268435456:\n case 536870912:\n case 1073741824:\n return -1;\n default:\n return -1;\n }\n}\nfunction claimNextRetryLane() {\n var lane = nextRetryLane;\n nextRetryLane <<= 1;\n 0 === (nextRetryLane & 62914560) && (nextRetryLane = 4194304);\n return lane;\n}\nfunction createLaneMap(initial) {\n for (var laneMap = [], i = 0; 31 > i; i++) laneMap.push(initial);\n return laneMap;\n}\nfunction markRootUpdated$1(root, updateLane) {\n root.pendingLanes |= updateLane;\n 268435456 !== updateLane &&\n ((root.suspendedLanes = 0), (root.pingedLanes = 0), (root.warmLanes = 0));\n}\nfunction markRootFinished(\n root,\n finishedLanes,\n remainingLanes,\n spawnedLane,\n updatedLanes,\n suspendedRetryLanes\n) {\n var previouslyPendingLanes = root.pendingLanes;\n root.pendingLanes = remainingLanes;\n root.suspendedLanes = 0;\n root.pingedLanes = 0;\n root.warmLanes = 0;\n root.expiredLanes &= remainingLanes;\n root.entangledLanes &= remainingLanes;\n root.errorRecoveryDisabledLanes &= remainingLanes;\n root.shellSuspendCounter = 0;\n var entanglements = root.entanglements,\n expirationTimes = root.expirationTimes,\n hiddenUpdates = root.hiddenUpdates;\n for (\n remainingLanes = previouslyPendingLanes & ~remainingLanes;\n 0 < remainingLanes;\n\n ) {\n var index$7 = 31 - clz32(remainingLanes),\n lane = 1 << index$7;\n entanglements[index$7] = 0;\n expirationTimes[index$7] = -1;\n var hiddenUpdatesForLane = hiddenUpdates[index$7];\n if (null !== hiddenUpdatesForLane)\n for (\n hiddenUpdates[index$7] = null, index$7 = 0;\n index$7 < hiddenUpdatesForLane.length;\n index$7++\n ) {\n var update = hiddenUpdatesForLane[index$7];\n null !== update && (update.lane &= -536870913);\n }\n remainingLanes &= ~lane;\n }\n 0 !== spawnedLane && markSpawnedDeferredLane(root, spawnedLane, 0);\n 0 !== suspendedRetryLanes &&\n 0 === updatedLanes &&\n 0 !== root.tag &&\n (root.suspendedLanes |=\n suspendedRetryLanes & ~(previouslyPendingLanes & ~finishedLanes));\n}\nfunction markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {\n root.pendingLanes |= spawnedLane;\n root.suspendedLanes &= ~spawnedLane;\n var spawnedLaneIndex = 31 - clz32(spawnedLane);\n root.entangledLanes |= spawnedLane;\n root.entanglements[spawnedLaneIndex] =\n root.entanglements[spawnedLaneIndex] |\n 1073741824 |\n (entangledLanes & 261930);\n}\nfunction markRootEntangled(root, entangledLanes) {\n var rootEntangledLanes = (root.entangledLanes |= entangledLanes);\n for (root = root.entanglements; rootEntangledLanes; ) {\n var index$8 = 31 - clz32(rootEntangledLanes),\n lane = 1 << index$8;\n (lane & entangledLanes) | (root[index$8] & entangledLanes) &&\n (root[index$8] |= entangledLanes);\n rootEntangledLanes &= ~lane;\n }\n}\nfunction getBumpedLaneForHydration(root, renderLanes) {\n var renderLane = renderLanes & -renderLanes;\n renderLane =\n 0 !== (renderLane & 42) ? 1 : getBumpedLaneForHydrationByLane(renderLane);\n return 0 !== (renderLane & (root.suspendedLanes | renderLanes))\n ? 0\n : renderLane;\n}\nfunction getBumpedLaneForHydrationByLane(lane) {\n switch (lane) {\n case 2:\n lane = 1;\n break;\n case 8:\n lane = 4;\n break;\n case 32:\n lane = 16;\n break;\n case 256:\n case 512:\n case 1024:\n case 2048:\n case 4096:\n case 8192:\n case 16384:\n case 32768:\n case 65536:\n case 131072:\n case 262144:\n case 524288:\n case 1048576:\n case 2097152:\n case 4194304:\n case 8388608:\n case 16777216:\n case 33554432:\n lane = 128;\n break;\n case 268435456:\n lane = 134217728;\n break;\n default:\n lane = 0;\n }\n return lane;\n}\nfunction lanesToEventPriority(lanes) {\n lanes &= -lanes;\n return 2 < lanes\n ? 8 < lanes\n ? 0 !== (lanes & 134217727)\n ? 32\n : 268435456\n : 8\n : 2;\n}\nfunction resolveUpdatePriority() {\n var updatePriority = ReactDOMSharedInternals.p;\n if (0 !== updatePriority) return updatePriority;\n updatePriority = window.event;\n return void 0 === updatePriority ? 32 : getEventPriority(updatePriority.type);\n}\nfunction runWithPriority(priority, fn) {\n var previousPriority = ReactDOMSharedInternals.p;\n try {\n return (ReactDOMSharedInternals.p = priority), fn();\n } finally {\n ReactDOMSharedInternals.p = previousPriority;\n }\n}\nvar randomKey = Math.random().toString(36).slice(2),\n internalInstanceKey = \"__reactFiber$\" + randomKey,\n internalPropsKey = \"__reactProps$\" + randomKey,\n internalContainerInstanceKey = \"__reactContainer$\" + randomKey,\n internalEventHandlersKey = \"__reactEvents$\" + randomKey,\n internalEventHandlerListenersKey = \"__reactListeners$\" + randomKey,\n internalEventHandlesSetKey = \"__reactHandles$\" + randomKey,\n internalRootNodeResourcesKey = \"__reactResources$\" + randomKey,\n internalHoistableMarker = \"__reactMarker$\" + randomKey;\nfunction detachDeletedInstance(node) {\n delete node[internalInstanceKey];\n delete node[internalPropsKey];\n delete node[internalEventHandlersKey];\n delete node[internalEventHandlerListenersKey];\n delete node[internalEventHandlesSetKey];\n}\nfunction getClosestInstanceFromNode(targetNode) {\n var targetInst = targetNode[internalInstanceKey];\n if (targetInst) return targetInst;\n for (var parentNode = targetNode.parentNode; parentNode; ) {\n if (\n (targetInst =\n parentNode[internalContainerInstanceKey] ||\n parentNode[internalInstanceKey])\n ) {\n parentNode = targetInst.alternate;\n if (\n null !== targetInst.child ||\n (null !== parentNode && null !== parentNode.child)\n )\n for (\n targetNode = getParentHydrationBoundary(targetNode);\n null !== targetNode;\n\n ) {\n if ((parentNode = targetNode[internalInstanceKey])) return parentNode;\n targetNode = getParentHydrationBoundary(targetNode);\n }\n return targetInst;\n }\n targetNode = parentNode;\n parentNode = targetNode.parentNode;\n }\n return null;\n}\nfunction getInstanceFromNode(node) {\n if (\n (node = node[internalInstanceKey] || node[internalContainerInstanceKey])\n ) {\n var tag = node.tag;\n if (\n 5 === tag ||\n 6 === tag ||\n 13 === tag ||\n 31 === tag ||\n 26 === tag ||\n 27 === tag ||\n 3 === tag\n )\n return node;\n }\n return null;\n}\nfunction getNodeFromInstance(inst) {\n var tag = inst.tag;\n if (5 === tag || 26 === tag || 27 === tag || 6 === tag) return inst.stateNode;\n throw Error(formatProdErrorMessage(33));\n}\nfunction getResourcesFromRoot(root) {\n var resources = root[internalRootNodeResourcesKey];\n resources ||\n (resources = root[internalRootNodeResourcesKey] =\n { hoistableStyles: new Map(), hoistableScripts: new Map() });\n return resources;\n}\nfunction markNodeAsHoistable(node) {\n node[internalHoistableMarker] = !0;\n}\nvar allNativeEvents = new Set(),\n registrationNameDependencies = {};\nfunction registerTwoPhaseEvent(registrationName, dependencies) {\n registerDirectEvent(registrationName, dependencies);\n registerDirectEvent(registrationName + \"Capture\", dependencies);\n}\nfunction registerDirectEvent(registrationName, dependencies) {\n registrationNameDependencies[registrationName] = dependencies;\n for (\n registrationName = 0;\n registrationName < dependencies.length;\n registrationName++\n )\n allNativeEvents.add(dependencies[registrationName]);\n}\nvar VALID_ATTRIBUTE_NAME_REGEX = RegExp(\n \"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"\n ),\n illegalAttributeNameCache = {},\n validatedAttributeNameCache = {};\nfunction isAttributeNameSafe(attributeName) {\n if (hasOwnProperty.call(validatedAttributeNameCache, attributeName))\n return !0;\n if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) return !1;\n if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName))\n return (validatedAttributeNameCache[attributeName] = !0);\n illegalAttributeNameCache[attributeName] = !0;\n return !1;\n}\nfunction setValueForAttribute(node, name, value) {\n if (isAttributeNameSafe(name))\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n node.removeAttribute(name);\n return;\n case \"boolean\":\n var prefix$10 = name.toLowerCase().slice(0, 5);\n if (\"data-\" !== prefix$10 && \"aria-\" !== prefix$10) {\n node.removeAttribute(name);\n return;\n }\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForKnownAttribute(node, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttribute(name, \"\" + value);\n }\n}\nfunction setValueForNamespacedAttribute(node, namespace, name, value) {\n if (null === value) node.removeAttribute(name);\n else {\n switch (typeof value) {\n case \"undefined\":\n case \"function\":\n case \"symbol\":\n case \"boolean\":\n node.removeAttribute(name);\n return;\n }\n node.setAttributeNS(namespace, name, \"\" + value);\n }\n}\nfunction getToStringValue(value) {\n switch (typeof value) {\n case \"bigint\":\n case \"boolean\":\n case \"number\":\n case \"string\":\n case \"undefined\":\n return value;\n case \"object\":\n return value;\n default:\n return \"\";\n }\n}\nfunction isCheckable(elem) {\n var type = elem.type;\n return (\n (elem = elem.nodeName) &&\n \"input\" === elem.toLowerCase() &&\n (\"checkbox\" === type || \"radio\" === type)\n );\n}\nfunction trackValueOnNode(node, valueField, currentValue) {\n var descriptor = Object.getOwnPropertyDescriptor(\n node.constructor.prototype,\n valueField\n );\n if (\n !node.hasOwnProperty(valueField) &&\n \"undefined\" !== typeof descriptor &&\n \"function\" === typeof descriptor.get &&\n \"function\" === typeof descriptor.set\n ) {\n var get = descriptor.get,\n set = descriptor.set;\n Object.defineProperty(node, valueField, {\n configurable: !0,\n get: function () {\n return get.call(this);\n },\n set: function (value) {\n currentValue = \"\" + value;\n set.call(this, value);\n }\n });\n Object.defineProperty(node, valueField, {\n enumerable: descriptor.enumerable\n });\n return {\n getValue: function () {\n return currentValue;\n },\n setValue: function (value) {\n currentValue = \"\" + value;\n },\n stopTracking: function () {\n node._valueTracker = null;\n delete node[valueField];\n }\n };\n }\n}\nfunction track(node) {\n if (!node._valueTracker) {\n var valueField = isCheckable(node) ? \"checked\" : \"value\";\n node._valueTracker = trackValueOnNode(\n node,\n valueField,\n \"\" + node[valueField]\n );\n }\n}\nfunction updateValueIfChanged(node) {\n if (!node) return !1;\n var tracker = node._valueTracker;\n if (!tracker) return !0;\n var lastValue = tracker.getValue();\n var value = \"\";\n node &&\n (value = isCheckable(node)\n ? node.checked\n ? \"true\"\n : \"false\"\n : node.value);\n node = value;\n return node !== lastValue ? (tracker.setValue(node), !0) : !1;\n}\nfunction getActiveElement(doc) {\n doc = doc || (\"undefined\" !== typeof document ? document : void 0);\n if (\"undefined\" === typeof doc) return null;\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\nvar escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\\n\"\\\\]/g;\nfunction escapeSelectorAttributeValueInsideDoubleQuotes(value) {\n return value.replace(\n escapeSelectorAttributeValueInsideDoubleQuotesRegex,\n function (ch) {\n return \"\\\\\" + ch.charCodeAt(0).toString(16) + \" \";\n }\n );\n}\nfunction updateInput(\n element,\n value,\n defaultValue,\n lastDefaultValue,\n checked,\n defaultChecked,\n type,\n name\n) {\n element.name = \"\";\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type\n ? (element.type = type)\n : element.removeAttribute(\"type\");\n if (null != value)\n if (\"number\" === type) {\n if ((0 === value && \"\" === element.value) || element.value != value)\n element.value = \"\" + getToStringValue(value);\n } else\n element.value !== \"\" + getToStringValue(value) &&\n (element.value = \"\" + getToStringValue(value));\n else\n (\"submit\" !== type && \"reset\" !== type) || element.removeAttribute(\"value\");\n null != value\n ? setDefaultValue(element, type, getToStringValue(value))\n : null != defaultValue\n ? setDefaultValue(element, type, getToStringValue(defaultValue))\n : null != lastDefaultValue && element.removeAttribute(\"value\");\n null == checked &&\n null != defaultChecked &&\n (element.defaultChecked = !!defaultChecked);\n null != checked &&\n (element.checked =\n checked && \"function\" !== typeof checked && \"symbol\" !== typeof checked);\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name\n ? (element.name = \"\" + getToStringValue(name))\n : element.removeAttribute(\"name\");\n}\nfunction initInput(\n element,\n value,\n defaultValue,\n checked,\n defaultChecked,\n type,\n name,\n isHydrating\n) {\n null != type &&\n \"function\" !== typeof type &&\n \"symbol\" !== typeof type &&\n \"boolean\" !== typeof type &&\n (element.type = type);\n if (null != value || null != defaultValue) {\n if (\n !(\n (\"submit\" !== type && \"reset\" !== type) ||\n (void 0 !== value && null !== value)\n )\n ) {\n track(element);\n return;\n }\n defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n value = null != value ? \"\" + getToStringValue(value) : defaultValue;\n isHydrating || value === element.value || (element.value = value);\n element.defaultValue = value;\n }\n checked = null != checked ? checked : defaultChecked;\n checked =\n \"function\" !== typeof checked && \"symbol\" !== typeof checked && !!checked;\n element.checked = isHydrating ? element.checked : !!checked;\n element.defaultChecked = !!checked;\n null != name &&\n \"function\" !== typeof name &&\n \"symbol\" !== typeof name &&\n \"boolean\" !== typeof name &&\n (element.name = name);\n track(element);\n}\nfunction setDefaultValue(node, type, value) {\n (\"number\" === type && getActiveElement(node.ownerDocument) === node) ||\n node.defaultValue === \"\" + value ||\n (node.defaultValue = \"\" + value);\n}\nfunction updateOptions(node, multiple, propValue, setDefaultSelected) {\n node = node.options;\n if (multiple) {\n multiple = {};\n for (var i = 0; i < propValue.length; i++)\n multiple[\"$\" + propValue[i]] = !0;\n for (propValue = 0; propValue < node.length; propValue++)\n (i = multiple.hasOwnProperty(\"$\" + node[propValue].value)),\n node[propValue].selected !== i && (node[propValue].selected = i),\n i && setDefaultSelected && (node[propValue].defaultSelected = !0);\n } else {\n propValue = \"\" + getToStringValue(propValue);\n multiple = null;\n for (i = 0; i < node.length; i++) {\n if (node[i].value === propValue) {\n node[i].selected = !0;\n setDefaultSelected && (node[i].defaultSelected = !0);\n return;\n }\n null !== multiple || node[i].disabled || (multiple = node[i]);\n }\n null !== multiple && (multiple.selected = !0);\n }\n}\nfunction updateTextarea(element, value, defaultValue) {\n if (\n null != value &&\n ((value = \"\" + getToStringValue(value)),\n value !== element.value && (element.value = value),\n null == defaultValue)\n ) {\n element.defaultValue !== value && (element.defaultValue = value);\n return;\n }\n element.defaultValue =\n null != defaultValue ? \"\" + getToStringValue(defaultValue) : \"\";\n}\nfunction initTextarea(element, value, defaultValue, children) {\n if (null == value) {\n if (null != children) {\n if (null != defaultValue) throw Error(formatProdErrorMessage(92));\n if (isArrayImpl(children)) {\n if (1 < children.length) throw Error(formatProdErrorMessage(93));\n children = children[0];\n }\n defaultValue = children;\n }\n null == defaultValue && (defaultValue = \"\");\n value = defaultValue;\n }\n defaultValue = getToStringValue(value);\n element.defaultValue = defaultValue;\n children = element.textContent;\n children === defaultValue &&\n \"\" !== children &&\n null !== children &&\n (element.value = children);\n track(element);\n}\nfunction setTextContent(node, text) {\n if (text) {\n var firstChild = node.firstChild;\n if (\n firstChild &&\n firstChild === node.lastChild &&\n 3 === firstChild.nodeType\n ) {\n firstChild.nodeValue = text;\n return;\n }\n }\n node.textContent = text;\n}\nvar unitlessNumbers = new Set(\n \"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\n \" \"\n )\n);\nfunction setValueForStyle(style, styleName, value) {\n var isCustomProperty = 0 === styleName.indexOf(\"--\");\n null == value || \"boolean\" === typeof value || \"\" === value\n ? isCustomProperty\n ? style.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (style.cssFloat = \"\")\n : (style[styleName] = \"\")\n : isCustomProperty\n ? style.setProperty(styleName, value)\n : \"number\" !== typeof value ||\n 0 === value ||\n unitlessNumbers.has(styleName)\n ? \"float\" === styleName\n ? (style.cssFloat = value)\n : (style[styleName] = (\"\" + value).trim())\n : (style[styleName] = value + \"px\");\n}\nfunction setValueForStyles(node, styles, prevStyles) {\n if (null != styles && \"object\" !== typeof styles)\n throw Error(formatProdErrorMessage(62));\n node = node.style;\n if (null != prevStyles) {\n for (var styleName in prevStyles)\n !prevStyles.hasOwnProperty(styleName) ||\n (null != styles && styles.hasOwnProperty(styleName)) ||\n (0 === styleName.indexOf(\"--\")\n ? node.setProperty(styleName, \"\")\n : \"float\" === styleName\n ? (node.cssFloat = \"\")\n : (node[styleName] = \"\"));\n for (var styleName$16 in styles)\n (styleName = styles[styleName$16]),\n styles.hasOwnProperty(styleName$16) &&\n prevStyles[styleName$16] !== styleName &&\n setValueForStyle(node, styleName$16, styleName);\n } else\n for (var styleName$17 in styles)\n styles.hasOwnProperty(styleName$17) &&\n setValueForStyle(node, styleName$17, styles[styleName$17]);\n}\nfunction isCustomElement(tagName) {\n if (-1 === tagName.indexOf(\"-\")) return !1;\n switch (tagName) {\n case \"annotation-xml\":\n case \"color-profile\":\n case \"font-face\":\n case \"font-face-src\":\n case \"font-face-uri\":\n case \"font-face-format\":\n case \"font-face-name\":\n case \"missing-glyph\":\n return !1;\n default:\n return !0;\n }\n}\nvar aliases = new Map([\n [\"acceptCharset\", \"accept-charset\"],\n [\"htmlFor\", \"for\"],\n [\"httpEquiv\", \"http-equiv\"],\n [\"crossOrigin\", \"crossorigin\"],\n [\"accentHeight\", \"accent-height\"],\n [\"alignmentBaseline\", \"alignment-baseline\"],\n [\"arabicForm\", \"arabic-form\"],\n [\"baselineShift\", \"baseline-shift\"],\n [\"capHeight\", \"cap-height\"],\n [\"clipPath\", \"clip-path\"],\n [\"clipRule\", \"clip-rule\"],\n [\"colorInterpolation\", \"color-interpolation\"],\n [\"colorInterpolationFilters\", \"color-interpolation-filters\"],\n [\"colorProfile\", \"color-profile\"],\n [\"colorRendering\", \"color-rendering\"],\n [\"dominantBaseline\", \"dominant-baseline\"],\n [\"enableBackground\", \"enable-background\"],\n [\"fillOpacity\", \"fill-opacity\"],\n [\"fillRule\", \"fill-rule\"],\n [\"floodColor\", \"flood-color\"],\n [\"floodOpacity\", \"flood-opacity\"],\n [\"fontFamily\", \"font-family\"],\n [\"fontSize\", \"font-size\"],\n [\"fontSizeAdjust\", \"font-size-adjust\"],\n [\"fontStretch\", \"font-stretch\"],\n [\"fontStyle\", \"font-style\"],\n [\"fontVariant\", \"font-variant\"],\n [\"fontWeight\", \"font-weight\"],\n [\"glyphName\", \"glyph-name\"],\n [\"glyphOrientationHorizontal\", \"glyph-orientation-horizontal\"],\n [\"glyphOrientationVertical\", \"glyph-orientation-vertical\"],\n [\"horizAdvX\", \"horiz-adv-x\"],\n [\"horizOriginX\", \"horiz-origin-x\"],\n [\"imageRendering\", \"image-rendering\"],\n [\"letterSpacing\", \"letter-spacing\"],\n [\"lightingColor\", \"lighting-color\"],\n [\"markerEnd\", \"marker-end\"],\n [\"markerMid\", \"marker-mid\"],\n [\"markerStart\", \"marker-start\"],\n [\"overlinePosition\", \"overline-position\"],\n [\"overlineThickness\", \"overline-thickness\"],\n [\"paintOrder\", \"paint-order\"],\n [\"panose-1\", \"panose-1\"],\n [\"pointerEvents\", \"pointer-events\"],\n [\"renderingIntent\", \"rendering-intent\"],\n [\"shapeRendering\", \"shape-rendering\"],\n [\"stopColor\", \"stop-color\"],\n [\"stopOpacity\", \"stop-opacity\"],\n [\"strikethroughPosition\", \"strikethrough-position\"],\n [\"strikethroughThickness\", \"strikethrough-thickness\"],\n [\"strokeDasharray\", \"stroke-dasharray\"],\n [\"strokeDashoffset\", \"stroke-dashoffset\"],\n [\"strokeLinecap\", \"stroke-linecap\"],\n [\"strokeLinejoin\", \"stroke-linejoin\"],\n [\"strokeMiterlimit\", \"stroke-miterlimit\"],\n [\"strokeOpacity\", \"stroke-opacity\"],\n [\"strokeWidth\", \"stroke-width\"],\n [\"textAnchor\", \"text-anchor\"],\n [\"textDecoration\", \"text-decoration\"],\n [\"textRendering\", \"text-rendering\"],\n [\"transformOrigin\", \"transform-origin\"],\n [\"underlinePosition\", \"underline-position\"],\n [\"underlineThickness\", \"underline-thickness\"],\n [\"unicodeBidi\", \"unicode-bidi\"],\n [\"unicodeRange\", \"unicode-range\"],\n [\"unitsPerEm\", \"units-per-em\"],\n [\"vAlphabetic\", \"v-alphabetic\"],\n [\"vHanging\", \"v-hanging\"],\n [\"vIdeographic\", \"v-ideographic\"],\n [\"vMathematical\", \"v-mathematical\"],\n [\"vectorEffect\", \"vector-effect\"],\n [\"vertAdvY\", \"vert-adv-y\"],\n [\"vertOriginX\", \"vert-origin-x\"],\n [\"vertOriginY\", \"vert-origin-y\"],\n [\"wordSpacing\", \"word-spacing\"],\n [\"writingMode\", \"writing-mode\"],\n [\"xmlnsXlink\", \"xmlns:xlink\"],\n [\"xHeight\", \"x-height\"]\n ]),\n isJavaScriptProtocol =\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;\nfunction sanitizeURL(url) {\n return isJavaScriptProtocol.test(\"\" + url)\n ? \"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\"\n : url;\n}\nfunction noop$1() {}\nvar currentReplayingEvent = null;\nfunction getEventTarget(nativeEvent) {\n nativeEvent = nativeEvent.target || nativeEvent.srcElement || window;\n nativeEvent.correspondingUseElement &&\n (nativeEvent = nativeEvent.correspondingUseElement);\n return 3 === nativeEvent.nodeType ? nativeEvent.parentNode : nativeEvent;\n}\nvar restoreTarget = null,\n restoreQueue = null;\nfunction restoreStateOfTarget(target) {\n var internalInstance = getInstanceFromNode(target);\n if (internalInstance && (target = internalInstance.stateNode)) {\n var props = target[internalPropsKey] || null;\n a: switch (((target = internalInstance.stateNode), internalInstance.type)) {\n case \"input\":\n updateInput(\n target,\n props.value,\n props.defaultValue,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name\n );\n internalInstance = props.name;\n if (\"radio\" === props.type && null != internalInstance) {\n for (props = target; props.parentNode; ) props = props.parentNode;\n props = props.querySelectorAll(\n 'input[name=\"' +\n escapeSelectorAttributeValueInsideDoubleQuotes(\n \"\" + internalInstance\n ) +\n '\"][type=\"radio\"]'\n );\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n ) {\n var otherNode = props[internalInstance];\n if (otherNode !== target && otherNode.form === target.form) {\n var otherProps = otherNode[internalPropsKey] || null;\n if (!otherProps) throw Error(formatProdErrorMessage(90));\n updateInput(\n otherNode,\n otherProps.value,\n otherProps.defaultValue,\n otherProps.defaultValue,\n otherProps.checked,\n otherProps.defaultChecked,\n otherProps.type,\n otherProps.name\n );\n }\n }\n for (\n internalInstance = 0;\n internalInstance < props.length;\n internalInstance++\n )\n (otherNode = props[internalInstance]),\n otherNode.form === target.form && updateValueIfChanged(otherNode);\n }\n break a;\n case \"textarea\":\n updateTextarea(target, props.value, props.defaultValue);\n break a;\n case \"select\":\n (internalInstance = props.value),\n null != internalInstance &&\n updateOptions(target, !!props.multiple, internalInstance, !1);\n }\n }\n}\nvar isInsideEventHandler = !1;\nfunction batchedUpdates$1(fn, a, b) {\n if (isInsideEventHandler) return fn(a, b);\n isInsideEventHandler = !0;\n try {\n var JSCompiler_inline_result = fn(a);\n return JSCompiler_inline_result;\n } finally {\n if (\n ((isInsideEventHandler = !1),\n null !== restoreTarget || null !== restoreQueue)\n )\n if (\n (flushSyncWork$1(),\n restoreTarget &&\n ((a = restoreTarget),\n (fn = restoreQueue),\n (restoreQueue = restoreTarget = null),\n restoreStateOfTarget(a),\n fn))\n )\n for (a = 0; a < fn.length; a++) restoreStateOfTarget(fn[a]);\n }\n}\nfunction getListener(inst, registrationName) {\n var stateNode = inst.stateNode;\n if (null === stateNode) return null;\n var props = stateNode[internalPropsKey] || null;\n if (null === props) return null;\n stateNode = props[registrationName];\n a: switch (registrationName) {\n case \"onClick\":\n case \"onClickCapture\":\n case \"onDoubleClick\":\n case \"onDoubleClickCapture\":\n case \"onMouseDown\":\n case \"onMouseDownCapture\":\n case \"onMouseMove\":\n case \"onMouseMoveCapture\":\n case \"onMouseUp\":\n case \"onMouseUpCapture\":\n case \"onMouseEnter\":\n (props = !props.disabled) ||\n ((inst = inst.type),\n (props = !(\n \"button\" === inst ||\n \"input\" === inst ||\n \"select\" === inst ||\n \"textarea\" === inst\n )));\n inst = !props;\n break a;\n default:\n inst = !1;\n }\n if (inst) return null;\n if (stateNode && \"function\" !== typeof stateNode)\n throw Error(\n formatProdErrorMessage(231, registrationName, typeof stateNode)\n );\n return stateNode;\n}\nvar canUseDOM = !(\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ),\n passiveBrowserEventsSupported = !1;\nif (canUseDOM)\n try {\n var options = {};\n Object.defineProperty(options, \"passive\", {\n get: function () {\n passiveBrowserEventsSupported = !0;\n }\n });\n window.addEventListener(\"test\", options, options);\n window.removeEventListener(\"test\", options, options);\n } catch (e) {\n passiveBrowserEventsSupported = !1;\n }\nvar root = null,\n startText = null,\n fallbackText = null;\nfunction getData() {\n if (fallbackText) return fallbackText;\n var start,\n startValue = startText,\n startLength = startValue.length,\n end,\n endValue = \"value\" in root ? root.value : root.textContent,\n endLength = endValue.length;\n for (\n start = 0;\n start < startLength && startValue[start] === endValue[start];\n start++\n );\n var minEnd = startLength - start;\n for (\n end = 1;\n end <= minEnd &&\n startValue[startLength - end] === endValue[endLength - end];\n end++\n );\n return (fallbackText = endValue.slice(start, 1 < end ? 1 - end : void 0));\n}\nfunction getEventCharCode(nativeEvent) {\n var keyCode = nativeEvent.keyCode;\n \"charCode\" in nativeEvent\n ? ((nativeEvent = nativeEvent.charCode),\n 0 === nativeEvent && 13 === keyCode && (nativeEvent = 13))\n : (nativeEvent = keyCode);\n 10 === nativeEvent && (nativeEvent = 13);\n return 32 <= nativeEvent || 13 === nativeEvent ? nativeEvent : 0;\n}\nfunction functionThatReturnsTrue() {\n return !0;\n}\nfunction functionThatReturnsFalse() {\n return !1;\n}\nfunction createSyntheticEvent(Interface) {\n function SyntheticBaseEvent(\n reactName,\n reactEventType,\n targetInst,\n nativeEvent,\n nativeEventTarget\n ) {\n this._reactName = reactName;\n this._targetInst = targetInst;\n this.type = reactEventType;\n this.nativeEvent = nativeEvent;\n this.target = nativeEventTarget;\n this.currentTarget = null;\n for (var propName in Interface)\n Interface.hasOwnProperty(propName) &&\n ((reactName = Interface[propName]),\n (this[propName] = reactName\n ? reactName(nativeEvent)\n : nativeEvent[propName]));\n this.isDefaultPrevented = (\n null != nativeEvent.defaultPrevented\n ? nativeEvent.defaultPrevented\n : !1 === nativeEvent.returnValue\n )\n ? functionThatReturnsTrue\n : functionThatReturnsFalse;\n this.isPropagationStopped = functionThatReturnsFalse;\n return this;\n }\n assign(SyntheticBaseEvent.prototype, {\n preventDefault: function () {\n this.defaultPrevented = !0;\n var event = this.nativeEvent;\n event &&\n (event.preventDefault\n ? event.preventDefault()\n : \"unknown\" !== typeof event.returnValue && (event.returnValue = !1),\n (this.isDefaultPrevented = functionThatReturnsTrue));\n },\n stopPropagation: function () {\n var event = this.nativeEvent;\n event &&\n (event.stopPropagation\n ? event.stopPropagation()\n : \"unknown\" !== typeof event.cancelBubble &&\n (event.cancelBubble = !0),\n (this.isPropagationStopped = functionThatReturnsTrue));\n },\n persist: function () {},\n isPersistent: functionThatReturnsTrue\n });\n return SyntheticBaseEvent;\n}\nvar EventInterface = {\n eventPhase: 0,\n bubbles: 0,\n cancelable: 0,\n timeStamp: function (event) {\n return event.timeStamp || Date.now();\n },\n defaultPrevented: 0,\n isTrusted: 0\n },\n SyntheticEvent = createSyntheticEvent(EventInterface),\n UIEventInterface = assign({}, EventInterface, { view: 0, detail: 0 }),\n SyntheticUIEvent = createSyntheticEvent(UIEventInterface),\n lastMovementX,\n lastMovementY,\n lastMouseEvent,\n MouseEventInterface = assign({}, UIEventInterface, {\n screenX: 0,\n screenY: 0,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n getModifierState: getEventModifierState,\n button: 0,\n buttons: 0,\n relatedTarget: function (event) {\n return void 0 === event.relatedTarget\n ? event.fromElement === event.srcElement\n ? event.toElement\n : event.fromElement\n : event.relatedTarget;\n },\n movementX: function (event) {\n if (\"movementX\" in event) return event.movementX;\n event !== lastMouseEvent &&\n (lastMouseEvent && \"mousemove\" === event.type\n ? ((lastMovementX = event.screenX - lastMouseEvent.screenX),\n (lastMovementY = event.screenY - lastMouseEvent.screenY))\n : (lastMovementY = lastMovementX = 0),\n (lastMouseEvent = event));\n return lastMovementX;\n },\n movementY: function (event) {\n return \"movementY\" in event ? event.movementY : lastMovementY;\n }\n }),\n SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface),\n DragEventInterface = assign({}, MouseEventInterface, { dataTransfer: 0 }),\n SyntheticDragEvent = createSyntheticEvent(DragEventInterface),\n FocusEventInterface = assign({}, UIEventInterface, { relatedTarget: 0 }),\n SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface),\n AnimationEventInterface = assign({}, EventInterface, {\n animationName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface),\n ClipboardEventInterface = assign({}, EventInterface, {\n clipboardData: function (event) {\n return \"clipboardData\" in event\n ? event.clipboardData\n : window.clipboardData;\n }\n }),\n SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface),\n CompositionEventInterface = assign({}, EventInterface, { data: 0 }),\n SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface),\n normalizeKey = {\n Esc: \"Escape\",\n Spacebar: \" \",\n Left: \"ArrowLeft\",\n Up: \"ArrowUp\",\n Right: \"ArrowRight\",\n Down: \"ArrowDown\",\n Del: \"Delete\",\n Win: \"OS\",\n Menu: \"ContextMenu\",\n Apps: \"ContextMenu\",\n Scroll: \"ScrollLock\",\n MozPrintableKey: \"Unidentified\"\n },\n translateToKey = {\n 8: \"Backspace\",\n 9: \"Tab\",\n 12: \"Clear\",\n 13: \"Enter\",\n 16: \"Shift\",\n 17: \"Control\",\n 18: \"Alt\",\n 19: \"Pause\",\n 20: \"CapsLock\",\n 27: \"Escape\",\n 32: \" \",\n 33: \"PageUp\",\n 34: \"PageDown\",\n 35: \"End\",\n 36: \"Home\",\n 37: \"ArrowLeft\",\n 38: \"ArrowUp\",\n 39: \"ArrowRight\",\n 40: \"ArrowDown\",\n 45: \"Insert\",\n 46: \"Delete\",\n 112: \"F1\",\n 113: \"F2\",\n 114: \"F3\",\n 115: \"F4\",\n 116: \"F5\",\n 117: \"F6\",\n 118: \"F7\",\n 119: \"F8\",\n 120: \"F9\",\n 121: \"F10\",\n 122: \"F11\",\n 123: \"F12\",\n 144: \"NumLock\",\n 145: \"ScrollLock\",\n 224: \"Meta\"\n },\n modifierKeyToProp = {\n Alt: \"altKey\",\n Control: \"ctrlKey\",\n Meta: \"metaKey\",\n Shift: \"shiftKey\"\n };\nfunction modifierStateGetter(keyArg) {\n var nativeEvent = this.nativeEvent;\n return nativeEvent.getModifierState\n ? nativeEvent.getModifierState(keyArg)\n : (keyArg = modifierKeyToProp[keyArg])\n ? !!nativeEvent[keyArg]\n : !1;\n}\nfunction getEventModifierState() {\n return modifierStateGetter;\n}\nvar KeyboardEventInterface = assign({}, UIEventInterface, {\n key: function (nativeEvent) {\n if (nativeEvent.key) {\n var key = normalizeKey[nativeEvent.key] || nativeEvent.key;\n if (\"Unidentified\" !== key) return key;\n }\n return \"keypress\" === nativeEvent.type\n ? ((nativeEvent = getEventCharCode(nativeEvent)),\n 13 === nativeEvent ? \"Enter\" : String.fromCharCode(nativeEvent))\n : \"keydown\" === nativeEvent.type || \"keyup\" === nativeEvent.type\n ? translateToKey[nativeEvent.keyCode] || \"Unidentified\"\n : \"\";\n },\n code: 0,\n location: 0,\n ctrlKey: 0,\n shiftKey: 0,\n altKey: 0,\n metaKey: 0,\n repeat: 0,\n locale: 0,\n getModifierState: getEventModifierState,\n charCode: function (event) {\n return \"keypress\" === event.type ? getEventCharCode(event) : 0;\n },\n keyCode: function (event) {\n return \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n },\n which: function (event) {\n return \"keypress\" === event.type\n ? getEventCharCode(event)\n : \"keydown\" === event.type || \"keyup\" === event.type\n ? event.keyCode\n : 0;\n }\n }),\n SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface),\n PointerEventInterface = assign({}, MouseEventInterface, {\n pointerId: 0,\n width: 0,\n height: 0,\n pressure: 0,\n tangentialPressure: 0,\n tiltX: 0,\n tiltY: 0,\n twist: 0,\n pointerType: 0,\n isPrimary: 0\n }),\n SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface),\n TouchEventInterface = assign({}, UIEventInterface, {\n touches: 0,\n targetTouches: 0,\n changedTouches: 0,\n altKey: 0,\n metaKey: 0,\n ctrlKey: 0,\n shiftKey: 0,\n getModifierState: getEventModifierState\n }),\n SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface),\n TransitionEventInterface = assign({}, EventInterface, {\n propertyName: 0,\n elapsedTime: 0,\n pseudoElement: 0\n }),\n SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface),\n WheelEventInterface = assign({}, MouseEventInterface, {\n deltaX: function (event) {\n return \"deltaX\" in event\n ? event.deltaX\n : \"wheelDeltaX\" in event\n ? -event.wheelDeltaX\n : 0;\n },\n deltaY: function (event) {\n return \"deltaY\" in event\n ? event.deltaY\n : \"wheelDeltaY\" in event\n ? -event.wheelDeltaY\n : \"wheelDelta\" in event\n ? -event.wheelDelta\n : 0;\n },\n deltaZ: 0,\n deltaMode: 0\n }),\n SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface),\n ToggleEventInterface = assign({}, EventInterface, {\n newState: 0,\n oldState: 0\n }),\n SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface),\n END_KEYCODES = [9, 13, 27, 32],\n canUseCompositionEvent = canUseDOM && \"CompositionEvent\" in window,\n documentMode = null;\ncanUseDOM &&\n \"documentMode\" in document &&\n (documentMode = document.documentMode);\nvar canUseTextInputEvent = canUseDOM && \"TextEvent\" in window && !documentMode,\n useFallbackCompositionData =\n canUseDOM &&\n (!canUseCompositionEvent ||\n (documentMode && 8 < documentMode && 11 >= documentMode)),\n SPACEBAR_CHAR = String.fromCharCode(32),\n hasSpaceKeypress = !1;\nfunction isFallbackCompositionEnd(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"keyup\":\n return -1 !== END_KEYCODES.indexOf(nativeEvent.keyCode);\n case \"keydown\":\n return 229 !== nativeEvent.keyCode;\n case \"keypress\":\n case \"mousedown\":\n case \"focusout\":\n return !0;\n default:\n return !1;\n }\n}\nfunction getDataFromCustomEvent(nativeEvent) {\n nativeEvent = nativeEvent.detail;\n return \"object\" === typeof nativeEvent && \"data\" in nativeEvent\n ? nativeEvent.data\n : null;\n}\nvar isComposing = !1;\nfunction getNativeBeforeInputChars(domEventName, nativeEvent) {\n switch (domEventName) {\n case \"compositionend\":\n return getDataFromCustomEvent(nativeEvent);\n case \"keypress\":\n if (32 !== nativeEvent.which) return null;\n hasSpaceKeypress = !0;\n return SPACEBAR_CHAR;\n case \"textInput\":\n return (\n (domEventName = nativeEvent.data),\n domEventName === SPACEBAR_CHAR && hasSpaceKeypress ? null : domEventName\n );\n default:\n return null;\n }\n}\nfunction getFallbackBeforeInputChars(domEventName, nativeEvent) {\n if (isComposing)\n return \"compositionend\" === domEventName ||\n (!canUseCompositionEvent &&\n isFallbackCompositionEnd(domEventName, nativeEvent))\n ? ((domEventName = getData()),\n (fallbackText = startText = root = null),\n (isComposing = !1),\n domEventName)\n : null;\n switch (domEventName) {\n case \"paste\":\n return null;\n case \"keypress\":\n if (\n !(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) ||\n (nativeEvent.ctrlKey && nativeEvent.altKey)\n ) {\n if (nativeEvent.char && 1 < nativeEvent.char.length)\n return nativeEvent.char;\n if (nativeEvent.which) return String.fromCharCode(nativeEvent.which);\n }\n return null;\n case \"compositionend\":\n return useFallbackCompositionData && \"ko\" !== nativeEvent.locale\n ? null\n : nativeEvent.data;\n default:\n return null;\n }\n}\nvar supportedInputTypes = {\n color: !0,\n date: !0,\n datetime: !0,\n \"datetime-local\": !0,\n email: !0,\n month: !0,\n number: !0,\n password: !0,\n range: !0,\n search: !0,\n tel: !0,\n text: !0,\n time: !0,\n url: !0,\n week: !0\n};\nfunction isTextInputElement(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return \"input\" === nodeName\n ? !!supportedInputTypes[elem.type]\n : \"textarea\" === nodeName\n ? !0\n : !1;\n}\nfunction createAndAccumulateChangeEvent(\n dispatchQueue,\n inst,\n nativeEvent,\n target\n) {\n restoreTarget\n ? restoreQueue\n ? restoreQueue.push(target)\n : (restoreQueue = [target])\n : (restoreTarget = target);\n inst = accumulateTwoPhaseListeners(inst, \"onChange\");\n 0 < inst.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onChange\",\n \"change\",\n null,\n nativeEvent,\n target\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: inst }));\n}\nvar activeElement$1 = null,\n activeElementInst$1 = null;\nfunction runEventInBatch(dispatchQueue) {\n processDispatchQueue(dispatchQueue, 0);\n}\nfunction getInstIfValueChanged(targetInst) {\n var targetNode = getNodeFromInstance(targetInst);\n if (updateValueIfChanged(targetNode)) return targetInst;\n}\nfunction getTargetInstForChangeEvent(domEventName, targetInst) {\n if (\"change\" === domEventName) return targetInst;\n}\nvar isInputEventSupported = !1;\nif (canUseDOM) {\n var JSCompiler_inline_result$jscomp$286;\n if (canUseDOM) {\n var isSupported$jscomp$inline_427 = \"oninput\" in document;\n if (!isSupported$jscomp$inline_427) {\n var element$jscomp$inline_428 = document.createElement(\"div\");\n element$jscomp$inline_428.setAttribute(\"oninput\", \"return;\");\n isSupported$jscomp$inline_427 =\n \"function\" === typeof element$jscomp$inline_428.oninput;\n }\n JSCompiler_inline_result$jscomp$286 = isSupported$jscomp$inline_427;\n } else JSCompiler_inline_result$jscomp$286 = !1;\n isInputEventSupported =\n JSCompiler_inline_result$jscomp$286 &&\n (!document.documentMode || 9 < document.documentMode);\n}\nfunction stopWatchingForValueChange() {\n activeElement$1 &&\n (activeElement$1.detachEvent(\"onpropertychange\", handlePropertyChange),\n (activeElementInst$1 = activeElement$1 = null));\n}\nfunction handlePropertyChange(nativeEvent) {\n if (\n \"value\" === nativeEvent.propertyName &&\n getInstIfValueChanged(activeElementInst$1)\n ) {\n var dispatchQueue = [];\n createAndAccumulateChangeEvent(\n dispatchQueue,\n activeElementInst$1,\n nativeEvent,\n getEventTarget(nativeEvent)\n );\n batchedUpdates$1(runEventInBatch, dispatchQueue);\n }\n}\nfunction handleEventsForInputEventPolyfill(domEventName, target, targetInst) {\n \"focusin\" === domEventName\n ? (stopWatchingForValueChange(),\n (activeElement$1 = target),\n (activeElementInst$1 = targetInst),\n activeElement$1.attachEvent(\"onpropertychange\", handlePropertyChange))\n : \"focusout\" === domEventName && stopWatchingForValueChange();\n}\nfunction getTargetInstForInputEventPolyfill(domEventName) {\n if (\n \"selectionchange\" === domEventName ||\n \"keyup\" === domEventName ||\n \"keydown\" === domEventName\n )\n return getInstIfValueChanged(activeElementInst$1);\n}\nfunction getTargetInstForClickEvent(domEventName, targetInst) {\n if (\"click\" === domEventName) return getInstIfValueChanged(targetInst);\n}\nfunction getTargetInstForInputOrChangeEvent(domEventName, targetInst) {\n if (\"input\" === domEventName || \"change\" === domEventName)\n return getInstIfValueChanged(targetInst);\n}\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is;\nfunction shallowEqual(objA, objB) {\n if (objectIs(objA, objB)) return !0;\n if (\n \"object\" !== typeof objA ||\n null === objA ||\n \"object\" !== typeof objB ||\n null === objB\n )\n return !1;\n var keysA = Object.keys(objA),\n keysB = Object.keys(objB);\n if (keysA.length !== keysB.length) return !1;\n for (keysB = 0; keysB < keysA.length; keysB++) {\n var currentKey = keysA[keysB];\n if (\n !hasOwnProperty.call(objB, currentKey) ||\n !objectIs(objA[currentKey], objB[currentKey])\n )\n return !1;\n }\n return !0;\n}\nfunction getLeafNode(node) {\n for (; node && node.firstChild; ) node = node.firstChild;\n return node;\n}\nfunction getNodeForCharacterOffset(root, offset) {\n var node = getLeafNode(root);\n root = 0;\n for (var nodeEnd; node; ) {\n if (3 === node.nodeType) {\n nodeEnd = root + node.textContent.length;\n if (root <= offset && nodeEnd >= offset)\n return { node: node, offset: offset - root };\n root = nodeEnd;\n }\n a: {\n for (; node; ) {\n if (node.nextSibling) {\n node = node.nextSibling;\n break a;\n }\n node = node.parentNode;\n }\n node = void 0;\n }\n node = getLeafNode(node);\n }\n}\nfunction containsNode(outerNode, innerNode) {\n return outerNode && innerNode\n ? outerNode === innerNode\n ? !0\n : outerNode && 3 === outerNode.nodeType\n ? !1\n : innerNode && 3 === innerNode.nodeType\n ? containsNode(outerNode, innerNode.parentNode)\n : \"contains\" in outerNode\n ? outerNode.contains(innerNode)\n : outerNode.compareDocumentPosition\n ? !!(outerNode.compareDocumentPosition(innerNode) & 16)\n : !1\n : !1;\n}\nfunction getActiveElementDeep(containerInfo) {\n containerInfo =\n null != containerInfo &&\n null != containerInfo.ownerDocument &&\n null != containerInfo.ownerDocument.defaultView\n ? containerInfo.ownerDocument.defaultView\n : window;\n for (\n var element = getActiveElement(containerInfo.document);\n element instanceof containerInfo.HTMLIFrameElement;\n\n ) {\n try {\n var JSCompiler_inline_result =\n \"string\" === typeof element.contentWindow.location.href;\n } catch (err) {\n JSCompiler_inline_result = !1;\n }\n if (JSCompiler_inline_result) containerInfo = element.contentWindow;\n else break;\n element = getActiveElement(containerInfo.document);\n }\n return element;\n}\nfunction hasSelectionCapabilities(elem) {\n var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();\n return (\n nodeName &&\n ((\"input\" === nodeName &&\n (\"text\" === elem.type ||\n \"search\" === elem.type ||\n \"tel\" === elem.type ||\n \"url\" === elem.type ||\n \"password\" === elem.type)) ||\n \"textarea\" === nodeName ||\n \"true\" === elem.contentEditable)\n );\n}\nvar skipSelectionChangeEvent =\n canUseDOM && \"documentMode\" in document && 11 >= document.documentMode,\n activeElement = null,\n activeElementInst = null,\n lastSelection = null,\n mouseDown = !1;\nfunction constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {\n var doc =\n nativeEventTarget.window === nativeEventTarget\n ? nativeEventTarget.document\n : 9 === nativeEventTarget.nodeType\n ? nativeEventTarget\n : nativeEventTarget.ownerDocument;\n mouseDown ||\n null == activeElement ||\n activeElement !== getActiveElement(doc) ||\n ((doc = activeElement),\n \"selectionStart\" in doc && hasSelectionCapabilities(doc)\n ? (doc = { start: doc.selectionStart, end: doc.selectionEnd })\n : ((doc = (\n (doc.ownerDocument && doc.ownerDocument.defaultView) ||\n window\n ).getSelection()),\n (doc = {\n anchorNode: doc.anchorNode,\n anchorOffset: doc.anchorOffset,\n focusNode: doc.focusNode,\n focusOffset: doc.focusOffset\n })),\n (lastSelection && shallowEqual(lastSelection, doc)) ||\n ((lastSelection = doc),\n (doc = accumulateTwoPhaseListeners(activeElementInst, \"onSelect\")),\n 0 < doc.length &&\n ((nativeEvent = new SyntheticEvent(\n \"onSelect\",\n \"select\",\n null,\n nativeEvent,\n nativeEventTarget\n )),\n dispatchQueue.push({ event: nativeEvent, listeners: doc }),\n (nativeEvent.target = activeElement))));\n}\nfunction makePrefixMap(styleProp, eventName) {\n var prefixes = {};\n prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();\n prefixes[\"Webkit\" + styleProp] = \"webkit\" + eventName;\n prefixes[\"Moz\" + styleProp] = \"moz\" + eventName;\n return prefixes;\n}\nvar vendorPrefixes = {\n animationend: makePrefixMap(\"Animation\", \"AnimationEnd\"),\n animationiteration: makePrefixMap(\"Animation\", \"AnimationIteration\"),\n animationstart: makePrefixMap(\"Animation\", \"AnimationStart\"),\n transitionrun: makePrefixMap(\"Transition\", \"TransitionRun\"),\n transitionstart: makePrefixMap(\"Transition\", \"TransitionStart\"),\n transitioncancel: makePrefixMap(\"Transition\", \"TransitionCancel\"),\n transitionend: makePrefixMap(\"Transition\", \"TransitionEnd\")\n },\n prefixedEventNames = {},\n style = {};\ncanUseDOM &&\n ((style = document.createElement(\"div\").style),\n \"AnimationEvent\" in window ||\n (delete vendorPrefixes.animationend.animation,\n delete vendorPrefixes.animationiteration.animation,\n delete vendorPrefixes.animationstart.animation),\n \"TransitionEvent\" in window ||\n delete vendorPrefixes.transitionend.transition);\nfunction getVendorPrefixedEventName(eventName) {\n if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];\n if (!vendorPrefixes[eventName]) return eventName;\n var prefixMap = vendorPrefixes[eventName],\n styleProp;\n for (styleProp in prefixMap)\n if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)\n return (prefixedEventNames[eventName] = prefixMap[styleProp]);\n return eventName;\n}\nvar ANIMATION_END = getVendorPrefixedEventName(\"animationend\"),\n ANIMATION_ITERATION = getVendorPrefixedEventName(\"animationiteration\"),\n ANIMATION_START = getVendorPrefixedEventName(\"animationstart\"),\n TRANSITION_RUN = getVendorPrefixedEventName(\"transitionrun\"),\n TRANSITION_START = getVendorPrefixedEventName(\"transitionstart\"),\n TRANSITION_CANCEL = getVendorPrefixedEventName(\"transitioncancel\"),\n TRANSITION_END = getVendorPrefixedEventName(\"transitionend\"),\n topLevelEventsToReactNames = new Map(),\n simpleEventPluginEvents =\n \"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\n \" \"\n );\nsimpleEventPluginEvents.push(\"scrollEnd\");\nfunction registerSimpleEvent(domEventName, reactName) {\n topLevelEventsToReactNames.set(domEventName, reactName);\n registerTwoPhaseEvent(reactName, [domEventName]);\n}\nvar reportGlobalError =\n \"function\" === typeof reportError\n ? reportError\n : function (error) {\n if (\n \"object\" === typeof window &&\n \"function\" === typeof window.ErrorEvent\n ) {\n var event = new window.ErrorEvent(\"error\", {\n bubbles: !0,\n cancelable: !0,\n message:\n \"object\" === typeof error &&\n null !== error &&\n \"string\" === typeof error.message\n ? String(error.message)\n : String(error),\n error: error\n });\n if (!window.dispatchEvent(event)) return;\n } else if (\n \"object\" === typeof process &&\n \"function\" === typeof process.emit\n ) {\n process.emit(\"uncaughtException\", error);\n return;\n }\n console.error(error);\n },\n concurrentQueues = [],\n concurrentQueuesIndex = 0,\n concurrentlyUpdatedLanes = 0;\nfunction finishQueueingConcurrentUpdates() {\n for (\n var endIndex = concurrentQueuesIndex,\n i = (concurrentlyUpdatedLanes = concurrentQueuesIndex = 0);\n i < endIndex;\n\n ) {\n var fiber = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var queue = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var update = concurrentQueues[i];\n concurrentQueues[i++] = null;\n var lane = concurrentQueues[i];\n concurrentQueues[i++] = null;\n if (null !== queue && null !== update) {\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n }\n 0 !== lane && markUpdateLaneFromFiberToRoot(fiber, update, lane);\n }\n}\nfunction enqueueUpdate$1(fiber, queue, update, lane) {\n concurrentQueues[concurrentQueuesIndex++] = fiber;\n concurrentQueues[concurrentQueuesIndex++] = queue;\n concurrentQueues[concurrentQueuesIndex++] = update;\n concurrentQueues[concurrentQueuesIndex++] = lane;\n concurrentlyUpdatedLanes |= lane;\n fiber.lanes |= lane;\n fiber = fiber.alternate;\n null !== fiber && (fiber.lanes |= lane);\n}\nfunction enqueueConcurrentHookUpdate(fiber, queue, update, lane) {\n enqueueUpdate$1(fiber, queue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction enqueueConcurrentRenderForLane(fiber, lane) {\n enqueueUpdate$1(fiber, null, null, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {\n sourceFiber.lanes |= lane;\n var alternate = sourceFiber.alternate;\n null !== alternate && (alternate.lanes |= lane);\n for (var isHidden = !1, parent = sourceFiber.return; null !== parent; )\n (parent.childLanes |= lane),\n (alternate = parent.alternate),\n null !== alternate && (alternate.childLanes |= lane),\n 22 === parent.tag &&\n ((sourceFiber = parent.stateNode),\n null === sourceFiber || sourceFiber._visibility & 1 || (isHidden = !0)),\n (sourceFiber = parent),\n (parent = parent.return);\n return 3 === sourceFiber.tag\n ? ((parent = sourceFiber.stateNode),\n isHidden &&\n null !== update &&\n ((isHidden = 31 - clz32(lane)),\n (sourceFiber = parent.hiddenUpdates),\n (alternate = sourceFiber[isHidden]),\n null === alternate\n ? (sourceFiber[isHidden] = [update])\n : alternate.push(update),\n (update.lane = lane | 536870912)),\n parent)\n : null;\n}\nfunction getRootForUpdatedFiber(sourceFiber) {\n if (50 < nestedUpdateCount)\n throw (\n ((nestedUpdateCount = 0),\n (rootWithNestedUpdates = null),\n Error(formatProdErrorMessage(185)))\n );\n for (var parent = sourceFiber.return; null !== parent; )\n (sourceFiber = parent), (parent = sourceFiber.return);\n return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;\n}\nvar emptyContextObject = {};\nfunction FiberNode(tag, pendingProps, key, mode) {\n this.tag = tag;\n this.key = key;\n this.sibling =\n this.child =\n this.return =\n this.stateNode =\n this.type =\n this.elementType =\n null;\n this.index = 0;\n this.refCleanup = this.ref = null;\n this.pendingProps = pendingProps;\n this.dependencies =\n this.memoizedState =\n this.updateQueue =\n this.memoizedProps =\n null;\n this.mode = mode;\n this.subtreeFlags = this.flags = 0;\n this.deletions = null;\n this.childLanes = this.lanes = 0;\n this.alternate = null;\n}\nfunction createFiberImplClass(tag, pendingProps, key, mode) {\n return new FiberNode(tag, pendingProps, key, mode);\n}\nfunction shouldConstruct(Component) {\n Component = Component.prototype;\n return !(!Component || !Component.isReactComponent);\n}\nfunction createWorkInProgress(current, pendingProps) {\n var workInProgress = current.alternate;\n null === workInProgress\n ? ((workInProgress = createFiberImplClass(\n current.tag,\n pendingProps,\n current.key,\n current.mode\n )),\n (workInProgress.elementType = current.elementType),\n (workInProgress.type = current.type),\n (workInProgress.stateNode = current.stateNode),\n (workInProgress.alternate = current),\n (current.alternate = workInProgress))\n : ((workInProgress.pendingProps = pendingProps),\n (workInProgress.type = current.type),\n (workInProgress.flags = 0),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null));\n workInProgress.flags = current.flags & 65011712;\n workInProgress.childLanes = current.childLanes;\n workInProgress.lanes = current.lanes;\n workInProgress.child = current.child;\n workInProgress.memoizedProps = current.memoizedProps;\n workInProgress.memoizedState = current.memoizedState;\n workInProgress.updateQueue = current.updateQueue;\n pendingProps = current.dependencies;\n workInProgress.dependencies =\n null === pendingProps\n ? null\n : { lanes: pendingProps.lanes, firstContext: pendingProps.firstContext };\n workInProgress.sibling = current.sibling;\n workInProgress.index = current.index;\n workInProgress.ref = current.ref;\n workInProgress.refCleanup = current.refCleanup;\n return workInProgress;\n}\nfunction resetWorkInProgress(workInProgress, renderLanes) {\n workInProgress.flags &= 65011714;\n var current = workInProgress.alternate;\n null === current\n ? ((workInProgress.childLanes = 0),\n (workInProgress.lanes = renderLanes),\n (workInProgress.child = null),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.memoizedProps = null),\n (workInProgress.memoizedState = null),\n (workInProgress.updateQueue = null),\n (workInProgress.dependencies = null),\n (workInProgress.stateNode = null))\n : ((workInProgress.childLanes = current.childLanes),\n (workInProgress.lanes = current.lanes),\n (workInProgress.child = current.child),\n (workInProgress.subtreeFlags = 0),\n (workInProgress.deletions = null),\n (workInProgress.memoizedProps = current.memoizedProps),\n (workInProgress.memoizedState = current.memoizedState),\n (workInProgress.updateQueue = current.updateQueue),\n (workInProgress.type = current.type),\n (renderLanes = current.dependencies),\n (workInProgress.dependencies =\n null === renderLanes\n ? null\n : {\n lanes: renderLanes.lanes,\n firstContext: renderLanes.firstContext\n }));\n return workInProgress;\n}\nfunction createFiberFromTypeAndProps(\n type,\n key,\n pendingProps,\n owner,\n mode,\n lanes\n) {\n var fiberTag = 0;\n owner = type;\n if (\"function\" === typeof type) shouldConstruct(type) && (fiberTag = 1);\n else if (\"string\" === typeof type)\n fiberTag = isHostHoistableType(\n type,\n pendingProps,\n contextStackCursor.current\n )\n ? 26\n : \"html\" === type || \"head\" === type || \"body\" === type\n ? 27\n : 5;\n else\n a: switch (type) {\n case REACT_ACTIVITY_TYPE:\n return (\n (type = createFiberImplClass(31, pendingProps, key, mode)),\n (type.elementType = REACT_ACTIVITY_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_FRAGMENT_TYPE:\n return createFiberFromFragment(pendingProps.children, mode, lanes, key);\n case REACT_STRICT_MODE_TYPE:\n fiberTag = 8;\n mode |= 24;\n break;\n case REACT_PROFILER_TYPE:\n return (\n (type = createFiberImplClass(12, pendingProps, key, mode | 2)),\n (type.elementType = REACT_PROFILER_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_TYPE:\n return (\n (type = createFiberImplClass(13, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_TYPE),\n (type.lanes = lanes),\n type\n );\n case REACT_SUSPENSE_LIST_TYPE:\n return (\n (type = createFiberImplClass(19, pendingProps, key, mode)),\n (type.elementType = REACT_SUSPENSE_LIST_TYPE),\n (type.lanes = lanes),\n type\n );\n default:\n if (\"object\" === typeof type && null !== type)\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n fiberTag = 10;\n break a;\n case REACT_CONSUMER_TYPE:\n fiberTag = 9;\n break a;\n case REACT_FORWARD_REF_TYPE:\n fiberTag = 11;\n break a;\n case REACT_MEMO_TYPE:\n fiberTag = 14;\n break a;\n case REACT_LAZY_TYPE:\n fiberTag = 16;\n owner = null;\n break a;\n }\n fiberTag = 29;\n pendingProps = Error(\n formatProdErrorMessage(130, null === type ? \"null\" : typeof type, \"\")\n );\n owner = null;\n }\n key = createFiberImplClass(fiberTag, pendingProps, key, mode);\n key.elementType = type;\n key.type = owner;\n key.lanes = lanes;\n return key;\n}\nfunction createFiberFromFragment(elements, mode, lanes, key) {\n elements = createFiberImplClass(7, elements, key, mode);\n elements.lanes = lanes;\n return elements;\n}\nfunction createFiberFromText(content, mode, lanes) {\n content = createFiberImplClass(6, content, null, mode);\n content.lanes = lanes;\n return content;\n}\nfunction createFiberFromDehydratedFragment(dehydratedNode) {\n var fiber = createFiberImplClass(18, null, null, 0);\n fiber.stateNode = dehydratedNode;\n return fiber;\n}\nfunction createFiberFromPortal(portal, mode, lanes) {\n mode = createFiberImplClass(\n 4,\n null !== portal.children ? portal.children : [],\n portal.key,\n mode\n );\n mode.lanes = lanes;\n mode.stateNode = {\n containerInfo: portal.containerInfo,\n pendingChildren: null,\n implementation: portal.implementation\n };\n return mode;\n}\nvar CapturedStacks = new WeakMap();\nfunction createCapturedValueAtFiber(value, source) {\n if (\"object\" === typeof value && null !== value) {\n var existing = CapturedStacks.get(value);\n if (void 0 !== existing) return existing;\n source = {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n CapturedStacks.set(value, source);\n return source;\n }\n return {\n value: value,\n source: source,\n stack: getStackByFiberInDevAndProd(source)\n };\n}\nvar forkStack = [],\n forkStackIndex = 0,\n treeForkProvider = null,\n treeForkCount = 0,\n idStack = [],\n idStackIndex = 0,\n treeContextProvider = null,\n treeContextId = 1,\n treeContextOverflow = \"\";\nfunction pushTreeFork(workInProgress, totalChildren) {\n forkStack[forkStackIndex++] = treeForkCount;\n forkStack[forkStackIndex++] = treeForkProvider;\n treeForkProvider = workInProgress;\n treeForkCount = totalChildren;\n}\nfunction pushTreeId(workInProgress, totalChildren, index) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextProvider = workInProgress;\n var baseIdWithLeadingBit = treeContextId;\n workInProgress = treeContextOverflow;\n var baseLength = 32 - clz32(baseIdWithLeadingBit) - 1;\n baseIdWithLeadingBit &= ~(1 << baseLength);\n index += 1;\n var length = 32 - clz32(totalChildren) + baseLength;\n if (30 < length) {\n var numberOfOverflowBits = baseLength - (baseLength % 5);\n length = (\n baseIdWithLeadingBit &\n ((1 << numberOfOverflowBits) - 1)\n ).toString(32);\n baseIdWithLeadingBit >>= numberOfOverflowBits;\n baseLength -= numberOfOverflowBits;\n treeContextId =\n (1 << (32 - clz32(totalChildren) + baseLength)) |\n (index << baseLength) |\n baseIdWithLeadingBit;\n treeContextOverflow = length + workInProgress;\n } else\n (treeContextId =\n (1 << length) | (index << baseLength) | baseIdWithLeadingBit),\n (treeContextOverflow = workInProgress);\n}\nfunction pushMaterializedTreeId(workInProgress) {\n null !== workInProgress.return &&\n (pushTreeFork(workInProgress, 1), pushTreeId(workInProgress, 1, 0));\n}\nfunction popTreeContext(workInProgress) {\n for (; workInProgress === treeForkProvider; )\n (treeForkProvider = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null),\n (treeForkCount = forkStack[--forkStackIndex]),\n (forkStack[forkStackIndex] = null);\n for (; workInProgress === treeContextProvider; )\n (treeContextProvider = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextOverflow = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null),\n (treeContextId = idStack[--idStackIndex]),\n (idStack[idStackIndex] = null);\n}\nfunction restoreSuspendedTreeContext(workInProgress, suspendedContext) {\n idStack[idStackIndex++] = treeContextId;\n idStack[idStackIndex++] = treeContextOverflow;\n idStack[idStackIndex++] = treeContextProvider;\n treeContextId = suspendedContext.id;\n treeContextOverflow = suspendedContext.overflow;\n treeContextProvider = workInProgress;\n}\nvar hydrationParentFiber = null,\n nextHydratableInstance = null,\n isHydrating = !1,\n hydrationErrors = null,\n rootOrSingletonContext = !1,\n HydrationMismatchException = Error(formatProdErrorMessage(519));\nfunction throwOnHydrationMismatch(fiber) {\n var error = Error(\n formatProdErrorMessage(\n 418,\n 1 < arguments.length && void 0 !== arguments[1] && arguments[1]\n ? \"text\"\n : \"HTML\",\n \"\"\n )\n );\n queueHydrationError(createCapturedValueAtFiber(error, fiber));\n throw HydrationMismatchException;\n}\nfunction prepareToHydrateHostInstance(fiber) {\n var instance = fiber.stateNode,\n type = fiber.type,\n props = fiber.memoizedProps;\n instance[internalInstanceKey] = fiber;\n instance[internalPropsKey] = props;\n switch (type) {\n case \"dialog\":\n listenToNonDelegatedEvent(\"cancel\", instance);\n listenToNonDelegatedEvent(\"close\", instance);\n break;\n case \"iframe\":\n case \"object\":\n case \"embed\":\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"video\":\n case \"audio\":\n for (type = 0; type < mediaEventTypes.length; type++)\n listenToNonDelegatedEvent(mediaEventTypes[type], instance);\n break;\n case \"source\":\n listenToNonDelegatedEvent(\"error\", instance);\n break;\n case \"img\":\n case \"image\":\n case \"link\":\n listenToNonDelegatedEvent(\"error\", instance);\n listenToNonDelegatedEvent(\"load\", instance);\n break;\n case \"details\":\n listenToNonDelegatedEvent(\"toggle\", instance);\n break;\n case \"input\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n initInput(\n instance,\n props.value,\n props.defaultValue,\n props.checked,\n props.defaultChecked,\n props.type,\n props.name,\n !0\n );\n break;\n case \"select\":\n listenToNonDelegatedEvent(\"invalid\", instance);\n break;\n case \"textarea\":\n listenToNonDelegatedEvent(\"invalid\", instance),\n initTextarea(instance, props.value, props.defaultValue, props.children);\n }\n type = props.children;\n (\"string\" !== typeof type &&\n \"number\" !== typeof type &&\n \"bigint\" !== typeof type) ||\n instance.textContent === \"\" + type ||\n !0 === props.suppressHydrationWarning ||\n checkForUnmatchedText(instance.textContent, type)\n ? (null != props.popover &&\n (listenToNonDelegatedEvent(\"beforetoggle\", instance),\n listenToNonDelegatedEvent(\"toggle\", instance)),\n null != props.onScroll && listenToNonDelegatedEvent(\"scroll\", instance),\n null != props.onScrollEnd &&\n listenToNonDelegatedEvent(\"scrollend\", instance),\n null != props.onClick && (instance.onclick = noop$1),\n (instance = !0))\n : (instance = !1);\n instance || throwOnHydrationMismatch(fiber, !0);\n}\nfunction popToNextHostParent(fiber) {\n for (hydrationParentFiber = fiber.return; hydrationParentFiber; )\n switch (hydrationParentFiber.tag) {\n case 5:\n case 31:\n case 13:\n rootOrSingletonContext = !1;\n return;\n case 27:\n case 3:\n rootOrSingletonContext = !0;\n return;\n default:\n hydrationParentFiber = hydrationParentFiber.return;\n }\n}\nfunction popHydrationState(fiber) {\n if (fiber !== hydrationParentFiber) return !1;\n if (!isHydrating) return popToNextHostParent(fiber), (isHydrating = !0), !1;\n var tag = fiber.tag,\n JSCompiler_temp;\n if ((JSCompiler_temp = 3 !== tag && 27 !== tag)) {\n if ((JSCompiler_temp = 5 === tag))\n (JSCompiler_temp = fiber.type),\n (JSCompiler_temp =\n !(\"form\" !== JSCompiler_temp && \"button\" !== JSCompiler_temp) ||\n shouldSetTextContent(fiber.type, fiber.memoizedProps));\n JSCompiler_temp = !JSCompiler_temp;\n }\n JSCompiler_temp && nextHydratableInstance && throwOnHydrationMismatch(fiber);\n popToNextHostParent(fiber);\n if (13 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else if (31 === tag) {\n fiber = fiber.memoizedState;\n fiber = null !== fiber ? fiber.dehydrated : null;\n if (!fiber) throw Error(formatProdErrorMessage(317));\n nextHydratableInstance =\n getNextHydratableInstanceAfterHydrationBoundary(fiber);\n } else\n 27 === tag\n ? ((tag = nextHydratableInstance),\n isSingletonScope(fiber.type)\n ? ((fiber = previousHydratableOnEnteringScopedSingleton),\n (previousHydratableOnEnteringScopedSingleton = null),\n (nextHydratableInstance = fiber))\n : (nextHydratableInstance = tag))\n : (nextHydratableInstance = hydrationParentFiber\n ? getNextHydratable(fiber.stateNode.nextSibling)\n : null);\n return !0;\n}\nfunction resetHydrationState() {\n nextHydratableInstance = hydrationParentFiber = null;\n isHydrating = !1;\n}\nfunction upgradeHydrationErrorsToRecoverable() {\n var queuedErrors = hydrationErrors;\n null !== queuedErrors &&\n (null === workInProgressRootRecoverableErrors\n ? (workInProgressRootRecoverableErrors = queuedErrors)\n : workInProgressRootRecoverableErrors.push.apply(\n workInProgressRootRecoverableErrors,\n queuedErrors\n ),\n (hydrationErrors = null));\n return queuedErrors;\n}\nfunction queueHydrationError(error) {\n null === hydrationErrors\n ? (hydrationErrors = [error])\n : hydrationErrors.push(error);\n}\nvar valueCursor = createCursor(null),\n currentlyRenderingFiber$1 = null,\n lastContextDependency = null;\nfunction pushProvider(providerFiber, context, nextValue) {\n push(valueCursor, context._currentValue);\n context._currentValue = nextValue;\n}\nfunction popProvider(context) {\n context._currentValue = valueCursor.current;\n pop(valueCursor);\n}\nfunction scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {\n for (; null !== parent; ) {\n var alternate = parent.alternate;\n (parent.childLanes & renderLanes) !== renderLanes\n ? ((parent.childLanes |= renderLanes),\n null !== alternate && (alternate.childLanes |= renderLanes))\n : null !== alternate &&\n (alternate.childLanes & renderLanes) !== renderLanes &&\n (alternate.childLanes |= renderLanes);\n if (parent === propagationRoot) break;\n parent = parent.return;\n }\n}\nfunction propagateContextChanges(\n workInProgress,\n contexts,\n renderLanes,\n forcePropagateEntireTree\n) {\n var fiber = workInProgress.child;\n null !== fiber && (fiber.return = workInProgress);\n for (; null !== fiber; ) {\n var list = fiber.dependencies;\n if (null !== list) {\n var nextFiber = fiber.child;\n list = list.firstContext;\n a: for (; null !== list; ) {\n var dependency = list;\n list = fiber;\n for (var i = 0; i < contexts.length; i++)\n if (dependency.context === contexts[i]) {\n list.lanes |= renderLanes;\n dependency = list.alternate;\n null !== dependency && (dependency.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(\n list.return,\n renderLanes,\n workInProgress\n );\n forcePropagateEntireTree || (nextFiber = null);\n break a;\n }\n list = dependency.next;\n }\n } else if (18 === fiber.tag) {\n nextFiber = fiber.return;\n if (null === nextFiber) throw Error(formatProdErrorMessage(341));\n nextFiber.lanes |= renderLanes;\n list = nextFiber.alternate;\n null !== list && (list.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(nextFiber, renderLanes, workInProgress);\n nextFiber = null;\n } else nextFiber = fiber.child;\n if (null !== nextFiber) nextFiber.return = fiber;\n else\n for (nextFiber = fiber; null !== nextFiber; ) {\n if (nextFiber === workInProgress) {\n nextFiber = null;\n break;\n }\n fiber = nextFiber.sibling;\n if (null !== fiber) {\n fiber.return = nextFiber.return;\n nextFiber = fiber;\n break;\n }\n nextFiber = nextFiber.return;\n }\n fiber = nextFiber;\n }\n}\nfunction propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n forcePropagateEntireTree\n) {\n current = null;\n for (\n var parent = workInProgress, isInsidePropagationBailout = !1;\n null !== parent;\n\n ) {\n if (!isInsidePropagationBailout)\n if (0 !== (parent.flags & 524288)) isInsidePropagationBailout = !0;\n else if (0 !== (parent.flags & 262144)) break;\n if (10 === parent.tag) {\n var currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent = currentParent.memoizedProps;\n if (null !== currentParent) {\n var context = parent.type;\n objectIs(parent.pendingProps.value, currentParent.value) ||\n (null !== current ? current.push(context) : (current = [context]));\n }\n } else if (parent === hostTransitionProviderCursor.current) {\n currentParent = parent.alternate;\n if (null === currentParent) throw Error(formatProdErrorMessage(387));\n currentParent.memoizedState.memoizedState !==\n parent.memoizedState.memoizedState &&\n (null !== current\n ? current.push(HostTransitionContext)\n : (current = [HostTransitionContext]));\n }\n parent = parent.return;\n }\n null !== current &&\n propagateContextChanges(\n workInProgress,\n current,\n renderLanes,\n forcePropagateEntireTree\n );\n workInProgress.flags |= 262144;\n}\nfunction checkIfContextChanged(currentDependencies) {\n for (\n currentDependencies = currentDependencies.firstContext;\n null !== currentDependencies;\n\n ) {\n if (\n !objectIs(\n currentDependencies.context._currentValue,\n currentDependencies.memoizedValue\n )\n )\n return !0;\n currentDependencies = currentDependencies.next;\n }\n return !1;\n}\nfunction prepareToReadContext(workInProgress) {\n currentlyRenderingFiber$1 = workInProgress;\n lastContextDependency = null;\n workInProgress = workInProgress.dependencies;\n null !== workInProgress && (workInProgress.firstContext = null);\n}\nfunction readContext(context) {\n return readContextForConsumer(currentlyRenderingFiber$1, context);\n}\nfunction readContextDuringReconciliation(consumer, context) {\n null === currentlyRenderingFiber$1 && prepareToReadContext(consumer);\n return readContextForConsumer(consumer, context);\n}\nfunction readContextForConsumer(consumer, context) {\n var value = context._currentValue;\n context = { context: context, memoizedValue: value, next: null };\n if (null === lastContextDependency) {\n if (null === consumer) throw Error(formatProdErrorMessage(308));\n lastContextDependency = context;\n consumer.dependencies = { lanes: 0, firstContext: context };\n consumer.flags |= 524288;\n } else lastContextDependency = lastContextDependency.next = context;\n return value;\n}\nvar AbortControllerLocal =\n \"undefined\" !== typeof AbortController\n ? AbortController\n : function () {\n var listeners = [],\n signal = (this.signal = {\n aborted: !1,\n addEventListener: function (type, listener) {\n listeners.push(listener);\n }\n });\n this.abort = function () {\n signal.aborted = !0;\n listeners.forEach(function (listener) {\n return listener();\n });\n };\n },\n scheduleCallback$2 = Scheduler.unstable_scheduleCallback,\n NormalPriority = Scheduler.unstable_NormalPriority,\n CacheContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n Consumer: null,\n Provider: null,\n _currentValue: null,\n _currentValue2: null,\n _threadCount: 0\n };\nfunction createCache() {\n return {\n controller: new AbortControllerLocal(),\n data: new Map(),\n refCount: 0\n };\n}\nfunction releaseCache(cache) {\n cache.refCount--;\n 0 === cache.refCount &&\n scheduleCallback$2(NormalPriority, function () {\n cache.controller.abort();\n });\n}\nvar currentEntangledListeners = null,\n currentEntangledPendingCount = 0,\n currentEntangledLane = 0,\n currentEntangledActionThenable = null;\nfunction entangleAsyncAction(transition, thenable) {\n if (null === currentEntangledListeners) {\n var entangledListeners = (currentEntangledListeners = []);\n currentEntangledPendingCount = 0;\n currentEntangledLane = requestTransitionLane();\n currentEntangledActionThenable = {\n status: \"pending\",\n value: void 0,\n then: function (resolve) {\n entangledListeners.push(resolve);\n }\n };\n }\n currentEntangledPendingCount++;\n thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);\n return thenable;\n}\nfunction pingEngtangledActionScope() {\n if (\n 0 === --currentEntangledPendingCount &&\n null !== currentEntangledListeners\n ) {\n null !== currentEntangledActionThenable &&\n (currentEntangledActionThenable.status = \"fulfilled\");\n var listeners = currentEntangledListeners;\n currentEntangledListeners = null;\n currentEntangledLane = 0;\n currentEntangledActionThenable = null;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])();\n }\n}\nfunction chainThenableValue(thenable, result) {\n var listeners = [],\n thenableWithOverride = {\n status: \"pending\",\n value: null,\n reason: null,\n then: function (resolve) {\n listeners.push(resolve);\n }\n };\n thenable.then(\n function () {\n thenableWithOverride.status = \"fulfilled\";\n thenableWithOverride.value = result;\n for (var i = 0; i < listeners.length; i++) (0, listeners[i])(result);\n },\n function (error) {\n thenableWithOverride.status = \"rejected\";\n thenableWithOverride.reason = error;\n for (error = 0; error < listeners.length; error++)\n (0, listeners[error])(void 0);\n }\n );\n return thenableWithOverride;\n}\nvar prevOnStartTransitionFinish = ReactSharedInternals.S;\nReactSharedInternals.S = function (transition, returnValue) {\n globalMostRecentTransitionTime = now();\n \"object\" === typeof returnValue &&\n null !== returnValue &&\n \"function\" === typeof returnValue.then &&\n entangleAsyncAction(transition, returnValue);\n null !== prevOnStartTransitionFinish &&\n prevOnStartTransitionFinish(transition, returnValue);\n};\nvar resumedCache = createCursor(null);\nfunction peekCacheFromPool() {\n var cacheResumedFromPreviousRender = resumedCache.current;\n return null !== cacheResumedFromPreviousRender\n ? cacheResumedFromPreviousRender\n : workInProgressRoot.pooledCache;\n}\nfunction pushTransition(offscreenWorkInProgress, prevCachePool) {\n null === prevCachePool\n ? push(resumedCache, resumedCache.current)\n : push(resumedCache, prevCachePool.pool);\n}\nfunction getSuspendedCache() {\n var cacheFromPool = peekCacheFromPool();\n return null === cacheFromPool\n ? null\n : { parent: CacheContext._currentValue, pool: cacheFromPool };\n}\nvar SuspenseException = Error(formatProdErrorMessage(460)),\n SuspenseyCommitException = Error(formatProdErrorMessage(474)),\n SuspenseActionException = Error(formatProdErrorMessage(542)),\n noopSuspenseyCommitThenable = { then: function () {} };\nfunction isThenableResolved(thenable) {\n thenable = thenable.status;\n return \"fulfilled\" === thenable || \"rejected\" === thenable;\n}\nfunction trackUsedThenable(thenableState, thenable, index) {\n index = thenableState[index];\n void 0 === index\n ? thenableState.push(thenable)\n : index !== thenable && (thenable.then(noop$1, noop$1), (thenable = index));\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n default:\n if (\"string\" === typeof thenable.status) thenable.then(noop$1, noop$1);\n else {\n thenableState = workInProgressRoot;\n if (null !== thenableState && 100 < thenableState.shellSuspendCounter)\n throw Error(formatProdErrorMessage(482));\n thenableState = thenable;\n thenableState.status = \"pending\";\n thenableState.then(\n function (fulfilledValue) {\n if (\"pending\" === thenable.status) {\n var fulfilledThenable = thenable;\n fulfilledThenable.status = \"fulfilled\";\n fulfilledThenable.value = fulfilledValue;\n }\n },\n function (error) {\n if (\"pending\" === thenable.status) {\n var rejectedThenable = thenable;\n rejectedThenable.status = \"rejected\";\n rejectedThenable.reason = error;\n }\n }\n );\n }\n switch (thenable.status) {\n case \"fulfilled\":\n return thenable.value;\n case \"rejected\":\n throw (\n ((thenableState = thenable.reason),\n checkIfUseWrappedInAsyncCatch(thenableState),\n thenableState)\n );\n }\n suspendedThenable = thenable;\n throw SuspenseException;\n }\n}\nfunction resolveLazy(lazyType) {\n try {\n var init = lazyType._init;\n return init(lazyType._payload);\n } catch (x) {\n if (null !== x && \"object\" === typeof x && \"function\" === typeof x.then)\n throw ((suspendedThenable = x), SuspenseException);\n throw x;\n }\n}\nvar suspendedThenable = null;\nfunction getSuspendedThenable() {\n if (null === suspendedThenable) throw Error(formatProdErrorMessage(459));\n var thenable = suspendedThenable;\n suspendedThenable = null;\n return thenable;\n}\nfunction checkIfUseWrappedInAsyncCatch(rejectedReason) {\n if (\n rejectedReason === SuspenseException ||\n rejectedReason === SuspenseActionException\n )\n throw Error(formatProdErrorMessage(483));\n}\nvar thenableState$1 = null,\n thenableIndexCounter$1 = 0;\nfunction unwrapThenable(thenable) {\n var index = thenableIndexCounter$1;\n thenableIndexCounter$1 += 1;\n null === thenableState$1 && (thenableState$1 = []);\n return trackUsedThenable(thenableState$1, thenable, index);\n}\nfunction coerceRef(workInProgress, element) {\n element = element.props.ref;\n workInProgress.ref = void 0 !== element ? element : null;\n}\nfunction throwOnInvalidObjectTypeImpl(returnFiber, newChild) {\n if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE)\n throw Error(formatProdErrorMessage(525));\n returnFiber = Object.prototype.toString.call(newChild);\n throw Error(\n formatProdErrorMessage(\n 31,\n \"[object Object]\" === returnFiber\n ? \"object with keys {\" + Object.keys(newChild).join(\", \") + \"}\"\n : returnFiber\n )\n );\n}\nfunction createChildReconciler(shouldTrackSideEffects) {\n function deleteChild(returnFiber, childToDelete) {\n if (shouldTrackSideEffects) {\n var deletions = returnFiber.deletions;\n null === deletions\n ? ((returnFiber.deletions = [childToDelete]), (returnFiber.flags |= 16))\n : deletions.push(childToDelete);\n }\n }\n function deleteRemainingChildren(returnFiber, currentFirstChild) {\n if (!shouldTrackSideEffects) return null;\n for (; null !== currentFirstChild; )\n deleteChild(returnFiber, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return null;\n }\n function mapRemainingChildren(currentFirstChild) {\n for (var existingChildren = new Map(); null !== currentFirstChild; )\n null !== currentFirstChild.key\n ? existingChildren.set(currentFirstChild.key, currentFirstChild)\n : existingChildren.set(currentFirstChild.index, currentFirstChild),\n (currentFirstChild = currentFirstChild.sibling);\n return existingChildren;\n }\n function useFiber(fiber, pendingProps) {\n fiber = createWorkInProgress(fiber, pendingProps);\n fiber.index = 0;\n fiber.sibling = null;\n return fiber;\n }\n function placeChild(newFiber, lastPlacedIndex, newIndex) {\n newFiber.index = newIndex;\n if (!shouldTrackSideEffects)\n return (newFiber.flags |= 1048576), lastPlacedIndex;\n newIndex = newFiber.alternate;\n if (null !== newIndex)\n return (\n (newIndex = newIndex.index),\n newIndex < lastPlacedIndex\n ? ((newFiber.flags |= 67108866), lastPlacedIndex)\n : newIndex\n );\n newFiber.flags |= 67108866;\n return lastPlacedIndex;\n }\n function placeSingleChild(newFiber) {\n shouldTrackSideEffects &&\n null === newFiber.alternate &&\n (newFiber.flags |= 67108866);\n return newFiber;\n }\n function updateTextNode(returnFiber, current, textContent, lanes) {\n if (null === current || 6 !== current.tag)\n return (\n (current = createFiberFromText(textContent, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, textContent);\n current.return = returnFiber;\n return current;\n }\n function updateElement(returnFiber, current, element, lanes) {\n var elementType = element.type;\n if (elementType === REACT_FRAGMENT_TYPE)\n return updateFragment(\n returnFiber,\n current,\n element.props.children,\n lanes,\n element.key\n );\n if (\n null !== current &&\n (current.elementType === elementType ||\n (\"object\" === typeof elementType &&\n null !== elementType &&\n elementType.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(elementType) === current.type))\n )\n return (\n (current = useFiber(current, element.props)),\n coerceRef(current, element),\n (current.return = returnFiber),\n current\n );\n current = createFiberFromTypeAndProps(\n element.type,\n element.key,\n element.props,\n null,\n returnFiber.mode,\n lanes\n );\n coerceRef(current, element);\n current.return = returnFiber;\n return current;\n }\n function updatePortal(returnFiber, current, portal, lanes) {\n if (\n null === current ||\n 4 !== current.tag ||\n current.stateNode.containerInfo !== portal.containerInfo ||\n current.stateNode.implementation !== portal.implementation\n )\n return (\n (current = createFiberFromPortal(portal, returnFiber.mode, lanes)),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, portal.children || []);\n current.return = returnFiber;\n return current;\n }\n function updateFragment(returnFiber, current, fragment, lanes, key) {\n if (null === current || 7 !== current.tag)\n return (\n (current = createFiberFromFragment(\n fragment,\n returnFiber.mode,\n lanes,\n key\n )),\n (current.return = returnFiber),\n current\n );\n current = useFiber(current, fragment);\n current.return = returnFiber;\n return current;\n }\n function createChild(returnFiber, newChild, lanes) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (newChild = createFiberFromText(\n \"\" + newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n lanes\n );\n case REACT_PORTAL_TYPE:\n return (\n (newChild = createFiberFromPortal(\n newChild,\n returnFiber.mode,\n lanes\n )),\n (newChild.return = returnFiber),\n newChild\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n createChild(returnFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (newChild = createFiberFromFragment(\n newChild,\n returnFiber.mode,\n lanes,\n null\n )),\n (newChild.return = returnFiber),\n newChild\n );\n if (\"function\" === typeof newChild.then)\n return createChild(returnFiber, unwrapThenable(newChild), lanes);\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return createChild(\n returnFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateSlot(returnFiber, oldFiber, newChild, lanes) {\n var key = null !== oldFiber ? oldFiber.key : null;\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return null !== key\n ? null\n : updateTextNode(returnFiber, oldFiber, \"\" + newChild, lanes);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return newChild.key === key\n ? updateElement(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_PORTAL_TYPE:\n return newChild.key === key\n ? updatePortal(returnFiber, oldFiber, newChild, lanes)\n : null;\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateSlot(returnFiber, oldFiber, newChild, lanes)\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return null !== key\n ? null\n : updateFragment(returnFiber, oldFiber, newChild, lanes, null);\n if (\"function\" === typeof newChild.then)\n return updateSlot(\n returnFiber,\n oldFiber,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateSlot(\n returnFiber,\n oldFiber,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n ) {\n if (\n (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n )\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateTextNode(returnFiber, existingChildren, \"\" + newChild, lanes)\n );\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updateElement(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_PORTAL_TYPE:\n return (\n (existingChildren =\n existingChildren.get(\n null === newChild.key ? newIdx : newChild.key\n ) || null),\n updatePortal(returnFiber, existingChildren, newChild, lanes)\n );\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild) || getIteratorFn(newChild))\n return (\n (existingChildren = existingChildren.get(newIdx) || null),\n updateFragment(returnFiber, existingChildren, newChild, lanes, null)\n );\n if (\"function\" === typeof newChild.then)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return updateFromMap(\n existingChildren,\n returnFiber,\n newIdx,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return null;\n }\n function reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null;\n null !== oldFiber && newIdx < newChildren.length;\n newIdx++\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(\n returnFiber,\n oldFiber,\n newChildren[newIdx],\n lanes\n );\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (newIdx === newChildren.length)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; newIdx < newChildren.length; newIdx++)\n (oldFiber = createChild(returnFiber, newChildren[newIdx], lanes)),\n null !== oldFiber &&\n ((currentFirstChild = placeChild(\n oldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = oldFiber)\n : (previousNewFiber.sibling = oldFiber),\n (previousNewFiber = oldFiber));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n newIdx < newChildren.length;\n newIdx++\n )\n (nextOldFiber = updateFromMap(\n oldFiber,\n returnFiber,\n newIdx,\n newChildren[newIdx],\n lanes\n )),\n null !== nextOldFiber &&\n (shouldTrackSideEffects &&\n null !== nextOldFiber.alternate &&\n oldFiber.delete(\n null === nextOldFiber.key ? newIdx : nextOldFiber.key\n ),\n (currentFirstChild = placeChild(\n nextOldFiber,\n currentFirstChild,\n newIdx\n )),\n null === previousNewFiber\n ? (resultingFirstChild = nextOldFiber)\n : (previousNewFiber.sibling = nextOldFiber),\n (previousNewFiber = nextOldFiber));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChildren,\n lanes\n ) {\n if (null == newChildren) throw Error(formatProdErrorMessage(151));\n for (\n var resultingFirstChild = null,\n previousNewFiber = null,\n oldFiber = currentFirstChild,\n newIdx = (currentFirstChild = 0),\n nextOldFiber = null,\n step = newChildren.next();\n null !== oldFiber && !step.done;\n newIdx++, step = newChildren.next()\n ) {\n oldFiber.index > newIdx\n ? ((nextOldFiber = oldFiber), (oldFiber = null))\n : (nextOldFiber = oldFiber.sibling);\n var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);\n if (null === newFiber) {\n null === oldFiber && (oldFiber = nextOldFiber);\n break;\n }\n shouldTrackSideEffects &&\n oldFiber &&\n null === newFiber.alternate &&\n deleteChild(returnFiber, oldFiber);\n currentFirstChild = placeChild(newFiber, currentFirstChild, newIdx);\n null === previousNewFiber\n ? (resultingFirstChild = newFiber)\n : (previousNewFiber.sibling = newFiber);\n previousNewFiber = newFiber;\n oldFiber = nextOldFiber;\n }\n if (step.done)\n return (\n deleteRemainingChildren(returnFiber, oldFiber),\n isHydrating && pushTreeFork(returnFiber, newIdx),\n resultingFirstChild\n );\n if (null === oldFiber) {\n for (; !step.done; newIdx++, step = newChildren.next())\n (step = createChild(returnFiber, step.value, lanes)),\n null !== step &&\n ((currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n for (\n oldFiber = mapRemainingChildren(oldFiber);\n !step.done;\n newIdx++, step = newChildren.next()\n )\n (step = updateFromMap(oldFiber, returnFiber, newIdx, step.value, lanes)),\n null !== step &&\n (shouldTrackSideEffects &&\n null !== step.alternate &&\n oldFiber.delete(null === step.key ? newIdx : step.key),\n (currentFirstChild = placeChild(step, currentFirstChild, newIdx)),\n null === previousNewFiber\n ? (resultingFirstChild = step)\n : (previousNewFiber.sibling = step),\n (previousNewFiber = step));\n shouldTrackSideEffects &&\n oldFiber.forEach(function (child) {\n return deleteChild(returnFiber, child);\n });\n isHydrating && pushTreeFork(returnFiber, newIdx);\n return resultingFirstChild;\n }\n function reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n ) {\n \"object\" === typeof newChild &&\n null !== newChild &&\n newChild.type === REACT_FRAGMENT_TYPE &&\n null === newChild.key &&\n (newChild = newChild.props.children);\n if (\"object\" === typeof newChild && null !== newChild) {\n switch (newChild.$$typeof) {\n case REACT_ELEMENT_TYPE:\n a: {\n for (var key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key) {\n key = newChild.type;\n if (key === REACT_FRAGMENT_TYPE) {\n if (7 === currentFirstChild.tag) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(\n currentFirstChild,\n newChild.props.children\n );\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n } else if (\n currentFirstChild.elementType === key ||\n (\"object\" === typeof key &&\n null !== key &&\n key.$$typeof === REACT_LAZY_TYPE &&\n resolveLazy(key) === currentFirstChild.type)\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.props);\n coerceRef(lanes, newChild);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n }\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n } else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n newChild.type === REACT_FRAGMENT_TYPE\n ? ((lanes = createFiberFromFragment(\n newChild.props.children,\n returnFiber.mode,\n lanes,\n newChild.key\n )),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : ((lanes = createFiberFromTypeAndProps(\n newChild.type,\n newChild.key,\n newChild.props,\n null,\n returnFiber.mode,\n lanes\n )),\n coerceRef(lanes, newChild),\n (lanes.return = returnFiber),\n (returnFiber = lanes));\n }\n return placeSingleChild(returnFiber);\n case REACT_PORTAL_TYPE:\n a: {\n for (key = newChild.key; null !== currentFirstChild; ) {\n if (currentFirstChild.key === key)\n if (\n 4 === currentFirstChild.tag &&\n currentFirstChild.stateNode.containerInfo ===\n newChild.containerInfo &&\n currentFirstChild.stateNode.implementation ===\n newChild.implementation\n ) {\n deleteRemainingChildren(\n returnFiber,\n currentFirstChild.sibling\n );\n lanes = useFiber(currentFirstChild, newChild.children || []);\n lanes.return = returnFiber;\n returnFiber = lanes;\n break a;\n } else {\n deleteRemainingChildren(returnFiber, currentFirstChild);\n break;\n }\n else deleteChild(returnFiber, currentFirstChild);\n currentFirstChild = currentFirstChild.sibling;\n }\n lanes = createFiberFromPortal(newChild, returnFiber.mode, lanes);\n lanes.return = returnFiber;\n returnFiber = lanes;\n }\n return placeSingleChild(returnFiber);\n case REACT_LAZY_TYPE:\n return (\n (newChild = resolveLazy(newChild)),\n reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n )\n );\n }\n if (isArrayImpl(newChild))\n return reconcileChildrenArray(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n if (getIteratorFn(newChild)) {\n key = getIteratorFn(newChild);\n if (\"function\" !== typeof key) throw Error(formatProdErrorMessage(150));\n newChild = key.call(newChild);\n return reconcileChildrenIterator(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n }\n if (\"function\" === typeof newChild.then)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n unwrapThenable(newChild),\n lanes\n );\n if (newChild.$$typeof === REACT_CONTEXT_TYPE)\n return reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n readContextDuringReconciliation(returnFiber, newChild),\n lanes\n );\n throwOnInvalidObjectTypeImpl(returnFiber, newChild);\n }\n return (\"string\" === typeof newChild && \"\" !== newChild) ||\n \"number\" === typeof newChild ||\n \"bigint\" === typeof newChild\n ? ((newChild = \"\" + newChild),\n null !== currentFirstChild && 6 === currentFirstChild.tag\n ? (deleteRemainingChildren(returnFiber, currentFirstChild.sibling),\n (lanes = useFiber(currentFirstChild, newChild)),\n (lanes.return = returnFiber),\n (returnFiber = lanes))\n : (deleteRemainingChildren(returnFiber, currentFirstChild),\n (lanes = createFiberFromText(newChild, returnFiber.mode, lanes)),\n (lanes.return = returnFiber),\n (returnFiber = lanes)),\n placeSingleChild(returnFiber))\n : deleteRemainingChildren(returnFiber, currentFirstChild);\n }\n return function (returnFiber, currentFirstChild, newChild, lanes) {\n try {\n thenableIndexCounter$1 = 0;\n var firstChildFiber = reconcileChildFibersImpl(\n returnFiber,\n currentFirstChild,\n newChild,\n lanes\n );\n thenableState$1 = null;\n return firstChildFiber;\n } catch (x) {\n if (x === SuspenseException || x === SuspenseActionException) throw x;\n var fiber = createFiberImplClass(29, x, null, returnFiber.mode);\n fiber.lanes = lanes;\n fiber.return = returnFiber;\n return fiber;\n } finally {\n }\n };\n}\nvar reconcileChildFibers = createChildReconciler(!0),\n mountChildFibers = createChildReconciler(!1),\n hasForceUpdate = !1;\nfunction initializeUpdateQueue(fiber) {\n fiber.updateQueue = {\n baseState: fiber.memoizedState,\n firstBaseUpdate: null,\n lastBaseUpdate: null,\n shared: { pending: null, lanes: 0, hiddenCallbacks: null },\n callbacks: null\n };\n}\nfunction cloneUpdateQueue(current, workInProgress) {\n current = current.updateQueue;\n workInProgress.updateQueue === current &&\n (workInProgress.updateQueue = {\n baseState: current.baseState,\n firstBaseUpdate: current.firstBaseUpdate,\n lastBaseUpdate: current.lastBaseUpdate,\n shared: current.shared,\n callbacks: null\n });\n}\nfunction createUpdate(lane) {\n return { lane: lane, tag: 0, payload: null, callback: null, next: null };\n}\nfunction enqueueUpdate(fiber, update, lane) {\n var updateQueue = fiber.updateQueue;\n if (null === updateQueue) return null;\n updateQueue = updateQueue.shared;\n if (0 !== (executionContext & 2)) {\n var pending = updateQueue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n updateQueue.pending = update;\n update = getRootForUpdatedFiber(fiber);\n markUpdateLaneFromFiberToRoot(fiber, null, lane);\n return update;\n }\n enqueueUpdate$1(fiber, updateQueue, update, lane);\n return getRootForUpdatedFiber(fiber);\n}\nfunction entangleTransitions(root, fiber, lane) {\n fiber = fiber.updateQueue;\n if (null !== fiber && ((fiber = fiber.shared), 0 !== (lane & 4194048))) {\n var queueLanes = fiber.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n fiber.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nfunction enqueueCapturedUpdate(workInProgress, capturedUpdate) {\n var queue = workInProgress.updateQueue,\n current = workInProgress.alternate;\n if (\n null !== current &&\n ((current = current.updateQueue), queue === current)\n ) {\n var newFirst = null,\n newLast = null;\n queue = queue.firstBaseUpdate;\n if (null !== queue) {\n do {\n var clone = {\n lane: queue.lane,\n tag: queue.tag,\n payload: queue.payload,\n callback: null,\n next: null\n };\n null === newLast\n ? (newFirst = newLast = clone)\n : (newLast = newLast.next = clone);\n queue = queue.next;\n } while (null !== queue);\n null === newLast\n ? (newFirst = newLast = capturedUpdate)\n : (newLast = newLast.next = capturedUpdate);\n } else newFirst = newLast = capturedUpdate;\n queue = {\n baseState: current.baseState,\n firstBaseUpdate: newFirst,\n lastBaseUpdate: newLast,\n shared: current.shared,\n callbacks: current.callbacks\n };\n workInProgress.updateQueue = queue;\n return;\n }\n workInProgress = queue.lastBaseUpdate;\n null === workInProgress\n ? (queue.firstBaseUpdate = capturedUpdate)\n : (workInProgress.next = capturedUpdate);\n queue.lastBaseUpdate = capturedUpdate;\n}\nvar didReadFromEntangledAsyncAction = !1;\nfunction suspendIfUpdateReadFromEntangledAsyncAction() {\n if (didReadFromEntangledAsyncAction) {\n var entangledActionThenable = currentEntangledActionThenable;\n if (null !== entangledActionThenable) throw entangledActionThenable;\n }\n}\nfunction processUpdateQueue(\n workInProgress$jscomp$0,\n props,\n instance$jscomp$0,\n renderLanes\n) {\n didReadFromEntangledAsyncAction = !1;\n var queue = workInProgress$jscomp$0.updateQueue;\n hasForceUpdate = !1;\n var firstBaseUpdate = queue.firstBaseUpdate,\n lastBaseUpdate = queue.lastBaseUpdate,\n pendingQueue = queue.shared.pending;\n if (null !== pendingQueue) {\n queue.shared.pending = null;\n var lastPendingUpdate = pendingQueue,\n firstPendingUpdate = lastPendingUpdate.next;\n lastPendingUpdate.next = null;\n null === lastBaseUpdate\n ? (firstBaseUpdate = firstPendingUpdate)\n : (lastBaseUpdate.next = firstPendingUpdate);\n lastBaseUpdate = lastPendingUpdate;\n var current = workInProgress$jscomp$0.alternate;\n null !== current &&\n ((current = current.updateQueue),\n (pendingQueue = current.lastBaseUpdate),\n pendingQueue !== lastBaseUpdate &&\n (null === pendingQueue\n ? (current.firstBaseUpdate = firstPendingUpdate)\n : (pendingQueue.next = firstPendingUpdate),\n (current.lastBaseUpdate = lastPendingUpdate)));\n }\n if (null !== firstBaseUpdate) {\n var newState = queue.baseState;\n lastBaseUpdate = 0;\n current = firstPendingUpdate = lastPendingUpdate = null;\n pendingQueue = firstBaseUpdate;\n do {\n var updateLane = pendingQueue.lane & -536870913,\n isHiddenUpdate = updateLane !== pendingQueue.lane;\n if (\n isHiddenUpdate\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n 0 !== updateLane &&\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction = !0);\n null !== current &&\n (current = current.next =\n {\n lane: 0,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: null,\n next: null\n });\n a: {\n var workInProgress = workInProgress$jscomp$0,\n update = pendingQueue;\n updateLane = props;\n var instance = instance$jscomp$0;\n switch (update.tag) {\n case 1:\n workInProgress = update.payload;\n if (\"function\" === typeof workInProgress) {\n newState = workInProgress.call(instance, newState, updateLane);\n break a;\n }\n newState = workInProgress;\n break a;\n case 3:\n workInProgress.flags = (workInProgress.flags & -65537) | 128;\n case 0:\n workInProgress = update.payload;\n updateLane =\n \"function\" === typeof workInProgress\n ? workInProgress.call(instance, newState, updateLane)\n : workInProgress;\n if (null === updateLane || void 0 === updateLane) break a;\n newState = assign({}, newState, updateLane);\n break a;\n case 2:\n hasForceUpdate = !0;\n }\n }\n updateLane = pendingQueue.callback;\n null !== updateLane &&\n ((workInProgress$jscomp$0.flags |= 64),\n isHiddenUpdate && (workInProgress$jscomp$0.flags |= 8192),\n (isHiddenUpdate = queue.callbacks),\n null === isHiddenUpdate\n ? (queue.callbacks = [updateLane])\n : isHiddenUpdate.push(updateLane));\n } else\n (isHiddenUpdate = {\n lane: updateLane,\n tag: pendingQueue.tag,\n payload: pendingQueue.payload,\n callback: pendingQueue.callback,\n next: null\n }),\n null === current\n ? ((firstPendingUpdate = current = isHiddenUpdate),\n (lastPendingUpdate = newState))\n : (current = current.next = isHiddenUpdate),\n (lastBaseUpdate |= updateLane);\n pendingQueue = pendingQueue.next;\n if (null === pendingQueue)\n if (((pendingQueue = queue.shared.pending), null === pendingQueue))\n break;\n else\n (isHiddenUpdate = pendingQueue),\n (pendingQueue = isHiddenUpdate.next),\n (isHiddenUpdate.next = null),\n (queue.lastBaseUpdate = isHiddenUpdate),\n (queue.shared.pending = null);\n } while (1);\n null === current && (lastPendingUpdate = newState);\n queue.baseState = lastPendingUpdate;\n queue.firstBaseUpdate = firstPendingUpdate;\n queue.lastBaseUpdate = current;\n null === firstBaseUpdate && (queue.shared.lanes = 0);\n workInProgressRootSkippedLanes |= lastBaseUpdate;\n workInProgress$jscomp$0.lanes = lastBaseUpdate;\n workInProgress$jscomp$0.memoizedState = newState;\n }\n}\nfunction callCallback(callback, context) {\n if (\"function\" !== typeof callback)\n throw Error(formatProdErrorMessage(191, callback));\n callback.call(context);\n}\nfunction commitCallbacks(updateQueue, context) {\n var callbacks = updateQueue.callbacks;\n if (null !== callbacks)\n for (\n updateQueue.callbacks = null, updateQueue = 0;\n updateQueue < callbacks.length;\n updateQueue++\n )\n callCallback(callbacks[updateQueue], context);\n}\nvar currentTreeHiddenStackCursor = createCursor(null),\n prevEntangledRenderLanesCursor = createCursor(0);\nfunction pushHiddenContext(fiber, context) {\n fiber = entangledRenderLanes;\n push(prevEntangledRenderLanesCursor, fiber);\n push(currentTreeHiddenStackCursor, context);\n entangledRenderLanes = fiber | context.baseLanes;\n}\nfunction reuseHiddenContextOnStack() {\n push(prevEntangledRenderLanesCursor, entangledRenderLanes);\n push(currentTreeHiddenStackCursor, currentTreeHiddenStackCursor.current);\n}\nfunction popHiddenContext() {\n entangledRenderLanes = prevEntangledRenderLanesCursor.current;\n pop(currentTreeHiddenStackCursor);\n pop(prevEntangledRenderLanesCursor);\n}\nvar suspenseHandlerStackCursor = createCursor(null),\n shellBoundary = null;\nfunction pushPrimaryTreeSuspenseHandler(handler) {\n var current = handler.alternate;\n push(suspenseStackCursor, suspenseStackCursor.current & 1);\n push(suspenseHandlerStackCursor, handler);\n null === shellBoundary &&\n (null === current || null !== currentTreeHiddenStackCursor.current\n ? (shellBoundary = handler)\n : null !== current.memoizedState && (shellBoundary = handler));\n}\nfunction pushDehydratedActivitySuspenseHandler(fiber) {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, fiber);\n null === shellBoundary && (shellBoundary = fiber);\n}\nfunction pushOffscreenSuspenseHandler(fiber) {\n 22 === fiber.tag\n ? (push(suspenseStackCursor, suspenseStackCursor.current),\n push(suspenseHandlerStackCursor, fiber),\n null === shellBoundary && (shellBoundary = fiber))\n : reuseSuspenseHandlerOnStack(fiber);\n}\nfunction reuseSuspenseHandlerOnStack() {\n push(suspenseStackCursor, suspenseStackCursor.current);\n push(suspenseHandlerStackCursor, suspenseHandlerStackCursor.current);\n}\nfunction popSuspenseHandler(fiber) {\n pop(suspenseHandlerStackCursor);\n shellBoundary === fiber && (shellBoundary = null);\n pop(suspenseStackCursor);\n}\nvar suspenseStackCursor = createCursor(0);\nfunction findFirstSuspended(row) {\n for (var node = row; null !== node; ) {\n if (13 === node.tag) {\n var state = node.memoizedState;\n if (\n null !== state &&\n ((state = state.dehydrated),\n null === state ||\n isSuspenseInstancePending(state) ||\n isSuspenseInstanceFallback(state))\n )\n return node;\n } else if (\n 19 === node.tag &&\n (\"forwards\" === node.memoizedProps.revealOrder ||\n \"backwards\" === node.memoizedProps.revealOrder ||\n \"unstable_legacy-backwards\" === node.memoizedProps.revealOrder ||\n \"together\" === node.memoizedProps.revealOrder)\n ) {\n if (0 !== (node.flags & 128)) return node;\n } else if (null !== node.child) {\n node.child.return = node;\n node = node.child;\n continue;\n }\n if (node === row) break;\n for (; null === node.sibling; ) {\n if (null === node.return || node.return === row) return null;\n node = node.return;\n }\n node.sibling.return = node.return;\n node = node.sibling;\n }\n return null;\n}\nvar renderLanes = 0,\n currentlyRenderingFiber = null,\n currentHook = null,\n workInProgressHook = null,\n didScheduleRenderPhaseUpdate = !1,\n didScheduleRenderPhaseUpdateDuringThisPass = !1,\n shouldDoubleInvokeUserFnsInHooksDEV = !1,\n localIdCounter = 0,\n thenableIndexCounter = 0,\n thenableState = null,\n globalClientIdCounter = 0;\nfunction throwInvalidHookError() {\n throw Error(formatProdErrorMessage(321));\n}\nfunction areHookInputsEqual(nextDeps, prevDeps) {\n if (null === prevDeps) return !1;\n for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)\n if (!objectIs(nextDeps[i], prevDeps[i])) return !1;\n return !0;\n}\nfunction renderWithHooks(\n current,\n workInProgress,\n Component,\n props,\n secondArg,\n nextRenderLanes\n) {\n renderLanes = nextRenderLanes;\n currentlyRenderingFiber = workInProgress;\n workInProgress.memoizedState = null;\n workInProgress.updateQueue = null;\n workInProgress.lanes = 0;\n ReactSharedInternals.H =\n null === current || null === current.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate;\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n nextRenderLanes = Component(props, secondArg);\n shouldDoubleInvokeUserFnsInHooksDEV = !1;\n didScheduleRenderPhaseUpdateDuringThisPass &&\n (nextRenderLanes = renderWithHooksAgain(\n workInProgress,\n Component,\n props,\n secondArg\n ));\n finishRenderingHooks(current);\n return nextRenderLanes;\n}\nfunction finishRenderingHooks(current) {\n ReactSharedInternals.H = ContextOnlyDispatcher;\n var didRenderTooFewHooks = null !== currentHook && null !== currentHook.next;\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdate = !1;\n thenableIndexCounter = 0;\n thenableState = null;\n if (didRenderTooFewHooks) throw Error(formatProdErrorMessage(300));\n null === current ||\n didReceiveUpdate ||\n ((current = current.dependencies),\n null !== current &&\n checkIfContextChanged(current) &&\n (didReceiveUpdate = !0));\n}\nfunction renderWithHooksAgain(workInProgress, Component, props, secondArg) {\n currentlyRenderingFiber = workInProgress;\n var numberOfReRenders = 0;\n do {\n didScheduleRenderPhaseUpdateDuringThisPass && (thenableState = null);\n thenableIndexCounter = 0;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n if (25 <= numberOfReRenders) throw Error(formatProdErrorMessage(301));\n numberOfReRenders += 1;\n workInProgressHook = currentHook = null;\n if (null != workInProgress.updateQueue) {\n var children = workInProgress.updateQueue;\n children.lastEffect = null;\n children.events = null;\n children.stores = null;\n null != children.memoCache && (children.memoCache.index = 0);\n }\n ReactSharedInternals.H = HooksDispatcherOnRerender;\n children = Component(props, secondArg);\n } while (didScheduleRenderPhaseUpdateDuringThisPass);\n return children;\n}\nfunction TransitionAwareHostComponent() {\n var dispatcher = ReactSharedInternals.H,\n maybeThenable = dispatcher.useState()[0];\n maybeThenable =\n \"function\" === typeof maybeThenable.then\n ? useThenable(maybeThenable)\n : maybeThenable;\n dispatcher = dispatcher.useState()[0];\n (null !== currentHook ? currentHook.memoizedState : null) !== dispatcher &&\n (currentlyRenderingFiber.flags |= 1024);\n return maybeThenable;\n}\nfunction checkDidRenderIdHook() {\n var didRenderIdHook = 0 !== localIdCounter;\n localIdCounter = 0;\n return didRenderIdHook;\n}\nfunction bailoutHooks(current, workInProgress, lanes) {\n workInProgress.updateQueue = current.updateQueue;\n workInProgress.flags &= -2053;\n current.lanes &= ~lanes;\n}\nfunction resetHooksOnUnwind(workInProgress) {\n if (didScheduleRenderPhaseUpdate) {\n for (\n workInProgress = workInProgress.memoizedState;\n null !== workInProgress;\n\n ) {\n var queue = workInProgress.queue;\n null !== queue && (queue.pending = null);\n workInProgress = workInProgress.next;\n }\n didScheduleRenderPhaseUpdate = !1;\n }\n renderLanes = 0;\n workInProgressHook = currentHook = currentlyRenderingFiber = null;\n didScheduleRenderPhaseUpdateDuringThisPass = !1;\n thenableIndexCounter = localIdCounter = 0;\n thenableState = null;\n}\nfunction mountWorkInProgressHook() {\n var hook = {\n memoizedState: null,\n baseState: null,\n baseQueue: null,\n queue: null,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook = hook)\n : (workInProgressHook = workInProgressHook.next = hook);\n return workInProgressHook;\n}\nfunction updateWorkInProgressHook() {\n if (null === currentHook) {\n var nextCurrentHook = currentlyRenderingFiber.alternate;\n nextCurrentHook =\n null !== nextCurrentHook ? nextCurrentHook.memoizedState : null;\n } else nextCurrentHook = currentHook.next;\n var nextWorkInProgressHook =\n null === workInProgressHook\n ? currentlyRenderingFiber.memoizedState\n : workInProgressHook.next;\n if (null !== nextWorkInProgressHook)\n (workInProgressHook = nextWorkInProgressHook),\n (currentHook = nextCurrentHook);\n else {\n if (null === nextCurrentHook) {\n if (null === currentlyRenderingFiber.alternate)\n throw Error(formatProdErrorMessage(467));\n throw Error(formatProdErrorMessage(310));\n }\n currentHook = nextCurrentHook;\n nextCurrentHook = {\n memoizedState: currentHook.memoizedState,\n baseState: currentHook.baseState,\n baseQueue: currentHook.baseQueue,\n queue: currentHook.queue,\n next: null\n };\n null === workInProgressHook\n ? (currentlyRenderingFiber.memoizedState = workInProgressHook =\n nextCurrentHook)\n : (workInProgressHook = workInProgressHook.next = nextCurrentHook);\n }\n return workInProgressHook;\n}\nfunction createFunctionComponentUpdateQueue() {\n return { lastEffect: null, events: null, stores: null, memoCache: null };\n}\nfunction useThenable(thenable) {\n var index = thenableIndexCounter;\n thenableIndexCounter += 1;\n null === thenableState && (thenableState = []);\n thenable = trackUsedThenable(thenableState, thenable, index);\n index = currentlyRenderingFiber;\n null ===\n (null === workInProgressHook\n ? index.memoizedState\n : workInProgressHook.next) &&\n ((index = index.alternate),\n (ReactSharedInternals.H =\n null === index || null === index.memoizedState\n ? HooksDispatcherOnMount\n : HooksDispatcherOnUpdate));\n return thenable;\n}\nfunction use(usable) {\n if (null !== usable && \"object\" === typeof usable) {\n if (\"function\" === typeof usable.then) return useThenable(usable);\n if (usable.$$typeof === REACT_CONTEXT_TYPE) return readContext(usable);\n }\n throw Error(formatProdErrorMessage(438, String(usable)));\n}\nfunction useMemoCache(size) {\n var memoCache = null,\n updateQueue = currentlyRenderingFiber.updateQueue;\n null !== updateQueue && (memoCache = updateQueue.memoCache);\n if (null == memoCache) {\n var current = currentlyRenderingFiber.alternate;\n null !== current &&\n ((current = current.updateQueue),\n null !== current &&\n ((current = current.memoCache),\n null != current &&\n (memoCache = {\n data: current.data.map(function (array) {\n return array.slice();\n }),\n index: 0\n })));\n }\n null == memoCache && (memoCache = { data: [], index: 0 });\n null === updateQueue &&\n ((updateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = updateQueue));\n updateQueue.memoCache = memoCache;\n updateQueue = memoCache.data[memoCache.index];\n if (void 0 === updateQueue)\n for (\n updateQueue = memoCache.data[memoCache.index] = Array(size), current = 0;\n current < size;\n current++\n )\n updateQueue[current] = REACT_MEMO_CACHE_SENTINEL;\n memoCache.index++;\n return updateQueue;\n}\nfunction basicStateReducer(state, action) {\n return \"function\" === typeof action ? action(state) : action;\n}\nfunction updateReducer(reducer) {\n var hook = updateWorkInProgressHook();\n return updateReducerImpl(hook, currentHook, reducer);\n}\nfunction updateReducerImpl(hook, current, reducer) {\n var queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var baseQueue = hook.baseQueue,\n pendingQueue = queue.pending;\n if (null !== pendingQueue) {\n if (null !== baseQueue) {\n var baseFirst = baseQueue.next;\n baseQueue.next = pendingQueue.next;\n pendingQueue.next = baseFirst;\n }\n current.baseQueue = baseQueue = pendingQueue;\n queue.pending = null;\n }\n pendingQueue = hook.baseState;\n if (null === baseQueue) hook.memoizedState = pendingQueue;\n else {\n current = baseQueue.next;\n var newBaseQueueFirst = (baseFirst = null),\n newBaseQueueLast = null,\n update = current,\n didReadFromEntangledAsyncAction$60 = !1;\n do {\n var updateLane = update.lane & -536870913;\n if (\n updateLane !== update.lane\n ? (workInProgressRootRenderLanes & updateLane) === updateLane\n : (renderLanes & updateLane) === updateLane\n ) {\n var revertLane = update.revertLane;\n if (0 === revertLane)\n null !== newBaseQueueLast &&\n (newBaseQueueLast = newBaseQueueLast.next =\n {\n lane: 0,\n revertLane: 0,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n updateLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n else if ((renderLanes & revertLane) === revertLane) {\n update = update.next;\n revertLane === currentEntangledLane &&\n (didReadFromEntangledAsyncAction$60 = !0);\n continue;\n } else\n (updateLane = {\n lane: 0,\n revertLane: update.revertLane,\n gesture: null,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = updateLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = updateLane),\n (currentlyRenderingFiber.lanes |= revertLane),\n (workInProgressRootSkippedLanes |= revertLane);\n updateLane = update.action;\n shouldDoubleInvokeUserFnsInHooksDEV &&\n reducer(pendingQueue, updateLane);\n pendingQueue = update.hasEagerState\n ? update.eagerState\n : reducer(pendingQueue, updateLane);\n } else\n (revertLane = {\n lane: updateLane,\n revertLane: update.revertLane,\n gesture: update.gesture,\n action: update.action,\n hasEagerState: update.hasEagerState,\n eagerState: update.eagerState,\n next: null\n }),\n null === newBaseQueueLast\n ? ((newBaseQueueFirst = newBaseQueueLast = revertLane),\n (baseFirst = pendingQueue))\n : (newBaseQueueLast = newBaseQueueLast.next = revertLane),\n (currentlyRenderingFiber.lanes |= updateLane),\n (workInProgressRootSkippedLanes |= updateLane);\n update = update.next;\n } while (null !== update && update !== current);\n null === newBaseQueueLast\n ? (baseFirst = pendingQueue)\n : (newBaseQueueLast.next = newBaseQueueFirst);\n if (\n !objectIs(pendingQueue, hook.memoizedState) &&\n ((didReceiveUpdate = !0),\n didReadFromEntangledAsyncAction$60 &&\n ((reducer = currentEntangledActionThenable), null !== reducer))\n )\n throw reducer;\n hook.memoizedState = pendingQueue;\n hook.baseState = baseFirst;\n hook.baseQueue = newBaseQueueLast;\n queue.lastRenderedState = pendingQueue;\n }\n null === baseQueue && (queue.lanes = 0);\n return [hook.memoizedState, queue.dispatch];\n}\nfunction rerenderReducer(reducer) {\n var hook = updateWorkInProgressHook(),\n queue = hook.queue;\n if (null === queue) throw Error(formatProdErrorMessage(311));\n queue.lastRenderedReducer = reducer;\n var dispatch = queue.dispatch,\n lastRenderPhaseUpdate = queue.pending,\n newState = hook.memoizedState;\n if (null !== lastRenderPhaseUpdate) {\n queue.pending = null;\n var update = (lastRenderPhaseUpdate = lastRenderPhaseUpdate.next);\n do (newState = reducer(newState, update.action)), (update = update.next);\n while (update !== lastRenderPhaseUpdate);\n objectIs(newState, hook.memoizedState) || (didReceiveUpdate = !0);\n hook.memoizedState = newState;\n null === hook.baseQueue && (hook.baseState = newState);\n queue.lastRenderedState = newState;\n }\n return [newState, dispatch];\n}\nfunction updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = updateWorkInProgressHook(),\n isHydrating$jscomp$0 = isHydrating;\n if (isHydrating$jscomp$0) {\n if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else getServerSnapshot = getSnapshot();\n var snapshotChanged = !objectIs(\n (currentHook || hook).memoizedState,\n getServerSnapshot\n );\n snapshotChanged &&\n ((hook.memoizedState = getServerSnapshot), (didReceiveUpdate = !0));\n hook = hook.queue;\n updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [\n subscribe\n ]);\n if (\n hook.getSnapshot !== getSnapshot ||\n snapshotChanged ||\n (null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1)\n ) {\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n hook,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));\n isHydrating$jscomp$0 ||\n 0 !== (renderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n return getServerSnapshot;\n}\nfunction pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {\n fiber.flags |= 16384;\n fiber = { getSnapshot: getSnapshot, value: renderedSnapshot };\n getSnapshot = currentlyRenderingFiber.updateQueue;\n null === getSnapshot\n ? ((getSnapshot = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = getSnapshot),\n (getSnapshot.stores = [fiber]))\n : ((renderedSnapshot = getSnapshot.stores),\n null === renderedSnapshot\n ? (getSnapshot.stores = [fiber])\n : renderedSnapshot.push(fiber));\n}\nfunction updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {\n inst.value = nextSnapshot;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n}\nfunction subscribeToStore(fiber, inst, subscribe) {\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);\n });\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction forceStoreRerender(fiber) {\n var root = enqueueConcurrentRenderForLane(fiber, 2);\n null !== root && scheduleUpdateOnFiber(root, fiber, 2);\n}\nfunction mountStateImpl(initialState) {\n var hook = mountWorkInProgressHook();\n if (\"function\" === typeof initialState) {\n var initialStateInitializer = initialState;\n initialState = initialStateInitializer();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n initialStateInitializer();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n }\n hook.memoizedState = hook.baseState = initialState;\n hook.queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState\n };\n return hook;\n}\nfunction updateOptimisticImpl(hook, current, passthrough, reducer) {\n hook.baseState = passthrough;\n return updateReducerImpl(\n hook,\n currentHook,\n \"function\" === typeof reducer ? reducer : basicStateReducer\n );\n}\nfunction dispatchActionState(\n fiber,\n actionQueue,\n setPendingState,\n setState,\n payload\n) {\n if (isRenderPhaseUpdate(fiber)) throw Error(formatProdErrorMessage(485));\n fiber = actionQueue.action;\n if (null !== fiber) {\n var actionNode = {\n payload: payload,\n action: fiber,\n next: null,\n isTransition: !0,\n status: \"pending\",\n value: null,\n reason: null,\n listeners: [],\n then: function (listener) {\n actionNode.listeners.push(listener);\n }\n };\n null !== ReactSharedInternals.T\n ? setPendingState(!0)\n : (actionNode.isTransition = !1);\n setState(actionNode);\n setPendingState = actionQueue.pending;\n null === setPendingState\n ? ((actionNode.next = actionQueue.pending = actionNode),\n runActionStateAction(actionQueue, actionNode))\n : ((actionNode.next = setPendingState.next),\n (actionQueue.pending = setPendingState.next = actionNode));\n }\n}\nfunction runActionStateAction(actionQueue, node) {\n var action = node.action,\n payload = node.payload,\n prevState = actionQueue.state;\n if (node.isTransition) {\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n try {\n var returnValue = action(prevState, payload),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n handleActionReturnValue(actionQueue, node, returnValue);\n } catch (error) {\n onActionError(actionQueue, node, error);\n } finally {\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n } else\n try {\n (prevTransition = action(prevState, payload)),\n handleActionReturnValue(actionQueue, node, prevTransition);\n } catch (error$66) {\n onActionError(actionQueue, node, error$66);\n }\n}\nfunction handleActionReturnValue(actionQueue, node, returnValue) {\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ? returnValue.then(\n function (nextState) {\n onActionSuccess(actionQueue, node, nextState);\n },\n function (error) {\n return onActionError(actionQueue, node, error);\n }\n )\n : onActionSuccess(actionQueue, node, returnValue);\n}\nfunction onActionSuccess(actionQueue, actionNode, nextState) {\n actionNode.status = \"fulfilled\";\n actionNode.value = nextState;\n notifyActionListeners(actionNode);\n actionQueue.state = nextState;\n actionNode = actionQueue.pending;\n null !== actionNode &&\n ((nextState = actionNode.next),\n nextState === actionNode\n ? (actionQueue.pending = null)\n : ((nextState = nextState.next),\n (actionNode.next = nextState),\n runActionStateAction(actionQueue, nextState)));\n}\nfunction onActionError(actionQueue, actionNode, error) {\n var last = actionQueue.pending;\n actionQueue.pending = null;\n if (null !== last) {\n last = last.next;\n do\n (actionNode.status = \"rejected\"),\n (actionNode.reason = error),\n notifyActionListeners(actionNode),\n (actionNode = actionNode.next);\n while (actionNode !== last);\n }\n actionQueue.action = null;\n}\nfunction notifyActionListeners(actionNode) {\n actionNode = actionNode.listeners;\n for (var i = 0; i < actionNode.length; i++) (0, actionNode[i])();\n}\nfunction actionStateReducer(oldState, newState) {\n return newState;\n}\nfunction mountActionState(action, initialStateProp) {\n if (isHydrating) {\n var ssrFormState = workInProgressRoot.formState;\n if (null !== ssrFormState) {\n a: {\n var JSCompiler_inline_result = currentlyRenderingFiber;\n if (isHydrating) {\n if (nextHydratableInstance) {\n b: {\n var JSCompiler_inline_result$jscomp$0 = nextHydratableInstance;\n for (\n var inRootOrSingleton = rootOrSingletonContext;\n 8 !== JSCompiler_inline_result$jscomp$0.nodeType;\n\n ) {\n if (!inRootOrSingleton) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n JSCompiler_inline_result$jscomp$0 = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n if (null === JSCompiler_inline_result$jscomp$0) {\n JSCompiler_inline_result$jscomp$0 = null;\n break b;\n }\n }\n inRootOrSingleton = JSCompiler_inline_result$jscomp$0.data;\n JSCompiler_inline_result$jscomp$0 =\n \"F!\" === inRootOrSingleton || \"F\" === inRootOrSingleton\n ? JSCompiler_inline_result$jscomp$0\n : null;\n }\n if (JSCompiler_inline_result$jscomp$0) {\n nextHydratableInstance = getNextHydratable(\n JSCompiler_inline_result$jscomp$0.nextSibling\n );\n JSCompiler_inline_result =\n \"F!\" === JSCompiler_inline_result$jscomp$0.data;\n break a;\n }\n }\n throwOnHydrationMismatch(JSCompiler_inline_result);\n }\n JSCompiler_inline_result = !1;\n }\n JSCompiler_inline_result && (initialStateProp = ssrFormState[0]);\n }\n }\n ssrFormState = mountWorkInProgressHook();\n ssrFormState.memoizedState = ssrFormState.baseState = initialStateProp;\n JSCompiler_inline_result = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: actionStateReducer,\n lastRenderedState: initialStateProp\n };\n ssrFormState.queue = JSCompiler_inline_result;\n ssrFormState = dispatchSetState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result\n );\n JSCompiler_inline_result.dispatch = ssrFormState;\n JSCompiler_inline_result = mountStateImpl(!1);\n inRootOrSingleton = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !1,\n JSCompiler_inline_result.queue\n );\n JSCompiler_inline_result = mountWorkInProgressHook();\n JSCompiler_inline_result$jscomp$0 = {\n state: initialStateProp,\n dispatch: null,\n action: action,\n pending: null\n };\n JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;\n ssrFormState = dispatchActionState.bind(\n null,\n currentlyRenderingFiber,\n JSCompiler_inline_result$jscomp$0,\n inRootOrSingleton,\n ssrFormState\n );\n JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;\n JSCompiler_inline_result.memoizedState = action;\n return [initialStateProp, ssrFormState, !1];\n}\nfunction updateActionState(action) {\n var stateHook = updateWorkInProgressHook();\n return updateActionStateImpl(stateHook, currentHook, action);\n}\nfunction updateActionStateImpl(stateHook, currentStateHook, action) {\n currentStateHook = updateReducerImpl(\n stateHook,\n currentStateHook,\n actionStateReducer\n )[0];\n stateHook = updateReducer(basicStateReducer)[0];\n if (\n \"object\" === typeof currentStateHook &&\n null !== currentStateHook &&\n \"function\" === typeof currentStateHook.then\n )\n try {\n var state = useThenable(currentStateHook);\n } catch (x) {\n if (x === SuspenseException) throw SuspenseActionException;\n throw x;\n }\n else state = currentStateHook;\n currentStateHook = updateWorkInProgressHook();\n var actionQueue = currentStateHook.queue,\n dispatch = actionQueue.dispatch;\n action !== currentStateHook.memoizedState &&\n ((currentlyRenderingFiber.flags |= 2048),\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n actionStateActionEffect.bind(null, actionQueue, action),\n null\n ));\n return [state, dispatch, stateHook];\n}\nfunction actionStateActionEffect(actionQueue, action) {\n actionQueue.action = action;\n}\nfunction rerenderActionState(action) {\n var stateHook = updateWorkInProgressHook(),\n currentStateHook = currentHook;\n if (null !== currentStateHook)\n return updateActionStateImpl(stateHook, currentStateHook, action);\n updateWorkInProgressHook();\n stateHook = stateHook.memoizedState;\n currentStateHook = updateWorkInProgressHook();\n var dispatch = currentStateHook.queue.dispatch;\n currentStateHook.memoizedState = action;\n return [stateHook, dispatch, !1];\n}\nfunction pushSimpleEffect(tag, inst, create, deps) {\n tag = { tag: tag, create: create, deps: deps, inst: inst, next: null };\n inst = currentlyRenderingFiber.updateQueue;\n null === inst &&\n ((inst = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = inst));\n create = inst.lastEffect;\n null === create\n ? (inst.lastEffect = tag.next = tag)\n : ((deps = create.next),\n (create.next = tag),\n (tag.next = deps),\n (inst.lastEffect = tag));\n return tag;\n}\nfunction updateRef() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = mountWorkInProgressHook();\n currentlyRenderingFiber.flags |= fiberFlags;\n hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n { destroy: void 0 },\n create,\n void 0 === deps ? null : deps\n );\n}\nfunction updateEffectImpl(fiberFlags, hookFlags, create, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var inst = hook.memoizedState.inst;\n null !== currentHook &&\n null !== deps &&\n areHookInputsEqual(deps, currentHook.memoizedState.deps)\n ? (hook.memoizedState = pushSimpleEffect(hookFlags, inst, create, deps))\n : ((currentlyRenderingFiber.flags |= fiberFlags),\n (hook.memoizedState = pushSimpleEffect(\n 1 | hookFlags,\n inst,\n create,\n deps\n )));\n}\nfunction mountEffect(create, deps) {\n mountEffectImpl(8390656, 8, create, deps);\n}\nfunction updateEffect(create, deps) {\n updateEffectImpl(2048, 8, create, deps);\n}\nfunction useEffectEventImpl(payload) {\n currentlyRenderingFiber.flags |= 4;\n var componentUpdateQueue = currentlyRenderingFiber.updateQueue;\n if (null === componentUpdateQueue)\n (componentUpdateQueue = createFunctionComponentUpdateQueue()),\n (currentlyRenderingFiber.updateQueue = componentUpdateQueue),\n (componentUpdateQueue.events = [payload]);\n else {\n var events = componentUpdateQueue.events;\n null === events\n ? (componentUpdateQueue.events = [payload])\n : events.push(payload);\n }\n}\nfunction updateEvent(callback) {\n var ref = updateWorkInProgressHook().memoizedState;\n useEffectEventImpl({ ref: ref, nextImpl: callback });\n return function () {\n if (0 !== (executionContext & 2)) throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n}\nfunction updateInsertionEffect(create, deps) {\n return updateEffectImpl(4, 2, create, deps);\n}\nfunction updateLayoutEffect(create, deps) {\n return updateEffectImpl(4, 4, create, deps);\n}\nfunction imperativeHandleEffect(create, ref) {\n if (\"function\" === typeof ref) {\n create = create();\n var refCleanup = ref(create);\n return function () {\n \"function\" === typeof refCleanup ? refCleanup() : ref(null);\n };\n }\n if (null !== ref && void 0 !== ref)\n return (\n (create = create()),\n (ref.current = create),\n function () {\n ref.current = null;\n }\n );\n}\nfunction updateImperativeHandle(ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n updateEffectImpl(4, 4, imperativeHandleEffect.bind(null, create, ref), deps);\n}\nfunction mountDebugValue() {}\nfunction updateCallback(callback, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n hook.memoizedState = [callback, deps];\n return callback;\n}\nfunction updateMemo(nextCreate, deps) {\n var hook = updateWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var prevState = hook.memoizedState;\n if (null !== deps && areHookInputsEqual(deps, prevState[1]))\n return prevState[0];\n prevState = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [prevState, deps];\n return prevState;\n}\nfunction mountDeferredValueImpl(hook, value, initialValue) {\n if (\n void 0 === initialValue ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (hook.memoizedState = value);\n hook.memoizedState = initialValue;\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return initialValue;\n}\nfunction updateDeferredValueImpl(hook, prevValue, value, initialValue) {\n if (objectIs(value, prevValue)) return value;\n if (null !== currentTreeHiddenStackCursor.current)\n return (\n (hook = mountDeferredValueImpl(hook, value, initialValue)),\n objectIs(hook, prevValue) || (didReceiveUpdate = !0),\n hook\n );\n if (\n 0 === (renderLanes & 42) ||\n (0 !== (renderLanes & 1073741824) &&\n 0 === (workInProgressRootRenderLanes & 261930))\n )\n return (didReceiveUpdate = !0), (hook.memoizedState = value);\n hook = requestDeferredLane();\n currentlyRenderingFiber.lanes |= hook;\n workInProgressRootSkippedLanes |= hook;\n return prevValue;\n}\nfunction startTransition(fiber, queue, pendingState, finishedState, callback) {\n var previousPriority = ReactDOMSharedInternals.p;\n ReactDOMSharedInternals.p =\n 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8;\n var prevTransition = ReactSharedInternals.T,\n currentTransition = {};\n ReactSharedInternals.T = currentTransition;\n dispatchOptimisticSetState(fiber, !1, queue, pendingState);\n try {\n var returnValue = callback(),\n onStartTransitionFinish = ReactSharedInternals.S;\n null !== onStartTransitionFinish &&\n onStartTransitionFinish(currentTransition, returnValue);\n if (\n null !== returnValue &&\n \"object\" === typeof returnValue &&\n \"function\" === typeof returnValue.then\n ) {\n var thenableForFinishedState = chainThenableValue(\n returnValue,\n finishedState\n );\n dispatchSetStateInternal(\n fiber,\n queue,\n thenableForFinishedState,\n requestUpdateLane(fiber)\n );\n } else\n dispatchSetStateInternal(\n fiber,\n queue,\n finishedState,\n requestUpdateLane(fiber)\n );\n } catch (error) {\n dispatchSetStateInternal(\n fiber,\n queue,\n { then: function () {}, status: \"rejected\", reason: error },\n requestUpdateLane()\n );\n } finally {\n (ReactDOMSharedInternals.p = previousPriority),\n null !== prevTransition &&\n null !== currentTransition.types &&\n (prevTransition.types = currentTransition.types),\n (ReactSharedInternals.T = prevTransition);\n }\n}\nfunction noop() {}\nfunction startHostTransition(formFiber, pendingState, action, formData) {\n if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));\n var queue = ensureFormComponentIsStateful(formFiber).queue;\n startTransition(\n formFiber,\n queue,\n pendingState,\n sharedNotPendingObject,\n null === action\n ? noop\n : function () {\n requestFormReset$1(formFiber);\n return action(formData);\n }\n );\n}\nfunction ensureFormComponentIsStateful(formFiber) {\n var existingStateHook = formFiber.memoizedState;\n if (null !== existingStateHook) return existingStateHook;\n existingStateHook = {\n memoizedState: sharedNotPendingObject,\n baseState: sharedNotPendingObject,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: sharedNotPendingObject\n },\n next: null\n };\n var initialResetState = {};\n existingStateHook.next = {\n memoizedState: initialResetState,\n baseState: initialResetState,\n baseQueue: null,\n queue: {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialResetState\n },\n next: null\n };\n formFiber.memoizedState = existingStateHook;\n formFiber = formFiber.alternate;\n null !== formFiber && (formFiber.memoizedState = existingStateHook);\n return existingStateHook;\n}\nfunction requestFormReset$1(formFiber) {\n var stateHook = ensureFormComponentIsStateful(formFiber);\n null === stateHook.next && (stateHook = formFiber.alternate.memoizedState);\n dispatchSetStateInternal(\n formFiber,\n stateHook.next.queue,\n {},\n requestUpdateLane()\n );\n}\nfunction useHostTransitionStatus() {\n return readContext(HostTransitionContext);\n}\nfunction updateId() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction updateRefresh() {\n return updateWorkInProgressHook().memoizedState;\n}\nfunction refreshCache(fiber) {\n for (var provider = fiber.return; null !== provider; ) {\n switch (provider.tag) {\n case 24:\n case 3:\n var lane = requestUpdateLane();\n fiber = createUpdate(lane);\n var root$69 = enqueueUpdate(provider, fiber, lane);\n null !== root$69 &&\n (scheduleUpdateOnFiber(root$69, provider, lane),\n entangleTransitions(root$69, provider, lane));\n provider = { cache: createCache() };\n fiber.payload = provider;\n return;\n }\n provider = provider.return;\n }\n}\nfunction dispatchReducerAction(fiber, queue, action) {\n var lane = requestUpdateLane();\n action = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n isRenderPhaseUpdate(fiber)\n ? enqueueRenderPhaseUpdate(queue, action)\n : ((action = enqueueConcurrentHookUpdate(fiber, queue, action, lane)),\n null !== action &&\n (scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane)));\n}\nfunction dispatchSetState(fiber, queue, action) {\n var lane = requestUpdateLane();\n dispatchSetStateInternal(fiber, queue, action, lane);\n}\nfunction dispatchSetStateInternal(fiber, queue, action, lane) {\n var update = {\n lane: lane,\n revertLane: 0,\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) enqueueRenderPhaseUpdate(queue, update);\n else {\n var alternate = fiber.alternate;\n if (\n 0 === fiber.lanes &&\n (null === alternate || 0 === alternate.lanes) &&\n ((alternate = queue.lastRenderedReducer), null !== alternate)\n )\n try {\n var currentState = queue.lastRenderedState,\n eagerState = alternate(currentState, action);\n update.hasEagerState = !0;\n update.eagerState = eagerState;\n if (objectIs(eagerState, currentState))\n return (\n enqueueUpdate$1(fiber, queue, update, 0),\n null === workInProgressRoot && finishQueueingConcurrentUpdates(),\n !1\n );\n } catch (error) {\n } finally {\n }\n action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n if (null !== action)\n return (\n scheduleUpdateOnFiber(action, fiber, lane),\n entangleTransitionUpdate(action, queue, lane),\n !0\n );\n }\n return !1;\n}\nfunction dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {\n action = {\n lane: 2,\n revertLane: requestTransitionLane(),\n gesture: null,\n action: action,\n hasEagerState: !1,\n eagerState: null,\n next: null\n };\n if (isRenderPhaseUpdate(fiber)) {\n if (throwIfDuringRender) throw Error(formatProdErrorMessage(479));\n } else\n (throwIfDuringRender = enqueueConcurrentHookUpdate(\n fiber,\n queue,\n action,\n 2\n )),\n null !== throwIfDuringRender &&\n scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);\n}\nfunction isRenderPhaseUpdate(fiber) {\n var alternate = fiber.alternate;\n return (\n fiber === currentlyRenderingFiber ||\n (null !== alternate && alternate === currentlyRenderingFiber)\n );\n}\nfunction enqueueRenderPhaseUpdate(queue, update) {\n didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =\n !0;\n var pending = queue.pending;\n null === pending\n ? (update.next = update)\n : ((update.next = pending.next), (pending.next = update));\n queue.pending = update;\n}\nfunction entangleTransitionUpdate(root, queue, lane) {\n if (0 !== (lane & 4194048)) {\n var queueLanes = queue.lanes;\n queueLanes &= root.pendingLanes;\n lane |= queueLanes;\n queue.lanes = lane;\n markRootEntangled(root, lane);\n }\n}\nvar ContextOnlyDispatcher = {\n readContext: readContext,\n use: use,\n useCallback: throwInvalidHookError,\n useContext: throwInvalidHookError,\n useEffect: throwInvalidHookError,\n useImperativeHandle: throwInvalidHookError,\n useLayoutEffect: throwInvalidHookError,\n useInsertionEffect: throwInvalidHookError,\n useMemo: throwInvalidHookError,\n useReducer: throwInvalidHookError,\n useRef: throwInvalidHookError,\n useState: throwInvalidHookError,\n useDebugValue: throwInvalidHookError,\n useDeferredValue: throwInvalidHookError,\n useTransition: throwInvalidHookError,\n useSyncExternalStore: throwInvalidHookError,\n useId: throwInvalidHookError,\n useHostTransitionStatus: throwInvalidHookError,\n useFormState: throwInvalidHookError,\n useActionState: throwInvalidHookError,\n useOptimistic: throwInvalidHookError,\n useMemoCache: throwInvalidHookError,\n useCacheRefresh: throwInvalidHookError\n};\nContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;\nvar HooksDispatcherOnMount = {\n readContext: readContext,\n use: use,\n useCallback: function (callback, deps) {\n mountWorkInProgressHook().memoizedState = [\n callback,\n void 0 === deps ? null : deps\n ];\n return callback;\n },\n useContext: readContext,\n useEffect: mountEffect,\n useImperativeHandle: function (ref, create, deps) {\n deps = null !== deps && void 0 !== deps ? deps.concat([ref]) : null;\n mountEffectImpl(\n 4194308,\n 4,\n imperativeHandleEffect.bind(null, create, ref),\n deps\n );\n },\n useLayoutEffect: function (create, deps) {\n return mountEffectImpl(4194308, 4, create, deps);\n },\n useInsertionEffect: function (create, deps) {\n mountEffectImpl(4, 2, create, deps);\n },\n useMemo: function (nextCreate, deps) {\n var hook = mountWorkInProgressHook();\n deps = void 0 === deps ? null : deps;\n var nextValue = nextCreate();\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n nextCreate();\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n hook.memoizedState = [nextValue, deps];\n return nextValue;\n },\n useReducer: function (reducer, initialArg, init) {\n var hook = mountWorkInProgressHook();\n if (void 0 !== init) {\n var initialState = init(initialArg);\n if (shouldDoubleInvokeUserFnsInHooksDEV) {\n setIsStrictModeForDevtools(!0);\n try {\n init(initialArg);\n } finally {\n setIsStrictModeForDevtools(!1);\n }\n }\n } else initialState = initialArg;\n hook.memoizedState = hook.baseState = initialState;\n reducer = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: reducer,\n lastRenderedState: initialState\n };\n hook.queue = reducer;\n reducer = reducer.dispatch = dispatchReducerAction.bind(\n null,\n currentlyRenderingFiber,\n reducer\n );\n return [hook.memoizedState, reducer];\n },\n useRef: function (initialValue) {\n var hook = mountWorkInProgressHook();\n initialValue = { current: initialValue };\n return (hook.memoizedState = initialValue);\n },\n useState: function (initialState) {\n initialState = mountStateImpl(initialState);\n var queue = initialState.queue,\n dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);\n queue.dispatch = dispatch;\n return [initialState.memoizedState, dispatch];\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = mountWorkInProgressHook();\n return mountDeferredValueImpl(hook, value, initialValue);\n },\n useTransition: function () {\n var stateHook = mountStateImpl(!1);\n stateHook = startTransition.bind(\n null,\n currentlyRenderingFiber,\n stateHook.queue,\n !0,\n !1\n );\n mountWorkInProgressHook().memoizedState = stateHook;\n return [!1, stateHook];\n },\n useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {\n var fiber = currentlyRenderingFiber,\n hook = mountWorkInProgressHook();\n if (isHydrating) {\n if (void 0 === getServerSnapshot)\n throw Error(formatProdErrorMessage(407));\n getServerSnapshot = getServerSnapshot();\n } else {\n getServerSnapshot = getSnapshot();\n if (null === workInProgressRoot)\n throw Error(formatProdErrorMessage(349));\n 0 !== (workInProgressRootRenderLanes & 127) ||\n pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);\n }\n hook.memoizedState = getServerSnapshot;\n var inst = { value: getServerSnapshot, getSnapshot: getSnapshot };\n hook.queue = inst;\n mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [\n subscribe\n ]);\n fiber.flags |= 2048;\n pushSimpleEffect(\n 9,\n { destroy: void 0 },\n updateStoreInstance.bind(\n null,\n fiber,\n inst,\n getServerSnapshot,\n getSnapshot\n ),\n null\n );\n return getServerSnapshot;\n },\n useId: function () {\n var hook = mountWorkInProgressHook(),\n identifierPrefix = workInProgressRoot.identifierPrefix;\n if (isHydrating) {\n var JSCompiler_inline_result = treeContextOverflow;\n var idWithLeadingBit = treeContextId;\n JSCompiler_inline_result =\n (\n idWithLeadingBit & ~(1 << (32 - clz32(idWithLeadingBit) - 1))\n ).toString(32) + JSCompiler_inline_result;\n identifierPrefix =\n \"_\" + identifierPrefix + \"R_\" + JSCompiler_inline_result;\n JSCompiler_inline_result = localIdCounter++;\n 0 < JSCompiler_inline_result &&\n (identifierPrefix += \"H\" + JSCompiler_inline_result.toString(32));\n identifierPrefix += \"_\";\n } else\n (JSCompiler_inline_result = globalClientIdCounter++),\n (identifierPrefix =\n \"_\" +\n identifierPrefix +\n \"r_\" +\n JSCompiler_inline_result.toString(32) +\n \"_\");\n return (hook.memoizedState = identifierPrefix);\n },\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: mountActionState,\n useActionState: mountActionState,\n useOptimistic: function (passthrough) {\n var hook = mountWorkInProgressHook();\n hook.memoizedState = hook.baseState = passthrough;\n var queue = {\n pending: null,\n lanes: 0,\n dispatch: null,\n lastRenderedReducer: null,\n lastRenderedState: null\n };\n hook.queue = queue;\n hook = dispatchOptimisticSetState.bind(\n null,\n currentlyRenderingFiber,\n !0,\n queue\n );\n queue.dispatch = hook;\n return [passthrough, hook];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: function () {\n return (mountWorkInProgressHook().memoizedState = refreshCache.bind(\n null,\n currentlyRenderingFiber\n ));\n },\n useEffectEvent: function (callback) {\n var hook = mountWorkInProgressHook(),\n ref = { impl: callback };\n hook.memoizedState = ref;\n return function () {\n if (0 !== (executionContext & 2))\n throw Error(formatProdErrorMessage(440));\n return ref.impl.apply(void 0, arguments);\n };\n }\n },\n HooksDispatcherOnUpdate = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: updateReducer,\n useRef: updateRef,\n useState: function () {\n return updateReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = updateReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: updateActionState,\n useActionState: updateActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n };\nHooksDispatcherOnUpdate.useEffectEvent = updateEvent;\nvar HooksDispatcherOnRerender = {\n readContext: readContext,\n use: use,\n useCallback: updateCallback,\n useContext: readContext,\n useEffect: updateEffect,\n useImperativeHandle: updateImperativeHandle,\n useInsertionEffect: updateInsertionEffect,\n useLayoutEffect: updateLayoutEffect,\n useMemo: updateMemo,\n useReducer: rerenderReducer,\n useRef: updateRef,\n useState: function () {\n return rerenderReducer(basicStateReducer);\n },\n useDebugValue: mountDebugValue,\n useDeferredValue: function (value, initialValue) {\n var hook = updateWorkInProgressHook();\n return null === currentHook\n ? mountDeferredValueImpl(hook, value, initialValue)\n : updateDeferredValueImpl(\n hook,\n currentHook.memoizedState,\n value,\n initialValue\n );\n },\n useTransition: function () {\n var booleanOrThenable = rerenderReducer(basicStateReducer)[0],\n start = updateWorkInProgressHook().memoizedState;\n return [\n \"boolean\" === typeof booleanOrThenable\n ? booleanOrThenable\n : useThenable(booleanOrThenable),\n start\n ];\n },\n useSyncExternalStore: updateSyncExternalStore,\n useId: updateId,\n useHostTransitionStatus: useHostTransitionStatus,\n useFormState: rerenderActionState,\n useActionState: rerenderActionState,\n useOptimistic: function (passthrough, reducer) {\n var hook = updateWorkInProgressHook();\n if (null !== currentHook)\n return updateOptimisticImpl(hook, currentHook, passthrough, reducer);\n hook.baseState = passthrough;\n return [passthrough, hook.queue.dispatch];\n },\n useMemoCache: useMemoCache,\n useCacheRefresh: updateRefresh\n};\nHooksDispatcherOnRerender.useEffectEvent = updateEvent;\nfunction applyDerivedStateFromProps(\n workInProgress,\n ctor,\n getDerivedStateFromProps,\n nextProps\n) {\n ctor = workInProgress.memoizedState;\n getDerivedStateFromProps = getDerivedStateFromProps(nextProps, ctor);\n getDerivedStateFromProps =\n null === getDerivedStateFromProps || void 0 === getDerivedStateFromProps\n ? ctor\n : assign({}, ctor, getDerivedStateFromProps);\n workInProgress.memoizedState = getDerivedStateFromProps;\n 0 === workInProgress.lanes &&\n (workInProgress.updateQueue.baseState = getDerivedStateFromProps);\n}\nvar classComponentUpdater = {\n enqueueSetState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueReplaceState: function (inst, payload, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 1;\n update.payload = payload;\n void 0 !== callback && null !== callback && (update.callback = callback);\n payload = enqueueUpdate(inst, update, lane);\n null !== payload &&\n (scheduleUpdateOnFiber(payload, inst, lane),\n entangleTransitions(payload, inst, lane));\n },\n enqueueForceUpdate: function (inst, callback) {\n inst = inst._reactInternals;\n var lane = requestUpdateLane(),\n update = createUpdate(lane);\n update.tag = 2;\n void 0 !== callback && null !== callback && (update.callback = callback);\n callback = enqueueUpdate(inst, update, lane);\n null !== callback &&\n (scheduleUpdateOnFiber(callback, inst, lane),\n entangleTransitions(callback, inst, lane));\n }\n};\nfunction checkShouldComponentUpdate(\n workInProgress,\n ctor,\n oldProps,\n newProps,\n oldState,\n newState,\n nextContext\n) {\n workInProgress = workInProgress.stateNode;\n return \"function\" === typeof workInProgress.shouldComponentUpdate\n ? workInProgress.shouldComponentUpdate(newProps, newState, nextContext)\n : ctor.prototype && ctor.prototype.isPureReactComponent\n ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)\n : !0;\n}\nfunction callComponentWillReceiveProps(\n workInProgress,\n instance,\n newProps,\n nextContext\n) {\n workInProgress = instance.state;\n \"function\" === typeof instance.componentWillReceiveProps &&\n instance.componentWillReceiveProps(newProps, nextContext);\n \"function\" === typeof instance.UNSAFE_componentWillReceiveProps &&\n instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);\n instance.state !== workInProgress &&\n classComponentUpdater.enqueueReplaceState(instance, instance.state, null);\n}\nfunction resolveClassComponentProps(Component, baseProps) {\n var newProps = baseProps;\n if (\"ref\" in baseProps) {\n newProps = {};\n for (var propName in baseProps)\n \"ref\" !== propName && (newProps[propName] = baseProps[propName]);\n }\n if ((Component = Component.defaultProps)) {\n newProps === baseProps && (newProps = assign({}, newProps));\n for (var propName$73 in Component)\n void 0 === newProps[propName$73] &&\n (newProps[propName$73] = Component[propName$73]);\n }\n return newProps;\n}\nfunction defaultOnUncaughtError(error) {\n reportGlobalError(error);\n}\nfunction defaultOnCaughtError(error) {\n console.error(error);\n}\nfunction defaultOnRecoverableError(error) {\n reportGlobalError(error);\n}\nfunction logUncaughtError(root, errorInfo) {\n try {\n var onUncaughtError = root.onUncaughtError;\n onUncaughtError(errorInfo.value, { componentStack: errorInfo.stack });\n } catch (e$74) {\n setTimeout(function () {\n throw e$74;\n });\n }\n}\nfunction logCaughtError(root, boundary, errorInfo) {\n try {\n var onCaughtError = root.onCaughtError;\n onCaughtError(errorInfo.value, {\n componentStack: errorInfo.stack,\n errorBoundary: 1 === boundary.tag ? boundary.stateNode : null\n });\n } catch (e$75) {\n setTimeout(function () {\n throw e$75;\n });\n }\n}\nfunction createRootErrorUpdate(root, errorInfo, lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n lane.payload = { element: null };\n lane.callback = function () {\n logUncaughtError(root, errorInfo);\n };\n return lane;\n}\nfunction createClassErrorUpdate(lane) {\n lane = createUpdate(lane);\n lane.tag = 3;\n return lane;\n}\nfunction initializeClassErrorUpdate(update, root, fiber, errorInfo) {\n var getDerivedStateFromError = fiber.type.getDerivedStateFromError;\n if (\"function\" === typeof getDerivedStateFromError) {\n var error = errorInfo.value;\n update.payload = function () {\n return getDerivedStateFromError(error);\n };\n update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n };\n }\n var inst = fiber.stateNode;\n null !== inst &&\n \"function\" === typeof inst.componentDidCatch &&\n (update.callback = function () {\n logCaughtError(root, fiber, errorInfo);\n \"function\" !== typeof getDerivedStateFromError &&\n (null === legacyErrorBoundariesThatAlreadyFailed\n ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this]))\n : legacyErrorBoundariesThatAlreadyFailed.add(this));\n var stack = errorInfo.stack;\n this.componentDidCatch(errorInfo.value, {\n componentStack: null !== stack ? stack : \"\"\n });\n });\n}\nfunction throwException(\n root,\n returnFiber,\n sourceFiber,\n value,\n rootRenderLanes\n) {\n sourceFiber.flags |= 32768;\n if (\n null !== value &&\n \"object\" === typeof value &&\n \"function\" === typeof value.then\n ) {\n returnFiber = sourceFiber.alternate;\n null !== returnFiber &&\n propagateParentContextChanges(\n returnFiber,\n sourceFiber,\n rootRenderLanes,\n !0\n );\n sourceFiber = suspenseHandlerStackCursor.current;\n if (null !== sourceFiber) {\n switch (sourceFiber.tag) {\n case 31:\n case 13:\n return (\n null === shellBoundary\n ? renderDidSuspendDelayIfPossible()\n : null === sourceFiber.alternate &&\n 0 === workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 3),\n (sourceFiber.flags &= -257),\n (sourceFiber.flags |= 65536),\n (sourceFiber.lanes = rootRenderLanes),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? (sourceFiber.updateQueue = new Set([value]))\n : returnFiber.add(value),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n case 22:\n return (\n (sourceFiber.flags |= 65536),\n value === noopSuspenseyCommitThenable\n ? (sourceFiber.flags |= 16384)\n : ((returnFiber = sourceFiber.updateQueue),\n null === returnFiber\n ? ((returnFiber = {\n transitions: null,\n markerInstances: null,\n retryQueue: new Set([value])\n }),\n (sourceFiber.updateQueue = returnFiber))\n : ((sourceFiber = returnFiber.retryQueue),\n null === sourceFiber\n ? (returnFiber.retryQueue = new Set([value]))\n : sourceFiber.add(value)),\n attachPingListener(root, value, rootRenderLanes)),\n !1\n );\n }\n throw Error(formatProdErrorMessage(435, sourceFiber.tag));\n }\n attachPingListener(root, value, rootRenderLanes);\n renderDidSuspendDelayIfPossible();\n return !1;\n }\n if (isHydrating)\n return (\n (returnFiber = suspenseHandlerStackCursor.current),\n null !== returnFiber\n ? (0 === (returnFiber.flags & 65536) && (returnFiber.flags |= 256),\n (returnFiber.flags |= 65536),\n (returnFiber.lanes = rootRenderLanes),\n value !== HydrationMismatchException &&\n ((root = Error(formatProdErrorMessage(422), { cause: value })),\n queueHydrationError(createCapturedValueAtFiber(root, sourceFiber))))\n : (value !== HydrationMismatchException &&\n ((returnFiber = Error(formatProdErrorMessage(423), {\n cause: value\n })),\n queueHydrationError(\n createCapturedValueAtFiber(returnFiber, sourceFiber)\n )),\n (root = root.current.alternate),\n (root.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (root.lanes |= rootRenderLanes),\n (value = createCapturedValueAtFiber(value, sourceFiber)),\n (rootRenderLanes = createRootErrorUpdate(\n root.stateNode,\n value,\n rootRenderLanes\n )),\n enqueueCapturedUpdate(root, rootRenderLanes),\n 4 !== workInProgressRootExitStatus &&\n (workInProgressRootExitStatus = 2)),\n !1\n );\n var wrapperError = Error(formatProdErrorMessage(520), { cause: value });\n wrapperError = createCapturedValueAtFiber(wrapperError, sourceFiber);\n null === workInProgressRootConcurrentErrors\n ? (workInProgressRootConcurrentErrors = [wrapperError])\n : workInProgressRootConcurrentErrors.push(wrapperError);\n 4 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2);\n if (null === returnFiber) return !0;\n value = createCapturedValueAtFiber(value, sourceFiber);\n sourceFiber = returnFiber;\n do {\n switch (sourceFiber.tag) {\n case 3:\n return (\n (sourceFiber.flags |= 65536),\n (root = rootRenderLanes & -rootRenderLanes),\n (sourceFiber.lanes |= root),\n (root = createRootErrorUpdate(sourceFiber.stateNode, value, root)),\n enqueueCapturedUpdate(sourceFiber, root),\n !1\n );\n case 1:\n if (\n ((returnFiber = sourceFiber.type),\n (wrapperError = sourceFiber.stateNode),\n 0 === (sourceFiber.flags & 128) &&\n (\"function\" === typeof returnFiber.getDerivedStateFromError ||\n (null !== wrapperError &&\n \"function\" === typeof wrapperError.componentDidCatch &&\n (null === legacyErrorBoundariesThatAlreadyFailed ||\n !legacyErrorBoundariesThatAlreadyFailed.has(wrapperError)))))\n )\n return (\n (sourceFiber.flags |= 65536),\n (rootRenderLanes &= -rootRenderLanes),\n (sourceFiber.lanes |= rootRenderLanes),\n (rootRenderLanes = createClassErrorUpdate(rootRenderLanes)),\n initializeClassErrorUpdate(\n rootRenderLanes,\n root,\n sourceFiber,\n value\n ),\n enqueueCapturedUpdate(sourceFiber, rootRenderLanes),\n !1\n );\n }\n sourceFiber = sourceFiber.return;\n } while (null !== sourceFiber);\n return !1;\n}\nvar SelectiveHydrationException = Error(formatProdErrorMessage(461)),\n didReceiveUpdate = !1;\nfunction reconcileChildren(current, workInProgress, nextChildren, renderLanes) {\n workInProgress.child =\n null === current\n ? mountChildFibers(workInProgress, null, nextChildren, renderLanes)\n : reconcileChildFibers(\n workInProgress,\n current.child,\n nextChildren,\n renderLanes\n );\n}\nfunction updateForwardRef(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n Component = Component.render;\n var ref = workInProgress.ref;\n if (\"ref\" in nextProps) {\n var propsWithoutRef = {};\n for (var key in nextProps)\n \"ref\" !== key && (propsWithoutRef[key] = nextProps[key]);\n } else propsWithoutRef = nextProps;\n prepareToReadContext(workInProgress);\n nextProps = renderWithHooks(\n current,\n workInProgress,\n Component,\n propsWithoutRef,\n ref,\n renderLanes\n );\n key = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && key && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null === current) {\n var type = Component.type;\n if (\n \"function\" === typeof type &&\n !shouldConstruct(type) &&\n void 0 === type.defaultProps &&\n null === Component.compare\n )\n return (\n (workInProgress.tag = 15),\n (workInProgress.type = type),\n updateSimpleMemoComponent(\n current,\n workInProgress,\n type,\n nextProps,\n renderLanes\n )\n );\n current = createFiberFromTypeAndProps(\n Component.type,\n null,\n nextProps,\n workInProgress,\n workInProgress.mode,\n renderLanes\n );\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n }\n type = current.child;\n if (!checkScheduledUpdateOrContext(current, renderLanes)) {\n var prevProps = type.memoizedProps;\n Component = Component.compare;\n Component = null !== Component ? Component : shallowEqual;\n if (Component(prevProps, nextProps) && current.ref === workInProgress.ref)\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n }\n workInProgress.flags |= 1;\n current = createWorkInProgress(type, nextProps);\n current.ref = workInProgress.ref;\n current.return = workInProgress;\n return (workInProgress.child = current);\n}\nfunction updateSimpleMemoComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n if (null !== current) {\n var prevProps = current.memoizedProps;\n if (\n shallowEqual(prevProps, nextProps) &&\n current.ref === workInProgress.ref\n )\n if (\n ((didReceiveUpdate = !1),\n (workInProgress.pendingProps = nextProps = prevProps),\n checkScheduledUpdateOrContext(current, renderLanes))\n )\n 0 !== (current.flags & 131072) && (didReceiveUpdate = !0);\n else\n return (\n (workInProgress.lanes = current.lanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n }\n return updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n );\n}\nfunction updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n nextProps\n) {\n var nextChildren = nextProps.children,\n prevState = null !== current ? current.memoizedState : null;\n null === current &&\n null === workInProgress.stateNode &&\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n if (\"hidden\" === nextProps.mode) {\n if (0 !== (workInProgress.flags & 128)) {\n prevState =\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes;\n if (null !== current) {\n nextProps = workInProgress.child = current.child;\n for (nextChildren = 0; null !== nextProps; )\n (nextChildren =\n nextChildren | nextProps.lanes | nextProps.childLanes),\n (nextProps = nextProps.sibling);\n nextProps = nextChildren & ~prevState;\n } else (nextProps = 0), (workInProgress.child = null);\n return deferHiddenOffscreenComponent(\n current,\n workInProgress,\n prevState,\n renderLanes,\n nextProps\n );\n }\n if (0 !== (renderLanes & 536870912))\n (workInProgress.memoizedState = { baseLanes: 0, cachePool: null }),\n null !== current &&\n pushTransition(\n workInProgress,\n null !== prevState ? prevState.cachePool : null\n ),\n null !== prevState\n ? pushHiddenContext(workInProgress, prevState)\n : reuseHiddenContextOnStack(),\n pushOffscreenSuspenseHandler(workInProgress);\n else\n return (\n (nextProps = workInProgress.lanes = 536870912),\n deferHiddenOffscreenComponent(\n current,\n workInProgress,\n null !== prevState ? prevState.baseLanes | renderLanes : renderLanes,\n renderLanes,\n nextProps\n )\n );\n } else\n null !== prevState\n ? (pushTransition(workInProgress, prevState.cachePool),\n pushHiddenContext(workInProgress, prevState),\n reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.memoizedState = null))\n : (null !== current && pushTransition(workInProgress, null),\n reuseHiddenContextOnStack(),\n reuseSuspenseHandlerOnStack(workInProgress));\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nfunction bailoutOffscreenComponent(current, workInProgress) {\n (null !== current && 22 === current.tag) ||\n null !== workInProgress.stateNode ||\n (workInProgress.stateNode = {\n _visibility: 1,\n _pendingMarkers: null,\n _retryCache: null,\n _transitions: null\n });\n return workInProgress.sibling;\n}\nfunction deferHiddenOffscreenComponent(\n current,\n workInProgress,\n nextBaseLanes,\n renderLanes,\n remainingChildLanes\n) {\n var JSCompiler_inline_result = peekCacheFromPool();\n JSCompiler_inline_result =\n null === JSCompiler_inline_result\n ? null\n : { parent: CacheContext._currentValue, pool: JSCompiler_inline_result };\n workInProgress.memoizedState = {\n baseLanes: nextBaseLanes,\n cachePool: JSCompiler_inline_result\n };\n null !== current && pushTransition(workInProgress, null);\n reuseHiddenContextOnStack();\n pushOffscreenSuspenseHandler(workInProgress);\n null !== current &&\n propagateParentContextChanges(current, workInProgress, renderLanes, !0);\n workInProgress.childLanes = remainingChildLanes;\n return null;\n}\nfunction mountActivityChildren(workInProgress, nextProps) {\n nextProps = mountWorkInProgressOffscreenFiber(\n { mode: nextProps.mode, children: nextProps.children },\n workInProgress.mode\n );\n nextProps.ref = workInProgress.ref;\n workInProgress.child = nextProps;\n nextProps.return = workInProgress;\n return nextProps;\n}\nfunction retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountActivityChildren(workInProgress, workInProgress.pendingProps);\n current.flags |= 2;\n popSuspenseHandler(workInProgress);\n workInProgress.memoizedState = null;\n return current;\n}\nfunction updateActivityComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n didSuspend = 0 !== (workInProgress.flags & 128);\n workInProgress.flags &= -129;\n if (null === current) {\n if (isHydrating) {\n if (\"hidden\" === nextProps.mode)\n return (\n (current = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.lanes = 536870912),\n bailoutOffscreenComponent(null, current)\n );\n pushDehydratedActivitySuspenseHandler(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" === current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n workInProgress.lanes = 536870912;\n return null;\n }\n return mountActivityChildren(workInProgress, nextProps);\n }\n var prevState = current.memoizedState;\n if (null !== prevState) {\n var dehydrated = prevState.dehydrated;\n pushDehydratedActivitySuspenseHandler(workInProgress);\n if (didSuspend)\n if (workInProgress.flags & 256)\n (workInProgress.flags &= -257),\n (workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n ));\n else if (null !== workInProgress.memoizedState)\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null);\n else throw Error(formatProdErrorMessage(558));\n else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (didSuspend = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || didSuspend)\n ) {\n nextProps = workInProgressRoot;\n if (\n null !== nextProps &&\n ((dehydrated = getBumpedLaneForHydration(nextProps, renderLanes)),\n 0 !== dehydrated && dehydrated !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = dehydrated),\n enqueueConcurrentRenderForLane(current, dehydrated),\n scheduleUpdateOnFiber(nextProps, current, dehydrated),\n SelectiveHydrationException)\n );\n renderDidSuspendDelayIfPossible();\n workInProgress = retryActivityComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n (current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(dehydrated.nextSibling)),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountActivityChildren(workInProgress, nextProps)),\n (workInProgress.flags |= 4096);\n return workInProgress;\n }\n current = createWorkInProgress(current.child, {\n mode: nextProps.mode,\n children: nextProps.children\n });\n current.ref = workInProgress.ref;\n workInProgress.child = current;\n current.return = workInProgress;\n return current;\n}\nfunction markRef(current, workInProgress) {\n var ref = workInProgress.ref;\n if (null === ref)\n null !== current &&\n null !== current.ref &&\n (workInProgress.flags |= 4194816);\n else {\n if (\"function\" !== typeof ref && \"object\" !== typeof ref)\n throw Error(formatProdErrorMessage(284));\n if (null === current || current.ref !== ref)\n workInProgress.flags |= 4194816;\n }\n}\nfunction updateFunctionComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n Component = renderWithHooks(\n current,\n workInProgress,\n Component,\n nextProps,\n void 0,\n renderLanes\n );\n nextProps = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && nextProps && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, Component, renderLanes);\n return workInProgress.child;\n}\nfunction replayFunctionComponent(\n current,\n workInProgress,\n nextProps,\n Component,\n secondArg,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n workInProgress.updateQueue = null;\n nextProps = renderWithHooksAgain(\n workInProgress,\n Component,\n nextProps,\n secondArg\n );\n finishRenderingHooks(current);\n Component = checkDidRenderIdHook();\n if (null !== current && !didReceiveUpdate)\n return (\n bailoutHooks(current, workInProgress, renderLanes),\n bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes)\n );\n isHydrating && Component && pushMaterializedTreeId(workInProgress);\n workInProgress.flags |= 1;\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n return workInProgress.child;\n}\nfunction updateClassComponent(\n current,\n workInProgress,\n Component,\n nextProps,\n renderLanes\n) {\n prepareToReadContext(workInProgress);\n if (null === workInProgress.stateNode) {\n var context = emptyContextObject,\n contextType = Component.contextType;\n \"object\" === typeof contextType &&\n null !== contextType &&\n (context = readContext(contextType));\n context = new Component(nextProps, context);\n workInProgress.memoizedState =\n null !== context.state && void 0 !== context.state ? context.state : null;\n context.updater = classComponentUpdater;\n workInProgress.stateNode = context;\n context._reactInternals = workInProgress;\n context = workInProgress.stateNode;\n context.props = nextProps;\n context.state = workInProgress.memoizedState;\n context.refs = {};\n initializeUpdateQueue(workInProgress);\n contextType = Component.contextType;\n context.context =\n \"object\" === typeof contextType && null !== contextType\n ? readContext(contextType)\n : emptyContextObject;\n context.state = workInProgress.memoizedState;\n contextType = Component.getDerivedStateFromProps;\n \"function\" === typeof contextType &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n contextType,\n nextProps\n ),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof Component.getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n ((contextType = context.state),\n \"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount(),\n contextType !== context.state &&\n classComponentUpdater.enqueueReplaceState(context, context.state, null),\n processUpdateQueue(workInProgress, nextProps, context, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction(),\n (context.state = workInProgress.memoizedState));\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308);\n nextProps = !0;\n } else if (null === current) {\n context = workInProgress.stateNode;\n var unresolvedOldProps = workInProgress.memoizedProps,\n oldProps = resolveClassComponentProps(Component, unresolvedOldProps);\n context.props = oldProps;\n var oldContext = context.context,\n contextType$jscomp$0 = Component.contextType;\n contextType = emptyContextObject;\n \"object\" === typeof contextType$jscomp$0 &&\n null !== contextType$jscomp$0 &&\n (contextType = readContext(contextType$jscomp$0));\n var getDerivedStateFromProps = Component.getDerivedStateFromProps;\n contextType$jscomp$0 =\n \"function\" === typeof getDerivedStateFromProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate;\n unresolvedOldProps = workInProgress.pendingProps !== unresolvedOldProps;\n contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((unresolvedOldProps || oldContext !== contextType) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n contextType\n ));\n hasForceUpdate = !1;\n var oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n oldContext = workInProgress.memoizedState;\n unresolvedOldProps || oldState !== oldContext || hasForceUpdate\n ? (\"function\" === typeof getDerivedStateFromProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n getDerivedStateFromProps,\n nextProps\n ),\n (oldContext = workInProgress.memoizedState)),\n (oldProps =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n oldProps,\n nextProps,\n oldState,\n oldContext,\n contextType\n ))\n ? (contextType$jscomp$0 ||\n (\"function\" !== typeof context.UNSAFE_componentWillMount &&\n \"function\" !== typeof context.componentWillMount) ||\n (\"function\" === typeof context.componentWillMount &&\n context.componentWillMount(),\n \"function\" === typeof context.UNSAFE_componentWillMount &&\n context.UNSAFE_componentWillMount()),\n \"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = oldContext)),\n (context.props = nextProps),\n (context.state = oldContext),\n (context.context = contextType),\n (nextProps = oldProps))\n : (\"function\" === typeof context.componentDidMount &&\n (workInProgress.flags |= 4194308),\n (nextProps = !1));\n } else {\n context = workInProgress.stateNode;\n cloneUpdateQueue(current, workInProgress);\n contextType = workInProgress.memoizedProps;\n contextType$jscomp$0 = resolveClassComponentProps(Component, contextType);\n context.props = contextType$jscomp$0;\n getDerivedStateFromProps = workInProgress.pendingProps;\n oldState = context.context;\n oldContext = Component.contextType;\n oldProps = emptyContextObject;\n \"object\" === typeof oldContext &&\n null !== oldContext &&\n (oldProps = readContext(oldContext));\n unresolvedOldProps = Component.getDerivedStateFromProps;\n (oldContext =\n \"function\" === typeof unresolvedOldProps ||\n \"function\" === typeof context.getSnapshotBeforeUpdate) ||\n (\"function\" !== typeof context.UNSAFE_componentWillReceiveProps &&\n \"function\" !== typeof context.componentWillReceiveProps) ||\n ((contextType !== getDerivedStateFromProps || oldState !== oldProps) &&\n callComponentWillReceiveProps(\n workInProgress,\n context,\n nextProps,\n oldProps\n ));\n hasForceUpdate = !1;\n oldState = workInProgress.memoizedState;\n context.state = oldState;\n processUpdateQueue(workInProgress, nextProps, context, renderLanes);\n suspendIfUpdateReadFromEntangledAsyncAction();\n var newState = workInProgress.memoizedState;\n contextType !== getDerivedStateFromProps ||\n oldState !== newState ||\n hasForceUpdate ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies))\n ? (\"function\" === typeof unresolvedOldProps &&\n (applyDerivedStateFromProps(\n workInProgress,\n Component,\n unresolvedOldProps,\n nextProps\n ),\n (newState = workInProgress.memoizedState)),\n (contextType$jscomp$0 =\n hasForceUpdate ||\n checkShouldComponentUpdate(\n workInProgress,\n Component,\n contextType$jscomp$0,\n nextProps,\n oldState,\n newState,\n oldProps\n ) ||\n (null !== current &&\n null !== current.dependencies &&\n checkIfContextChanged(current.dependencies)))\n ? (oldContext ||\n (\"function\" !== typeof context.UNSAFE_componentWillUpdate &&\n \"function\" !== typeof context.componentWillUpdate) ||\n (\"function\" === typeof context.componentWillUpdate &&\n context.componentWillUpdate(nextProps, newState, oldProps),\n \"function\" === typeof context.UNSAFE_componentWillUpdate &&\n context.UNSAFE_componentWillUpdate(\n nextProps,\n newState,\n oldProps\n )),\n \"function\" === typeof context.componentDidUpdate &&\n (workInProgress.flags |= 4),\n \"function\" === typeof context.getSnapshotBeforeUpdate &&\n (workInProgress.flags |= 1024))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (workInProgress.memoizedProps = nextProps),\n (workInProgress.memoizedState = newState)),\n (context.props = nextProps),\n (context.state = newState),\n (context.context = oldProps),\n (nextProps = contextType$jscomp$0))\n : (\"function\" !== typeof context.componentDidUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 4),\n \"function\" !== typeof context.getSnapshotBeforeUpdate ||\n (contextType === current.memoizedProps &&\n oldState === current.memoizedState) ||\n (workInProgress.flags |= 1024),\n (nextProps = !1));\n }\n context = nextProps;\n markRef(current, workInProgress);\n nextProps = 0 !== (workInProgress.flags & 128);\n context || nextProps\n ? ((context = workInProgress.stateNode),\n (Component =\n nextProps && \"function\" !== typeof Component.getDerivedStateFromError\n ? null\n : context.render()),\n (workInProgress.flags |= 1),\n null !== current && nextProps\n ? ((workInProgress.child = reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n )),\n (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n Component,\n renderLanes\n )))\n : reconcileChildren(current, workInProgress, Component, renderLanes),\n (workInProgress.memoizedState = context.state),\n (current = workInProgress.child))\n : (current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n ));\n return current;\n}\nfunction mountHostRootWithoutHydrating(\n current,\n workInProgress,\n nextChildren,\n renderLanes\n) {\n resetHydrationState();\n workInProgress.flags |= 256;\n reconcileChildren(current, workInProgress, nextChildren, renderLanes);\n return workInProgress.child;\n}\nvar SUSPENDED_MARKER = {\n dehydrated: null,\n treeContext: null,\n retryLane: 0,\n hydrationErrors: null\n};\nfunction mountSuspenseOffscreenState(renderLanes) {\n return { baseLanes: renderLanes, cachePool: getSuspendedCache() };\n}\nfunction getRemainingWorkInPrimaryTree(\n current,\n primaryTreeDidDefer,\n renderLanes\n) {\n current = null !== current ? current.childLanes & ~renderLanes : 0;\n primaryTreeDidDefer && (current |= workInProgressDeferredLane);\n return current;\n}\nfunction updateSuspenseComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n showFallback = !1,\n didSuspend = 0 !== (workInProgress.flags & 128),\n JSCompiler_temp;\n (JSCompiler_temp = didSuspend) ||\n (JSCompiler_temp =\n null !== current && null === current.memoizedState\n ? !1\n : 0 !== (suspenseStackCursor.current & 2));\n JSCompiler_temp && ((showFallback = !0), (workInProgress.flags &= -129));\n JSCompiler_temp = 0 !== (workInProgress.flags & 32);\n workInProgress.flags &= -33;\n if (null === current) {\n if (isHydrating) {\n showFallback\n ? pushPrimaryTreeSuspenseHandler(workInProgress)\n : reuseSuspenseHandlerOnStack(workInProgress);\n (current = nextHydratableInstance)\n ? ((current = canHydrateHydrationBoundary(\n current,\n rootOrSingletonContext\n )),\n (current = null !== current && \"&\" !== current.data ? current : null),\n null !== current &&\n ((workInProgress.memoizedState = {\n dehydrated: current,\n treeContext:\n null !== treeContextProvider\n ? { id: treeContextId, overflow: treeContextOverflow }\n : null,\n retryLane: 536870912,\n hydrationErrors: null\n }),\n (renderLanes = createFiberFromDehydratedFragment(current)),\n (renderLanes.return = workInProgress),\n (workInProgress.child = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null)))\n : (current = null);\n if (null === current) throw throwOnHydrationMismatch(workInProgress);\n isSuspenseInstanceFallback(current)\n ? (workInProgress.lanes = 32)\n : (workInProgress.lanes = 536870912);\n return null;\n }\n var nextPrimaryChildren = nextProps.children;\n nextProps = nextProps.fallback;\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (showFallback = workInProgress.mode),\n (nextPrimaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"hidden\", children: nextPrimaryChildren },\n showFallback\n )),\n (nextProps = createFiberFromFragment(\n nextProps,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.sibling = nextProps),\n (workInProgress.child = nextPrimaryChildren),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState = mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(null, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);\n }\n var prevState = current.memoizedState;\n if (\n null !== prevState &&\n ((nextPrimaryChildren = prevState.dehydrated), null !== nextPrimaryChildren)\n ) {\n if (didSuspend)\n workInProgress.flags & 256\n ? (pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags &= -257),\n (workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n )))\n : null !== workInProgress.memoizedState\n ? (reuseSuspenseHandlerOnStack(workInProgress),\n (workInProgress.child = current.child),\n (workInProgress.flags |= 128),\n (workInProgress = null))\n : (reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (nextProps = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: nextProps.children },\n showFallback\n )),\n (nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2),\n (nextProps.return = workInProgress),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n reconcileChildFibers(\n workInProgress,\n current.child,\n null,\n renderLanes\n ),\n (nextProps = workInProgress.child),\n (nextProps.memoizedState =\n mountSuspenseOffscreenState(renderLanes)),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n (workInProgress = bailoutOffscreenComponent(null, nextProps)));\n else if (\n (pushPrimaryTreeSuspenseHandler(workInProgress),\n isSuspenseInstanceFallback(nextPrimaryChildren))\n ) {\n JSCompiler_temp =\n nextPrimaryChildren.nextSibling &&\n nextPrimaryChildren.nextSibling.dataset;\n if (JSCompiler_temp) var digest = JSCompiler_temp.dgst;\n JSCompiler_temp = digest;\n nextProps = Error(formatProdErrorMessage(419));\n nextProps.stack = \"\";\n nextProps.digest = JSCompiler_temp;\n queueHydrationError({ value: nextProps, source: null, stack: null });\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else if (\n (didReceiveUpdate ||\n propagateParentContextChanges(current, workInProgress, renderLanes, !1),\n (JSCompiler_temp = 0 !== (renderLanes & current.childLanes)),\n didReceiveUpdate || JSCompiler_temp)\n ) {\n JSCompiler_temp = workInProgressRoot;\n if (\n null !== JSCompiler_temp &&\n ((nextProps = getBumpedLaneForHydration(JSCompiler_temp, renderLanes)),\n 0 !== nextProps && nextProps !== prevState.retryLane)\n )\n throw (\n ((prevState.retryLane = nextProps),\n enqueueConcurrentRenderForLane(current, nextProps),\n scheduleUpdateOnFiber(JSCompiler_temp, current, nextProps),\n SelectiveHydrationException)\n );\n isSuspenseInstancePending(nextPrimaryChildren) ||\n renderDidSuspendDelayIfPossible();\n workInProgress = retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n );\n } else\n isSuspenseInstancePending(nextPrimaryChildren)\n ? ((workInProgress.flags |= 192),\n (workInProgress.child = current.child),\n (workInProgress = null))\n : ((current = prevState.treeContext),\n (nextHydratableInstance = getNextHydratable(\n nextPrimaryChildren.nextSibling\n )),\n (hydrationParentFiber = workInProgress),\n (isHydrating = !0),\n (hydrationErrors = null),\n (rootOrSingletonContext = !1),\n null !== current &&\n restoreSuspendedTreeContext(workInProgress, current),\n (workInProgress = mountSuspensePrimaryChildren(\n workInProgress,\n nextProps.children\n )),\n (workInProgress.flags |= 4096));\n return workInProgress;\n }\n if (showFallback)\n return (\n reuseSuspenseHandlerOnStack(workInProgress),\n (nextPrimaryChildren = nextProps.fallback),\n (showFallback = workInProgress.mode),\n (prevState = current.child),\n (digest = prevState.sibling),\n (nextProps = createWorkInProgress(prevState, {\n mode: \"hidden\",\n children: nextProps.children\n })),\n (nextProps.subtreeFlags = prevState.subtreeFlags & 65011712),\n null !== digest\n ? (nextPrimaryChildren = createWorkInProgress(\n digest,\n nextPrimaryChildren\n ))\n : ((nextPrimaryChildren = createFiberFromFragment(\n nextPrimaryChildren,\n showFallback,\n renderLanes,\n null\n )),\n (nextPrimaryChildren.flags |= 2)),\n (nextPrimaryChildren.return = workInProgress),\n (nextProps.return = workInProgress),\n (nextProps.sibling = nextPrimaryChildren),\n (workInProgress.child = nextProps),\n bailoutOffscreenComponent(null, nextProps),\n (nextProps = workInProgress.child),\n (nextPrimaryChildren = current.child.memoizedState),\n null === nextPrimaryChildren\n ? (nextPrimaryChildren = mountSuspenseOffscreenState(renderLanes))\n : ((showFallback = nextPrimaryChildren.cachePool),\n null !== showFallback\n ? ((prevState = CacheContext._currentValue),\n (showFallback =\n showFallback.parent !== prevState\n ? { parent: prevState, pool: prevState }\n : showFallback))\n : (showFallback = getSuspendedCache()),\n (nextPrimaryChildren = {\n baseLanes: nextPrimaryChildren.baseLanes | renderLanes,\n cachePool: showFallback\n })),\n (nextProps.memoizedState = nextPrimaryChildren),\n (nextProps.childLanes = getRemainingWorkInPrimaryTree(\n current,\n JSCompiler_temp,\n renderLanes\n )),\n (workInProgress.memoizedState = SUSPENDED_MARKER),\n bailoutOffscreenComponent(current.child, nextProps)\n );\n pushPrimaryTreeSuspenseHandler(workInProgress);\n renderLanes = current.child;\n current = renderLanes.sibling;\n renderLanes = createWorkInProgress(renderLanes, {\n mode: \"visible\",\n children: nextProps.children\n });\n renderLanes.return = workInProgress;\n renderLanes.sibling = null;\n null !== current &&\n ((JSCompiler_temp = workInProgress.deletions),\n null === JSCompiler_temp\n ? ((workInProgress.deletions = [current]), (workInProgress.flags |= 16))\n : JSCompiler_temp.push(current));\n workInProgress.child = renderLanes;\n workInProgress.memoizedState = null;\n return renderLanes;\n}\nfunction mountSuspensePrimaryChildren(workInProgress, primaryChildren) {\n primaryChildren = mountWorkInProgressOffscreenFiber(\n { mode: \"visible\", children: primaryChildren },\n workInProgress.mode\n );\n primaryChildren.return = workInProgress;\n return (workInProgress.child = primaryChildren);\n}\nfunction mountWorkInProgressOffscreenFiber(offscreenProps, mode) {\n offscreenProps = createFiberImplClass(22, offscreenProps, null, mode);\n offscreenProps.lanes = 0;\n return offscreenProps;\n}\nfunction retrySuspenseComponentWithoutHydrating(\n current,\n workInProgress,\n renderLanes\n) {\n reconcileChildFibers(workInProgress, current.child, null, renderLanes);\n current = mountSuspensePrimaryChildren(\n workInProgress,\n workInProgress.pendingProps.children\n );\n current.flags |= 2;\n workInProgress.memoizedState = null;\n return current;\n}\nfunction scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {\n fiber.lanes |= renderLanes;\n var alternate = fiber.alternate;\n null !== alternate && (alternate.lanes |= renderLanes);\n scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);\n}\nfunction initSuspenseListRenderState(\n workInProgress,\n isBackwards,\n tail,\n lastContentRow,\n tailMode,\n treeForkCount\n) {\n var renderState = workInProgress.memoizedState;\n null === renderState\n ? (workInProgress.memoizedState = {\n isBackwards: isBackwards,\n rendering: null,\n renderingStartTime: 0,\n last: lastContentRow,\n tail: tail,\n tailMode: tailMode,\n treeForkCount: treeForkCount\n })\n : ((renderState.isBackwards = isBackwards),\n (renderState.rendering = null),\n (renderState.renderingStartTime = 0),\n (renderState.last = lastContentRow),\n (renderState.tail = tail),\n (renderState.tailMode = tailMode),\n (renderState.treeForkCount = treeForkCount));\n}\nfunction updateSuspenseListComponent(current, workInProgress, renderLanes) {\n var nextProps = workInProgress.pendingProps,\n revealOrder = nextProps.revealOrder,\n tailMode = nextProps.tail;\n nextProps = nextProps.children;\n var suspenseContext = suspenseStackCursor.current,\n shouldForceFallback = 0 !== (suspenseContext & 2);\n shouldForceFallback\n ? ((suspenseContext = (suspenseContext & 1) | 2),\n (workInProgress.flags |= 128))\n : (suspenseContext &= 1);\n push(suspenseStackCursor, suspenseContext);\n reconcileChildren(current, workInProgress, nextProps, renderLanes);\n nextProps = isHydrating ? treeForkCount : 0;\n if (!shouldForceFallback && null !== current && 0 !== (current.flags & 128))\n a: for (current = workInProgress.child; null !== current; ) {\n if (13 === current.tag)\n null !== current.memoizedState &&\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (19 === current.tag)\n scheduleSuspenseWorkOnFiber(current, renderLanes, workInProgress);\n else if (null !== current.child) {\n current.child.return = current;\n current = current.child;\n continue;\n }\n if (current === workInProgress) break a;\n for (; null === current.sibling; ) {\n if (null === current.return || current.return === workInProgress)\n break a;\n current = current.return;\n }\n current.sibling.return = current.return;\n current = current.sibling;\n }\n switch (revealOrder) {\n case \"forwards\":\n renderLanes = workInProgress.child;\n for (revealOrder = null; null !== renderLanes; )\n (current = renderLanes.alternate),\n null !== current &&\n null === findFirstSuspended(current) &&\n (revealOrder = renderLanes),\n (renderLanes = renderLanes.sibling);\n renderLanes = revealOrder;\n null === renderLanes\n ? ((revealOrder = workInProgress.child), (workInProgress.child = null))\n : ((revealOrder = renderLanes.sibling), (renderLanes.sibling = null));\n initSuspenseListRenderState(\n workInProgress,\n !1,\n revealOrder,\n renderLanes,\n tailMode,\n nextProps\n );\n break;\n case \"backwards\":\n case \"unstable_legacy-backwards\":\n renderLanes = null;\n revealOrder = workInProgress.child;\n for (workInProgress.child = null; null !== revealOrder; ) {\n current = revealOrder.alternate;\n if (null !== current && null === findFirstSuspended(current)) {\n workInProgress.child = revealOrder;\n break;\n }\n current = revealOrder.sibling;\n revealOrder.sibling = renderLanes;\n renderLanes = revealOrder;\n revealOrder = current;\n }\n initSuspenseListRenderState(\n workInProgress,\n !0,\n renderLanes,\n null,\n tailMode,\n nextProps\n );\n break;\n case \"together\":\n initSuspenseListRenderState(\n workInProgress,\n !1,\n null,\n null,\n void 0,\n nextProps\n );\n break;\n default:\n workInProgress.memoizedState = null;\n }\n return workInProgress.child;\n}\nfunction bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {\n null !== current && (workInProgress.dependencies = current.dependencies);\n workInProgressRootSkippedLanes |= workInProgress.lanes;\n if (0 === (renderLanes & workInProgress.childLanes))\n if (null !== current) {\n if (\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n 0 === (renderLanes & workInProgress.childLanes))\n )\n return null;\n } else return null;\n if (null !== current && workInProgress.child !== current.child)\n throw Error(formatProdErrorMessage(153));\n if (null !== workInProgress.child) {\n current = workInProgress.child;\n renderLanes = createWorkInProgress(current, current.pendingProps);\n workInProgress.child = renderLanes;\n for (renderLanes.return = workInProgress; null !== current.sibling; )\n (current = current.sibling),\n (renderLanes = renderLanes.sibling =\n createWorkInProgress(current, current.pendingProps)),\n (renderLanes.return = workInProgress);\n renderLanes.sibling = null;\n }\n return workInProgress.child;\n}\nfunction checkScheduledUpdateOrContext(current, renderLanes) {\n if (0 !== (current.lanes & renderLanes)) return !0;\n current = current.dependencies;\n return null !== current && checkIfContextChanged(current) ? !0 : !1;\n}\nfunction attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n) {\n switch (workInProgress.tag) {\n case 3:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n resetHydrationState();\n break;\n case 27:\n case 5:\n pushHostContext(workInProgress);\n break;\n case 4:\n pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);\n break;\n case 10:\n pushProvider(\n workInProgress,\n workInProgress.type,\n workInProgress.memoizedProps.value\n );\n break;\n case 31:\n if (null !== workInProgress.memoizedState)\n return (\n (workInProgress.flags |= 128),\n pushDehydratedActivitySuspenseHandler(workInProgress),\n null\n );\n break;\n case 13:\n var state$102 = workInProgress.memoizedState;\n if (null !== state$102) {\n if (null !== state$102.dehydrated)\n return (\n pushPrimaryTreeSuspenseHandler(workInProgress),\n (workInProgress.flags |= 128),\n null\n );\n if (0 !== (renderLanes & workInProgress.child.childLanes))\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n pushPrimaryTreeSuspenseHandler(workInProgress);\n current = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n return null !== current ? current.sibling : null;\n }\n pushPrimaryTreeSuspenseHandler(workInProgress);\n break;\n case 19:\n var didSuspendBefore = 0 !== (current.flags & 128);\n state$102 = 0 !== (renderLanes & workInProgress.childLanes);\n state$102 ||\n (propagateParentContextChanges(\n current,\n workInProgress,\n renderLanes,\n !1\n ),\n (state$102 = 0 !== (renderLanes & workInProgress.childLanes)));\n if (didSuspendBefore) {\n if (state$102)\n return updateSuspenseListComponent(\n current,\n workInProgress,\n renderLanes\n );\n workInProgress.flags |= 128;\n }\n didSuspendBefore = workInProgress.memoizedState;\n null !== didSuspendBefore &&\n ((didSuspendBefore.rendering = null),\n (didSuspendBefore.tail = null),\n (didSuspendBefore.lastEffect = null));\n push(suspenseStackCursor, suspenseStackCursor.current);\n if (state$102) break;\n else return null;\n case 22:\n return (\n (workInProgress.lanes = 0),\n updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n )\n );\n case 24:\n pushProvider(workInProgress, CacheContext, current.memoizedState.cache);\n }\n return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);\n}\nfunction beginWork(current, workInProgress, renderLanes) {\n if (null !== current)\n if (current.memoizedProps !== workInProgress.pendingProps)\n didReceiveUpdate = !0;\n else {\n if (\n !checkScheduledUpdateOrContext(current, renderLanes) &&\n 0 === (workInProgress.flags & 128)\n )\n return (\n (didReceiveUpdate = !1),\n attemptEarlyBailoutIfNoScheduledUpdate(\n current,\n workInProgress,\n renderLanes\n )\n );\n didReceiveUpdate = 0 !== (current.flags & 131072) ? !0 : !1;\n }\n else\n (didReceiveUpdate = !1),\n isHydrating &&\n 0 !== (workInProgress.flags & 1048576) &&\n pushTreeId(workInProgress, treeForkCount, workInProgress.index);\n workInProgress.lanes = 0;\n switch (workInProgress.tag) {\n case 16:\n a: {\n var props = workInProgress.pendingProps;\n current = resolveLazy(workInProgress.elementType);\n workInProgress.type = current;\n if (\"function\" === typeof current)\n shouldConstruct(current)\n ? ((props = resolveClassComponentProps(current, props)),\n (workInProgress.tag = 1),\n (workInProgress = updateClassComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )))\n : ((workInProgress.tag = 0),\n (workInProgress = updateFunctionComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n )));\n else {\n if (void 0 !== current && null !== current) {\n var $$typeof = current.$$typeof;\n if ($$typeof === REACT_FORWARD_REF_TYPE) {\n workInProgress.tag = 11;\n workInProgress = updateForwardRef(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n } else if ($$typeof === REACT_MEMO_TYPE) {\n workInProgress.tag = 14;\n workInProgress = updateMemoComponent(\n null,\n workInProgress,\n current,\n props,\n renderLanes\n );\n break a;\n }\n }\n workInProgress = getComponentNameFromType(current) || current;\n throw Error(formatProdErrorMessage(306, workInProgress, \"\"));\n }\n }\n return workInProgress;\n case 0:\n return updateFunctionComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 1:\n return (\n (props = workInProgress.type),\n ($$typeof = resolveClassComponentProps(\n props,\n workInProgress.pendingProps\n )),\n updateClassComponent(\n current,\n workInProgress,\n props,\n $$typeof,\n renderLanes\n )\n );\n case 3:\n a: {\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n );\n if (null === current) throw Error(formatProdErrorMessage(387));\n props = workInProgress.pendingProps;\n var prevState = workInProgress.memoizedState;\n $$typeof = prevState.element;\n cloneUpdateQueue(current, workInProgress);\n processUpdateQueue(workInProgress, props, null, renderLanes);\n var nextState = workInProgress.memoizedState;\n props = nextState.cache;\n pushProvider(workInProgress, CacheContext, props);\n props !== prevState.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n );\n suspendIfUpdateReadFromEntangledAsyncAction();\n props = nextState.element;\n if (prevState.isDehydrated)\n if (\n ((prevState = {\n element: props,\n isDehydrated: !1,\n cache: nextState.cache\n }),\n (workInProgress.updateQueue.baseState = prevState),\n (workInProgress.memoizedState = prevState),\n workInProgress.flags & 256)\n ) {\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else if (props !== $$typeof) {\n $$typeof = createCapturedValueAtFiber(\n Error(formatProdErrorMessage(424)),\n workInProgress\n );\n queueHydrationError($$typeof);\n workInProgress = mountHostRootWithoutHydrating(\n current,\n workInProgress,\n props,\n renderLanes\n );\n break a;\n } else {\n current = workInProgress.stateNode.containerInfo;\n switch (current.nodeType) {\n case 9:\n current = current.body;\n break;\n default:\n current =\n \"HTML\" === current.nodeName\n ? current.ownerDocument.body\n : current;\n }\n nextHydratableInstance = getNextHydratable(current.firstChild);\n hydrationParentFiber = workInProgress;\n isHydrating = !0;\n hydrationErrors = null;\n rootOrSingletonContext = !0;\n renderLanes = mountChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n );\n for (workInProgress.child = renderLanes; renderLanes; )\n (renderLanes.flags = (renderLanes.flags & -3) | 4096),\n (renderLanes = renderLanes.sibling);\n }\n else {\n resetHydrationState();\n if (props === $$typeof) {\n workInProgress = bailoutOnAlreadyFinishedWork(\n current,\n workInProgress,\n renderLanes\n );\n break a;\n }\n reconcileChildren(current, workInProgress, props, renderLanes);\n }\n workInProgress = workInProgress.child;\n }\n return workInProgress;\n case 26:\n return (\n markRef(current, workInProgress),\n null === current\n ? (renderLanes = getResource(\n workInProgress.type,\n null,\n workInProgress.pendingProps,\n null\n ))\n ? (workInProgress.memoizedState = renderLanes)\n : isHydrating ||\n ((renderLanes = workInProgress.type),\n (current = workInProgress.pendingProps),\n (props = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n ).createElement(renderLanes)),\n (props[internalInstanceKey] = workInProgress),\n (props[internalPropsKey] = current),\n setInitialProperties(props, renderLanes, current),\n markNodeAsHoistable(props),\n (workInProgress.stateNode = props))\n : (workInProgress.memoizedState = getResource(\n workInProgress.type,\n current.memoizedProps,\n workInProgress.pendingProps,\n current.memoizedState\n )),\n null\n );\n case 27:\n return (\n pushHostContext(workInProgress),\n null === current &&\n isHydrating &&\n ((props = workInProgress.stateNode =\n resolveSingletonInstance(\n workInProgress.type,\n workInProgress.pendingProps,\n rootInstanceStackCursor.current\n )),\n (hydrationParentFiber = workInProgress),\n (rootOrSingletonContext = !0),\n ($$typeof = nextHydratableInstance),\n isSingletonScope(workInProgress.type)\n ? ((previousHydratableOnEnteringScopedSingleton = $$typeof),\n (nextHydratableInstance = getNextHydratable(props.firstChild)))\n : (nextHydratableInstance = $$typeof)),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n markRef(current, workInProgress),\n null === current && (workInProgress.flags |= 4194304),\n workInProgress.child\n );\n case 5:\n if (null === current && isHydrating) {\n if (($$typeof = props = nextHydratableInstance))\n (props = canHydrateInstance(\n props,\n workInProgress.type,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== props\n ? ((workInProgress.stateNode = props),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = getNextHydratable(props.firstChild)),\n (rootOrSingletonContext = !1),\n ($$typeof = !0))\n : ($$typeof = !1);\n $$typeof || throwOnHydrationMismatch(workInProgress);\n }\n pushHostContext(workInProgress);\n $$typeof = workInProgress.type;\n prevState = workInProgress.pendingProps;\n nextState = null !== current ? current.memoizedProps : null;\n props = prevState.children;\n shouldSetTextContent($$typeof, prevState)\n ? (props = null)\n : null !== nextState &&\n shouldSetTextContent($$typeof, nextState) &&\n (workInProgress.flags |= 32);\n null !== workInProgress.memoizedState &&\n (($$typeof = renderWithHooks(\n current,\n workInProgress,\n TransitionAwareHostComponent,\n null,\n null,\n renderLanes\n )),\n (HostTransitionContext._currentValue = $$typeof));\n markRef(current, workInProgress);\n reconcileChildren(current, workInProgress, props, renderLanes);\n return workInProgress.child;\n case 6:\n if (null === current && isHydrating) {\n if ((current = renderLanes = nextHydratableInstance))\n (renderLanes = canHydrateTextInstance(\n renderLanes,\n workInProgress.pendingProps,\n rootOrSingletonContext\n )),\n null !== renderLanes\n ? ((workInProgress.stateNode = renderLanes),\n (hydrationParentFiber = workInProgress),\n (nextHydratableInstance = null),\n (current = !0))\n : (current = !1);\n current || throwOnHydrationMismatch(workInProgress);\n }\n return null;\n case 13:\n return updateSuspenseComponent(current, workInProgress, renderLanes);\n case 4:\n return (\n pushHostContainer(\n workInProgress,\n workInProgress.stateNode.containerInfo\n ),\n (props = workInProgress.pendingProps),\n null === current\n ? (workInProgress.child = reconcileChildFibers(\n workInProgress,\n null,\n props,\n renderLanes\n ))\n : reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 11:\n return updateForwardRef(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 7:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps,\n renderLanes\n ),\n workInProgress.child\n );\n case 8:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 12:\n return (\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 10:\n return (\n (props = workInProgress.pendingProps),\n pushProvider(workInProgress, workInProgress.type, props.value),\n reconcileChildren(current, workInProgress, props.children, renderLanes),\n workInProgress.child\n );\n case 9:\n return (\n ($$typeof = workInProgress.type._context),\n (props = workInProgress.pendingProps.children),\n prepareToReadContext(workInProgress),\n ($$typeof = readContext($$typeof)),\n (props = props($$typeof)),\n (workInProgress.flags |= 1),\n reconcileChildren(current, workInProgress, props, renderLanes),\n workInProgress.child\n );\n case 14:\n return updateMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 15:\n return updateSimpleMemoComponent(\n current,\n workInProgress,\n workInProgress.type,\n workInProgress.pendingProps,\n renderLanes\n );\n case 19:\n return updateSuspenseListComponent(current, workInProgress, renderLanes);\n case 31:\n return updateActivityComponent(current, workInProgress, renderLanes);\n case 22:\n return updateOffscreenComponent(\n current,\n workInProgress,\n renderLanes,\n workInProgress.pendingProps\n );\n case 24:\n return (\n prepareToReadContext(workInProgress),\n (props = readContext(CacheContext)),\n null === current\n ? (($$typeof = peekCacheFromPool()),\n null === $$typeof &&\n (($$typeof = workInProgressRoot),\n (prevState = createCache()),\n ($$typeof.pooledCache = prevState),\n prevState.refCount++,\n null !== prevState && ($$typeof.pooledCacheLanes |= renderLanes),\n ($$typeof = prevState)),\n (workInProgress.memoizedState = { parent: props, cache: $$typeof }),\n initializeUpdateQueue(workInProgress),\n pushProvider(workInProgress, CacheContext, $$typeof))\n : (0 !== (current.lanes & renderLanes) &&\n (cloneUpdateQueue(current, workInProgress),\n processUpdateQueue(workInProgress, null, null, renderLanes),\n suspendIfUpdateReadFromEntangledAsyncAction()),\n ($$typeof = current.memoizedState),\n (prevState = workInProgress.memoizedState),\n $$typeof.parent !== props\n ? (($$typeof = { parent: props, cache: props }),\n (workInProgress.memoizedState = $$typeof),\n 0 === workInProgress.lanes &&\n (workInProgress.memoizedState =\n workInProgress.updateQueue.baseState =\n $$typeof),\n pushProvider(workInProgress, CacheContext, props))\n : ((props = prevState.cache),\n pushProvider(workInProgress, CacheContext, props),\n props !== $$typeof.cache &&\n propagateContextChanges(\n workInProgress,\n [CacheContext],\n renderLanes,\n !0\n ))),\n reconcileChildren(\n current,\n workInProgress,\n workInProgress.pendingProps.children,\n renderLanes\n ),\n workInProgress.child\n );\n case 29:\n throw workInProgress.pendingProps;\n }\n throw Error(formatProdErrorMessage(156, workInProgress.tag));\n}\nfunction markUpdate(workInProgress) {\n workInProgress.flags |= 4;\n}\nfunction preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n oldProps,\n newProps,\n renderLanes\n) {\n if ((type = 0 !== (workInProgress.mode & 32))) type = !1;\n if (type) {\n if (\n ((workInProgress.flags |= 16777216),\n (renderLanes & 335544128) === renderLanes)\n )\n if (workInProgress.stateNode.complete) workInProgress.flags |= 8192;\n else if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n } else workInProgress.flags &= -16777217;\n}\nfunction preloadResourceAndSuspendIfNeeded(workInProgress, resource) {\n if (\"stylesheet\" !== resource.type || 0 !== (resource.state.loading & 4))\n workInProgress.flags &= -16777217;\n else if (((workInProgress.flags |= 16777216), !preloadResource(resource)))\n if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192;\n else\n throw (\n ((suspendedThenable = noopSuspenseyCommitThenable),\n SuspenseyCommitException)\n );\n}\nfunction scheduleRetryEffect(workInProgress, retryQueue) {\n null !== retryQueue && (workInProgress.flags |= 4);\n workInProgress.flags & 16384 &&\n ((retryQueue =\n 22 !== workInProgress.tag ? claimNextRetryLane() : 536870912),\n (workInProgress.lanes |= retryQueue),\n (workInProgressSuspendedRetryLanes |= retryQueue));\n}\nfunction cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {\n if (!isHydrating)\n switch (renderState.tailMode) {\n case \"hidden\":\n hasRenderedATailFallback = renderState.tail;\n for (var lastTailNode = null; null !== hasRenderedATailFallback; )\n null !== hasRenderedATailFallback.alternate &&\n (lastTailNode = hasRenderedATailFallback),\n (hasRenderedATailFallback = hasRenderedATailFallback.sibling);\n null === lastTailNode\n ? (renderState.tail = null)\n : (lastTailNode.sibling = null);\n break;\n case \"collapsed\":\n lastTailNode = renderState.tail;\n for (var lastTailNode$106 = null; null !== lastTailNode; )\n null !== lastTailNode.alternate && (lastTailNode$106 = lastTailNode),\n (lastTailNode = lastTailNode.sibling);\n null === lastTailNode$106\n ? hasRenderedATailFallback || null === renderState.tail\n ? (renderState.tail = null)\n : (renderState.tail.sibling = null)\n : (lastTailNode$106.sibling = null);\n }\n}\nfunction bubbleProperties(completedWork) {\n var didBailout =\n null !== completedWork.alternate &&\n completedWork.alternate.child === completedWork.child,\n newChildLanes = 0,\n subtreeFlags = 0;\n if (didBailout)\n for (var child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags & 65011712),\n (subtreeFlags |= child$107.flags & 65011712),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n else\n for (child$107 = completedWork.child; null !== child$107; )\n (newChildLanes |= child$107.lanes | child$107.childLanes),\n (subtreeFlags |= child$107.subtreeFlags),\n (subtreeFlags |= child$107.flags),\n (child$107.return = completedWork),\n (child$107 = child$107.sibling);\n completedWork.subtreeFlags |= subtreeFlags;\n completedWork.childLanes = newChildLanes;\n return didBailout;\n}\nfunction completeWork(current, workInProgress, renderLanes) {\n var newProps = workInProgress.pendingProps;\n popTreeContext(workInProgress);\n switch (workInProgress.tag) {\n case 16:\n case 15:\n case 0:\n case 11:\n case 7:\n case 8:\n case 12:\n case 9:\n case 14:\n return bubbleProperties(workInProgress), null;\n case 1:\n return bubbleProperties(workInProgress), null;\n case 3:\n renderLanes = workInProgress.stateNode;\n newProps = null;\n null !== current && (newProps = current.memoizedState.cache);\n workInProgress.memoizedState.cache !== newProps &&\n (workInProgress.flags |= 2048);\n popProvider(CacheContext);\n popHostContainer();\n renderLanes.pendingContext &&\n ((renderLanes.context = renderLanes.pendingContext),\n (renderLanes.pendingContext = null));\n if (null === current || null === current.child)\n popHydrationState(workInProgress)\n ? markUpdate(workInProgress)\n : null === current ||\n (current.memoizedState.isDehydrated &&\n 0 === (workInProgress.flags & 256)) ||\n ((workInProgress.flags |= 1024),\n upgradeHydrationErrorsToRecoverable());\n bubbleProperties(workInProgress);\n return null;\n case 26:\n var type = workInProgress.type,\n nextResource = workInProgress.memoizedState;\n null === current\n ? (markUpdate(workInProgress),\n null !== nextResource\n ? (bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n null,\n newProps,\n renderLanes\n )))\n : nextResource\n ? nextResource !== current.memoizedState\n ? (markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadResourceAndSuspendIfNeeded(workInProgress, nextResource))\n : (bubbleProperties(workInProgress),\n (workInProgress.flags &= -16777217))\n : ((current = current.memoizedProps),\n current !== newProps && markUpdate(workInProgress),\n bubbleProperties(workInProgress),\n preloadInstanceAndSuspendIfNeeded(\n workInProgress,\n type,\n current,\n newProps,\n renderLanes\n ));\n return null;\n case 27:\n popHostContext(workInProgress);\n renderLanes = rootInstanceStackCursor.current;\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n current = contextStackCursor.current;\n popHydrationState(workInProgress)\n ? prepareToHydrateHostInstance(workInProgress, current)\n : ((current = resolveSingletonInstance(type, newProps, renderLanes)),\n (workInProgress.stateNode = current),\n markUpdate(workInProgress));\n }\n bubbleProperties(workInProgress);\n return null;\n case 5:\n popHostContext(workInProgress);\n type = workInProgress.type;\n if (null !== current && null != workInProgress.stateNode)\n current.memoizedProps !== newProps && markUpdate(workInProgress);\n else {\n if (!newProps) {\n if (null === workInProgress.stateNode)\n throw Error(formatProdErrorMessage(166));\n bubbleProperties(workInProgress);\n return null;\n }\n nextResource = contextStackCursor.current;\n if (popHydrationState(workInProgress))\n prepareToHydrateHostInstance(workInProgress, nextResource);\n else {\n var ownerDocument = getOwnerDocumentFromRootContainer(\n rootInstanceStackCursor.current\n );\n switch (nextResource) {\n case 1:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case 2:\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n default:\n switch (type) {\n case \"svg\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/2000/svg\",\n type\n );\n break;\n case \"math\":\n nextResource = ownerDocument.createElementNS(\n \"http://www.w3.org/1998/Math/MathML\",\n type\n );\n break;\n case \"script\":\n nextResource = ownerDocument.createElement(\"div\");\n nextResource.innerHTML = \" + diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index 60b007a9..e5610469 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.910", + "version": "1.0.942", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index cbe9603d..b641319b 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1435", + "version": "1.0.1467", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index b3ebbbe7..5f469eb0 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index b04a325c..5e2785f9 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1440", + "version": "0.5.1472", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 83b74e34..f9289ecc 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 7c566288..c71aff0e 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 1cfa0880..652efaa5 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 2d6c7de3..530b6744 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 4bd6f7f8..239dca93 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 57464b45..04156264 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 9c52ac92..626bce15 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 70c6beae..6c7868f1 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 04b40ba1..0ff5bff0 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1461", + "version": "0.5.1493", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index f4c8ea72..92cb5cb6 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 1e5ef84f..6cd46fd7 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 5ee80022..50e70891 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 89d7fd4d..6d3caaed 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 89ee7ccd..752b19f9 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 8c7c3e5f..b09bd25a 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 826ecd81..5c4a0a79 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx index 3d0d1c57..97bdffee 100755 --- a/pages/options/src/components/LLMSelector.tsx +++ b/pages/options/src/components/LLMSelector.tsx @@ -20,7 +20,7 @@ const LLMSelector: React.FC = ({ hasDefaultLLM, onLLMChange, }) => { - const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings } = usePluginSettings(); + const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings, updateAPIKey, saveSettings } = usePluginSettings(); const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl); const [apiKey, setApiKey] = useState(''); const saveTimeoutRef = useRef(null); @@ -28,7 +28,7 @@ const LLMSelector: React.FC = ({ // Загружаем API-ключ при монтировании компонента useEffect(() => { const loadApiKey = async () => { - const keyId = `ozon-analyzer-${promptType}-${language}`; + const keyId = `ozon-analyzer.${promptType}.${language}.default`; const key = await APIKeyManager.getDecryptedKey(keyId) || ''; setApiKey(key); }; @@ -77,8 +77,29 @@ const LLMSelector: React.FC = ({ saveTimeoutRef.current = setTimeout(async () => { try { - const keyId = `ozon-analyzer-${promptType}-${language}`; - await APIKeyManager.saveEncryptedKey(keyId, newApiKey); + // Для Default LLM сохраняем ключ в encryptedApiKeys под ключом с суффиксом -default + if (selectedLLM === 'default') { + const keyId = `ozon-analyzer.${promptType}.${language}.default`; + console.log(`[API_KEY_FLOW] LLMSelector: Saving API key for ${keyId}`); + await APIKeyManager.saveEncryptedKey(keyId, newApiKey); + + // Обновляем локальное состояние settings для немедленного отображения + const updatedSettings = { ...settings }; + if (!updatedSettings.api_keys) { + updatedSettings.api_keys = { default: '' }; + } + // Для default LLM сохраняем без префикса ozon-analyzer. + const localKey = keyId.replace('ozon-analyzer.', ''); + (updatedSettings.api_keys as any)[localKey] = newApiKey; + console.log(`[API_KEY_FLOW] LLMSelector: Updated local settings with key ${localKey}`); + + // Сохраняем обновленные настройки + await saveSettings(updatedSettings); + console.log(`[API_KEY_FLOW] LLMSelector: Settings saved successfully`); + } + // Для платформенных LLM API-ключи уже сохранены на уровне платформы + + console.log(`[API_KEY_FLOW] LLMSelector: Calling onLLMChange with LLM=${selectedLLM}, apiKey length=${newApiKey.length}`); onLLMChange(selectedLLM, newApiKey); } catch (error) { console.error('Failed to save API key:', error); diff --git a/pages/options/src/components/PluginDetails.tsx b/pages/options/src/components/PluginDetails.tsx index 480011d2..78d5379f 100755 --- a/pages/options/src/components/PluginDetails.tsx +++ b/pages/options/src/components/PluginDetails.tsx @@ -281,7 +281,7 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK defaultLLMCurl={getDefaultLLMForPrompt(promptType, language)} hasDefaultLLM={hasDefaultLLMForPrompt(promptType, language)} onLLMChange={(llm, apiKey) => { - console.log(`LLM changed for ${promptType} ${language}:`, llm, apiKey); + console.log(`[API_KEY_FLOW] PluginDetails: LLM changed for ${promptType}.${language}: ${llm}, apiKey length: ${apiKey?.length || 0}`); // Сохраняем выбранную LLM и API ключ в pluginSettings для передачи в mcp_server.py const updatedPluginSettings = { ...pluginSettings } as any; if (!updatedPluginSettings.selected_llms) { @@ -297,14 +297,17 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIK if (!updatedPluginSettings.api_keys) { updatedPluginSettings.api_keys = {}; } + let keyId = ''; if (apiKey) { - const keyId = `ozon-analyzer-${promptType}-${language}`; + keyId = `ozon-analyzer.${promptType}.${language}.default`; updatedPluginSettings.api_keys[keyId] = apiKey; + console.log(`[API_KEY_FLOW] PluginDetails: Saved API key for ${keyId} in updatedPluginSettings`); } // Обновляем pluginSettings через глобальный объект if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) { (window as any).pyodide.globals.pluginSettings = updatedPluginSettings; + console.log(`[API_KEY_FLOW] PluginDetails: Updated pyodide.globals.pluginSettings with API key for ${keyId || 'no key'}`); } }} /> @@ -355,7 +358,7 @@ const PluginDetails = (props: PluginDetailsProps) => { } }, [selectedPlugin?.id]); - // Передаем выбранные LLM в mcp_server.py через pyodide.globals + // Передаем выбранные LLM и API ключи в mcp_server.py через pyodide.globals useEffect(() => { if (pluginSettings && typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) { // Создаем структуру selected_llms из pluginSettings @@ -369,13 +372,26 @@ const PluginDetails = (props: PluginDetailsProps) => { en: pluginSettings.deep_analysis?.en?.llm || 'default', } }; - + + // Создаем структуру api_keys из pluginSettings + const api_keys = { + 'ozon-analyzer.basic_analysis.ru.default': (pluginSettings.api_keys as any)?.['basic_analysis.ru.default'] || '', + 'ozon-analyzer.basic_analysis.en.default': (pluginSettings.api_keys as any)?.['basic_analysis.en.default'] || '', + 'ozon-analyzer.deep_analysis.ru.default': (pluginSettings.api_keys as any)?.['deep_analysis.ru.default'] || '', + 'ozon-analyzer.deep_analysis.en.default': (pluginSettings.api_keys as any)?.['deep_analysis.en.default'] || '', + 'ozon-analyzer-basic_analysis-ru': (pluginSettings.api_keys as any)?.['ozon-analyzer-basic_analysis-ru'] || '', + 'ozon-analyzer-basic_analysis-en': (pluginSettings.api_keys as any)?.['ozon-analyzer-basic_analysis-en'] || '', + 'ozon-analyzer-deep_analysis-ru': (pluginSettings.api_keys as any)?.['ozon-analyzer-deep_analysis-ru'] || '', + 'ozon-analyzer-deep_analysis-en': (pluginSettings.api_keys as any)?.['ozon-analyzer-deep_analysis-en'] || '', + }; + // Обновляем pluginSettings в pyodide.globals const updatedPluginSettings = { ...(window as any).pyodide.globals.pluginSettings || {}, - selected_llms: selected_llms + selected_llms: selected_llms, + api_keys: api_keys }; - + (window as any).pyodide.globals.pluginSettings = updatedPluginSettings; console.log('Updated pluginSettings in pyodide.globals:', updatedPluginSettings); } diff --git a/pages/options/src/hooks/usePluginSettings.ts b/pages/options/src/hooks/usePluginSettings.ts index fb310324..f66a1673 100755 --- a/pages/options/src/hooks/usePluginSettings.ts +++ b/pages/options/src/hooks/usePluginSettings.ts @@ -24,6 +24,10 @@ export interface PluginSettings { }; api_keys: { default: string; // encrypted + 'ozon-analyzer-basic_analysis-ru'?: string; + 'ozon-analyzer-basic_analysis-en'?: string; + 'ozon-analyzer-deep_analysis-ru'?: string; + 'ozon-analyzer-deep_analysis-en'?: string; }; } @@ -156,8 +160,30 @@ export const usePluginSettings = () => { if (storedSettings) { // Расшифровываем API ключи const decryptedApiKeys = { ...storedSettings.api_keys }; - if (storedSettings.api_keys?.default) { - decryptedApiKeys.default = await APIKeyManager.getDecryptedKey('ozon-analyzer-default') || ''; + + // Расшифровываем все API ключи для всех комбинаций promptType-language + const apiKeyIds = [ + 'ozon-analyzer-basic_analysis-ru-default', + 'ozon-analyzer-basic_analysis-en-default', + 'ozon-analyzer-deep_analysis-ru-default', + 'ozon-analyzer-deep_analysis-en-default', + 'ozon-analyzer-basic_analysis-ru', + 'ozon-analyzer-basic_analysis-en', + 'ozon-analyzer-deep_analysis-ru', + 'ozon-analyzer-deep_analysis-en' + ]; + + for (const keyId of apiKeyIds) { + const decryptedKey = await APIKeyManager.getDecryptedKey(keyId); + if (decryptedKey) { + // Для default LLM сохраняем без префикса ozon-analyzer- + if (keyId.endsWith('-default')) { + decryptedApiKeys[keyId.replace('ozon-analyzer-', '')] = decryptedKey; + } else { + // Для других LLM сохраняем с полным именем + decryptedApiKeys[keyId] = decryptedKey; + } + } } setSettings({ @@ -200,14 +226,36 @@ export const usePluginSettings = () => { try { // Шифруем API ключи перед сохранением const settingsToSave = { ...newSettings }; - if (newSettings.api_keys?.default) { - await APIKeyManager.saveEncryptedKey('ozon-analyzer-default', newSettings.api_keys.default); - } else { - await APIKeyManager.removeKey('ozon-analyzer-default'); + + // Шифруем все API ключи для всех комбинаций promptType-language + const apiKeyMappings = [ + { key: 'basic_analysis-ru-default', id: 'ozon-analyzer-basic_analysis-ru-default' }, + { key: 'basic_analysis-en-default', id: 'ozon-analyzer-basic_analysis-en-default' }, + { key: 'deep_analysis-ru-default', id: 'ozon-analyzer-deep_analysis-ru-default' }, + { key: 'deep_analysis-en-default', id: 'ozon-analyzer-deep_analysis-en-default' }, + { key: 'ozon-analyzer-basic_analysis-ru', id: 'ozon-analyzer-basic_analysis-ru' }, + { key: 'ozon-analyzer-basic_analysis-en', id: 'ozon-analyzer-basic_analysis-en' }, + { key: 'ozon-analyzer-deep_analysis-ru', id: 'ozon-analyzer-deep_analysis-ru' }, + { key: 'ozon-analyzer-deep_analysis-en', id: 'ozon-analyzer-deep_analysis-en' } + ]; + + for (const mapping of apiKeyMappings) { + const apiKeyValue = (newSettings.api_keys as any)?.[mapping.key]; + if (apiKeyValue) { + await APIKeyManager.saveEncryptedKey(mapping.id, apiKeyValue); + } else { + await APIKeyManager.removeKey(mapping.id); + } } // Убираем API ключи из объекта настроек, которые сохраняются в plain JSON - settingsToSave.api_keys = { default: '' }; + settingsToSave.api_keys = { + default: '', + 'ozon-analyzer-basic_analysis-ru': '', + 'ozon-analyzer-basic_analysis-en': '', + 'ozon-analyzer-deep_analysis-ru': '', + 'ozon-analyzer-deep_analysis-en': '' + }; await chrome.storage.local.set({ [STORAGE_KEY]: settingsToSave }); setSettings(newSettings); diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 51edede2..174e8f06 100755 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1453", + "version": "0.5.1485", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 25a32276..e0cc4453 100755 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1458", + "version": "0.27.1490", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index fd707d1e..e6aa5e6e 100755 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1460", + "version": "0.27.1492", "description": "The Pyodide JavaScript package", "keywords": [ "python", From 0d5289ed6a1ff2a7108206ba8b0682b804c3483f Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Thu, 30 Oct 2025 06:00:17 +0500 Subject: [PATCH 14/15] =?UTF-8?q?Grok=20=D1=81=D0=BC=D0=BE=D0=B3!=20=D0=92?= =?UTF-8?q?=D1=8B=D0=B1=D0=BE=D1=80=20=D0=B4=D0=B5=D1=84=D0=BE=D0=BB=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B7=D0=B0=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectGraphAgent/package.json | 4 +- chrome-extension/package.json | 2 +- chrome-extension/public/offscreen.js | 141 ++++++- .../plugins/ozon-analyzer/mcp_server.py | 137 +++--- chrome-extension/public/pyodide/package.json | 2 +- chrome-extension/public/pyodide/test.py | 11 - chrome-extension/public/test_api_keys.html | 393 ------------------ chrome-extension/public/test_api_keys.js | 276 ------------ chrome-extension/src/background/index.ts | 171 +++++++- chrome-extension/src/background/package.json | 2 +- package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/env/package.json | 2 +- packages/hmr/package.json | 2 +- packages/i18n/package.json | 2 +- packages/module-manager/package.json | 2 +- packages/shared/package.json | 2 +- packages/storage/package.json | 2 +- packages/tailwindcss-config/package.json | 2 +- packages/tsconfig/package.json | 2 +- packages/ui/package.json | 2 +- packages/vite-config/package.json | 2 +- packages/zipper/package.json | 2 +- pages/content-runtime/package.json | 2 +- pages/content-ui/package.json | 2 +- pages/content/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/side-panel/package.json | 2 +- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- 32 files changed, 376 insertions(+), 807 deletions(-) delete mode 100755 chrome-extension/public/pyodide/test.py delete mode 100644 chrome-extension/public/test_api_keys.html delete mode 100644 chrome-extension/public/test_api_keys.js diff --git a/ProjectGraphAgent/package.json b/ProjectGraphAgent/package.json index 67d51a87..92ed2019 100755 --- a/ProjectGraphAgent/package.json +++ b/ProjectGraphAgent/package.json @@ -1,7 +1,7 @@ { "name": "project-graph-agent", - "version": "1.0.1467", - "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1467", + "version": "1.0.1494", + "description": "Jsonnet-driven project control system for AI agents - Integrated with Agent Plugins Platform v1.0.1494", "main": "scripts/graph_generator.mjs", "type": "module", "scripts": { diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 60628fe4..00a04b48 100755 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - core settings", "type": "module", "private": true, diff --git a/chrome-extension/public/offscreen.js b/chrome-extension/public/offscreen.js index 696c25cb..19b39a98 100755 --- a/chrome-extension/public/offscreen.js +++ b/chrome-extension/public/offscreen.js @@ -211,6 +211,9 @@ async function trackSendResponse(response, options = {}) { // === WORKFLOW EXECUTION SYSTEM === // Убрана логика предотвращения дублирования для гарантированного выполнения каждого workflow +// [API_KEY_FLOW] MARKER: OFFSCREEN_WORKFLOW_EXECUTION_START +console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_WORKFLOW_EXECUTION_START'); + // === LOGGING SYSTEM === // Логирование уровни @@ -722,14 +725,14 @@ async function initializePyodide() { if (window.pluginSettings && window.pluginSettings.api_keys) { console.log('[OFFSCREEN_DIAGNOSIS] Available API keys:', Object.keys(window.pluginSettings.api_keys)); console.log('[API_KEY_FLOW] Offscreen: Checking pluginSettings.api_keys keys:', Object.keys(window.pluginSettings.api_keys)); - console.log('[API_KEY_FLOW] Offscreen: Looking for apiKeyId:', apiKeyId); - const apiKeyFromPlugin = window.pluginSettings.api_keys[apiKeyId]; + console.log('[API_KEY_FLOW] Offscreen: Looking for apiKeyId:', jsOptions.apiKeyId); + const apiKeyFromPlugin = window.pluginSettings.api_keys[jsOptions.apiKeyId]; if (apiKeyFromPlugin) { - console.log('[OFFSCREEN_DIAGNOSIS] Found API key in pluginSettings:', apiKeyId); - console.log('[API_KEY_FLOW] Offscreen: Found API key for', apiKeyId, 'length:', apiKeyFromPlugin.length); - return apiKeyFromPlugin; + console.log('[OFFSCREEN_DIAGNOSIS] Found API key in pluginSettings:', jsOptions.apiKeyId); + console.log('[API_KEY_FLOW] Offscreen: Found API key for', jsOptions.apiKeyId, 'length:', apiKeyFromPlugin.length); + jsOptions.apiKey = apiKeyFromPlugin; } else { - console.log('[API_KEY_FLOW] Offscreen: API key not found for', apiKeyId, 'in pluginSettings.api_keys'); + console.log('[API_KEY_FLOW] Offscreen: API key not found for', jsOptions.apiKeyId, 'in pluginSettings.api_keys'); } } else { console.log('[API_KEY_FLOW] Offscreen: pluginSettings.api_keys is not available'); @@ -806,7 +809,41 @@ async function initializePyodide() { console.log('[API_KEY_FLOW_PROBLEM_END] ===== END OF API KEY FLOW PROBLEM RANGE ====='); // logDebug('PYODIDE', 'Gemini API key retrieved successfully'); - // 3. Подготовка данных для запроса к Gemini API + // 3. Проверяем, нужно ли использовать background для platform LLM (НЕ Default LLM) + // Default LLM (basic_analysis, deep_analysis) должны использовать curl_file из manifest.json + // и API ключ из encryptedApiKeys, а не отправлять запрос в background + if (cleanedModelAlias !== 'basic_analysis' && cleanedModelAlias !== 'deep_analysis') { + console.log('[OFFSCREEN_DIAGNOSIS] Using background for platform LLM:', cleanedModelAlias); + console.log('[API_KEY_FLOW] Sending request to background for modelAlias:', cleanedModelAlias); + + try { + const response = await safeSendMessage({ + type: 'llm_call', + data: { + modelAlias: cleanedModelAlias, + options: jsOptions, + pluginId: 'ozon-analyzer' + } + }, { + timeout: 30000, + retries: 2, + silent: false + }); + + console.log('[API_KEY_FLOW] Background response:', response); + + if (response.error) { + throw new Error(response.error_message || 'Background LLM call failed'); + } + + return response.response || response.result; + } catch (error) { + console.error('[API_KEY_FLOW] Background LLM call failed:', error); + throw error; + } + } + + // 4. Подготовка данных для прямого запроса к Gemini API (для платформенных LLM) const requestBody = { contents: [{ parts: [{ @@ -822,7 +859,7 @@ async function initializePyodide() { } }; - // 4. HTTP запрос к Gemini API с улучшенной обработкой ошибок и таймаутами + // 5. HTTP запрос к Gemini API с улучшенной обработкой ошибок и таймаутами // Проверяем, содержит ли finalModelName уже :generateContent let urlModelName = finalModelName; if (!finalModelName.includes(':generateContent')) { @@ -2049,11 +2086,24 @@ chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { } } + // [API_KEY_FLOW] MARKER: OFFSCREEN_EXECUTE_WORKFLOW_START + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_EXECUTE_WORKFLOW_START'); + console.log('[API_KEY_FLOW] executeWorkflowWithChunks called with pluginId:', pluginId); + console.log('[API_KEY_FLOW] Plugin settings received in offscreen:', { + hasApiKeys: !!pluginSettings.api_keys, + hasPrompts: !!pluginSettings.prompts, + settingsKeys: Object.keys(pluginSettings), + apiKeysCount: pluginSettings.api_keys ? Object.keys(pluginSettings.api_keys).length : 0, + promptsCount: pluginSettings.prompts ? Object.keys(pluginSettings.prompts).length : 0 + }); + // Execute workflow with chunks // logInfo('EXECUTION', `Шаг 8: Запуск executeWorkflowWithChunks - ${new Date(Date.now()).toISOString()}`); const result = await executeWorkflowWithChunks(pluginId, pageKey, workflowPayload, requestId, pluginSettings, sendWorkflowResponse); // logInfo('EXECUTION', `Шаг 9: executeWorkflowWithChunks завершен - ${new Date(Date.now()).toISOString()}`); // logInfo('EXECUTION', 'Workflow execution completed'); + + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_EXECUTE_WORKFLOW_END'); } catch (error) { // logError('EXECUTION', 'CRITICAL ERROR:', error); @@ -2205,6 +2255,9 @@ function logAPIKeyMessageFlow(message, direction, additionalInfo = {}) { window.testAPIKeyRetrieval = testAPIKeyRetrieval; window.checkEncryptedAPIKeys = checkEncryptedAPIKeys; +// [API_KEY_FLOW] MARKER: OFFSCREEN_ENHANCED_LLM_CALL_INIT_START +console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_ENHANCED_LLM_CALL_INIT_START'); + // Initialize the enhanced LLM call after jsBridge is ready (async function() { try { @@ -2235,6 +2288,45 @@ window.checkEncryptedAPIKeys = checkEncryptedAPIKeys; let apiKey = jsOptions.apiKey; + // [API_KEY_FLOW] MARKER: OFFSCREEN_LLM_CALL_API_KEY_CHECK_START + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_LLM_CALL_API_KEY_CHECK_START'); + console.log('[API_KEY_FLOW] LLM call with modelAlias:', cleanedModelAlias); + console.log('[API_KEY_FLOW] jsOptions has apiKey:', !!jsOptions.apiKey); + console.log('[API_KEY_FLOW] jsOptions has apiKeyId:', !!jsOptions.apiKeyId); + if (jsOptions.apiKeyId) { + console.log('[API_KEY_FLOW] apiKeyId:', jsOptions.apiKeyId); + } + + // [API_KEY_FLOW_PROBLEM_START] ===== BEGINNING OF API KEY FLOW PROBLEM RANGE ===== + console.log('[API_KEY_FLOW_PROBLEM_START] ===== BEGINNING OF API KEY FLOW PROBLEM RANGE ====='); + + // Check if we have pluginSettings with api_keys available + console.log('[API_KEY_FLOW] Checking for pluginSettings.api_keys...'); + console.log('[API_KEY_FLOW] window.pluginSettings exists:', !!window.pluginSettings); + if (window.pluginSettings) { + console.log('[API_KEY_FLOW] window.pluginSettings has api_keys:', !!window.pluginSettings.api_keys); + if (window.pluginSettings.api_keys) { + console.log('[API_KEY_FLOW] Available API key IDs in pluginSettings:', Object.keys(window.pluginSettings.api_keys)); + console.log('[API_KEY_FLOW] API keys structure:', Object.keys(window.pluginSettings.api_keys).map(key => ({ + keyId: key, + hasValue: !!window.pluginSettings.api_keys[key], + valueLength: window.pluginSettings.api_keys[key] ? window.pluginSettings.api_keys[key].length : 0 + }))); + } + } + + // Try to get API key from pluginSettings first + if (jsOptions.apiKeyId && !apiKey && window.pluginSettings && window.pluginSettings.api_keys) { + const pluginApiKey = window.pluginSettings.api_keys[jsOptions.apiKeyId]; + if (pluginApiKey) { + apiKey = pluginApiKey; + console.log('[API_KEY_FLOW] ✅ API key found in pluginSettings for keyId:', jsOptions.apiKeyId); + console.log('[API_KEY_FLOW] API key length:', apiKey.length); + } else { + console.log('[API_KEY_FLOW] ❌ API key not found in pluginSettings for keyId:', jsOptions.apiKeyId); + } + } + // Enhanced logging for API key retrieval if (jsOptions.apiKeyId && !apiKey) { console.log('[OFFSCREEN_DIAGNOSIS] Requesting API key from background:', jsOptions.apiKeyId); @@ -2243,12 +2335,20 @@ window.checkEncryptedAPIKeys = checkEncryptedAPIKeys; data: { keyId: jsOptions.apiKeyId } }; + console.log('[API_KEY_FLOW] Sending GET_API_KEY message to background for keyId:', jsOptions.apiKeyId); const response = await safeSendMessage(getApiKeyMessage, { timeout: 10000, retries: 2, silent: false }); + console.log('[API_KEY_FLOW] GET_API_KEY response received:', { + success: response.success, + hasResponse: !!response.response, + hasApiKey: !!(response.response && response.response.apiKey), + apiKeyLength: response.response && response.response.apiKey ? response.response.apiKey.length : 0 + }); + console.log('[OFFSCREEN_DIAGNOSIS] API key response:', { success: response.success, hasApiKey: !!(response.response && response.response.apiKey), @@ -2259,37 +2359,60 @@ window.checkEncryptedAPIKeys = checkEncryptedAPIKeys; if (response.response.apiKey) { apiKey = response.response.apiKey; console.log('[OFFSCREEN_DIAGNOSIS] ✅ API key retrieved from background successfully'); + console.log('[API_KEY_FLOW] ✅ API key successfully retrieved and assigned'); } else { console.log('[OFFSCREEN_DIAGNOSIS] ⚠️ API key response received but no apiKey field'); + console.log('[API_KEY_FLOW] ⚠️ API key response received but no apiKey field'); } } else { console.log('[OFFSCREEN_DIAGNOSIS] ❌ Failed to retrieve API key from background:', response.error); + console.log('[API_KEY_FLOW] ❌ Failed to retrieve API key from background:', response.error); } + } else if (apiKey) { + console.log('[API_KEY_FLOW] API key already present in jsOptions or retrieved from pluginSettings'); + } else { + console.log('[API_KEY_FLOW] No apiKeyId provided and no apiKey in jsOptions'); } + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_LLM_CALL_API_KEY_CHECK_END'); + // Modify jsOptions to include the retrieved apiKey if (apiKey) { jsOptions.apiKey = apiKey; + console.log('[API_KEY_FLOW] API key assigned to jsOptions.apiKey'); } else { console.log('[OFFSCREEN_DIAGNOSIS] ❌ No API key available for LLM call - this will cause failure'); + console.log('[API_KEY_FLOW] ❌ No API key available for LLM call - this will cause failure'); } + // [API_KEY_FLOW_PROBLEM_END] ===== END OF API KEY FLOW PROBLEM RANGE ===== + console.log('[API_KEY_FLOW_PROBLEM_END] ===== END OF API KEY FLOW PROBLEM RANGE ====='); + // Convert back to Pyodide objects const modifiedOptions = pyodide.toPy(jsOptions); const modifiedModelAlias = pyodide.toPy(cleanedModelAlias); + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_LLM_CALL_EXECUTE_START'); // Call original function with modified parameters - return await originalLLMCall.call(this, modifiedModelAlias, modifiedOptions); + const result = await originalLLMCall.call(this, modifiedModelAlias, modifiedOptions); + console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_LLM_CALL_EXECUTE_END'); + + return result; } catch (error) { console.error('[OFFSCREEN_DIAGNOSIS] Error in enhanced llm_call:', error); + console.error('[API_KEY_FLOW] Error in enhanced llm_call:', error); throw error; } }; } catch (error) { console.error('[OFFSCREEN_DIAGNOSIS] Failed to initialize enhanced LLM call:', error); + console.error('[API_KEY_FLOW] Failed to initialize enhanced LLM call:', error); } })(); +console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_ENHANCED_LLM_CALL_INIT_END'); +console.log('[API_KEY_FLOW] MARKER: OFFSCREEN_WORKFLOW_EXECUTION_END'); + logInfo('SYSTEM', 'Offscreen document ready, waiting for messages...'); \ No newline at end of file diff --git a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py index 0cafc937..e16d39c8 100755 --- a/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py +++ b/chrome-extension/public/plugins/ozon-analyzer/mcp_server.py @@ -2633,24 +2633,22 @@ def get_api_key_for_analysis(plugin_settings: Optional[Dict[str, Any]] = None, console_log(f"[DIAGNOSIS] Checking API key availability...") if selected_llm == 'default': - # Для Default LLM используем специфичный ключ для комбинации - key_id = f'ozon-analyzer-{analysis_type}-{content_language}' - console_log(f"[DIAGNOSIS] Generated key_id for default LLM: {key_id}") + # Для Default LLM используем dotted key с .default + key_id_dotted = f'ozon-analyzer.{analysis_type}.{content_language}.default' + console_log(f"[DIAGNOSIS] Generated dotted key_id for default LLM: {key_id_dotted}") - # Проверяем, есть ли конкретный ключ в api_keys для этой комбинации - specific_key = safe_dict_get(api_keys, key_id, '') - console_log(f"[DIAGNOSIS] Specific key {key_id} exists: {bool(specific_key and specific_key.strip())}") + # Проверяем наличие ключа по dotted-формату + specific_key = safe_dict_get(api_keys, key_id_dotted, '') + console_log(f"[DIAGNOSIS] Specific dotted key {key_id_dotted} exists: {bool(specific_key and specific_key.strip())}") if specific_key and specific_key.strip(): - console_log(f"[DIAGNOSIS] ✅ Using specific key: {key_id}") + console_log(f"[DIAGNOSIS] ✅ Using specific key: {key_id_dotted}") console_log(f"[DIAGNOSIS] ===== API KEY DIAGNOSTICS COMPLETE =====") - return key_id + return key_id_dotted else: - # Fallback на платформенный ключ из manifest - model_alias = get_default_model_id_from_manifest(analysis_type, content_language, plugin_settings) - console_log(f"[DIAGNOSIS] ❌ Specific key {key_id} not set, falling back to platform key: {model_alias}") + console_log(f"[DIAGNOSIS] ❌ Dotted key {key_id_dotted} not set, will rely on background default handling") console_log(f"[DIAGNOSIS] ===== API KEY DIAGNOSTICS COMPLETE =====") - return model_alias + return None else: # Для платформенной LLM используем её ID console_log(f"[DIAGNOSIS] ✅ Using platform key: {selected_llm}") @@ -2709,9 +2707,9 @@ def get_api_key_for_llm(plugin_settings: Optional[Dict[str, Any]] = None, console_log(f"[API_KEY] Получение API-ключа для {selected_llm} ({analysis_type}.{content_language})") if selected_llm == 'default': - # Для Default LLM используем специфичный ключ для комбинации - key_id = f'ozon-analyzer-{analysis_type}-{content_language}' - console_log(f"[API_KEY] Используем специфичный ключ: {key_id}") + # Для Default LLM используем dotted key с .default + key_id = f'ozon-analyzer.{analysis_type}.{content_language}.default' + console_log(f"[API_KEY] Используем специфичный dotted ключ: {key_id}") return key_id else: # Для платформенной LLM используем её ID @@ -2850,39 +2848,50 @@ def get_default_model_id_from_manifest(analysis_type: str = 'basic_analysis', co """ Получить id модели по умолчанию из manifest для данного analysis_type и языка. """ + console_log(f"[DEFAULT_MODEL] Получение модели для {analysis_type}.{content_language}") + manifest = None if plugin_settings and isinstance(plugin_settings, dict): manifest = safe_dict_get(plugin_settings, 'manifest', None) + console_log(f"[DEFAULT_MODEL] Manifest из plugin_settings: {manifest is not None}") + if manifest is None: manifest = get_pyodide_var('manifest', {}) + console_log(f"[DEFAULT_MODEL] Manifest из pyodide globals: {manifest is not None}") + + console_log(f"[DEFAULT_MODEL] Manifest keys: {list(manifest.keys()) if isinstance(manifest, dict) else 'not dict'}") try: prompts = manifest.get('options', {}).get('prompts', {}) + console_log(f"[DEFAULT_MODEL] Prompts keys: {list(prompts.keys()) if isinstance(prompts, dict) else 'not dict'}") + analysis_prompts = prompts.get(analysis_type, {}) + console_log(f"[DEFAULT_MODEL] Analysis prompts for {analysis_type}: {list(analysis_prompts.keys()) if isinstance(analysis_prompts, dict) else 'not dict'}") + lang_prompts = analysis_prompts.get(content_language, {}) + console_log(f"[DEFAULT_MODEL] Language prompts for {content_language}: {list(lang_prompts.keys()) if isinstance(lang_prompts, dict) else 'not dict'}") + default_llm = lang_prompts.get('LLM', {}).get('default', None) + console_log(f"[DEFAULT_MODEL] Default LLM config: {default_llm}") - # Проверяем, есть ли curl_file в default LLM + # Если для Default LLM задан curl_file, не подменяем на платформенные модели + # Возвращаем alias по типу анализа, чтобы background сопоставил его через manifest.ai_models if isinstance(default_llm, dict) and 'curl_file' in default_llm: - # Для Default LLM с curl_file используем fallback модели - if analysis_type == 'basic_analysis': - return 'gemini-flash-lite' - elif analysis_type == 'deep_analysis': - return 'gemini-pro' + console_log(f"[DEFAULT_MODEL] ✅ Found curl_file, returning analysis_type: {analysis_type}") + return analysis_type if isinstance(default_llm, dict) and 'model' in default_llm: + console_log(f"[DEFAULT_MODEL] Found model in default_llm: {default_llm['model']}") return default_llm['model'] if isinstance(default_llm, str): + console_log(f"[DEFAULT_MODEL] Found string default_llm: {default_llm}") return default_llm except Exception as e: console_log(f"[DEFAULT_MODEL] Ошибка получения модели из manifest: {str(e)}") - # Fallback - if analysis_type == 'basic_analysis': - return 'gemini-flash-lite' - if analysis_type == 'deep_analysis': - return 'gemini-pro' - return 'gemini-flash-lite' + # Fallback: возвращаем alias типа анализа, чтобы background решил сопоставление + console_log(f"[DEFAULT_MODEL] Fallback: returning analysis_type {analysis_type}") + return analysis_type async def analyze_ozon_product(input_data: Dict[str, Any] = None, page_html_chunk_count: int = None, @@ -3876,7 +3885,9 @@ async def perform_deep_analysis(input_data: Dict[str, Any]) -> Dict[str, Any]: # Получить правильный model_alias для вызова _call_ai_model if selected_llm == 'default': + console_log(f"[DEEP_ANALYSIS] Calling get_default_model_id_from_manifest for deep_analysis.{content_language}") model_alias = get_default_model_id_from_manifest('deep_analysis', content_language, plugin_settings) + console_log(f"[DEEP_ANALYSIS] get_default_model_id_from_manifest returned: {model_alias}") else: model_alias = selected_llm # Получить правильный API-ключ для выбранной LLM @@ -4352,9 +4363,20 @@ async def _analyze_composition_vs_description(description: str, composition: str # Получаем выбранную пользователем LLM для данного типа анализа и языка selected_llm = get_selected_llm_for_analysis(plugin_settings, 'basic_analysis', content_language) + console_log(f"[BASIC_ANALYSIS] selected_llm: {selected_llm}") if selected_llm == 'default': + console_log(f"[BASIC_ANALYSIS] Calling get_default_model_id_from_manifest for basic_analysis.{content_language}") model_alias = get_default_model_id_from_manifest('basic_analysis', content_language, plugin_settings) + console_log(f"[BASIC_ANALYSIS] get_default_model_id_from_manifest returned: {model_alias}") api_key_id = get_api_key_for_analysis(plugin_settings, 'basic_analysis', content_language, selected_llm) + # Для Default LLM формируем правильный apiKeyId в формате ozon-analyzer.basic_analysis.ru.default + if api_key_id: + api_key_id = f"ozon-analyzer.basic_analysis.{content_language}.default" + console_log(f"[API_KEY_FLOW_PROBLEM_START] selected_llm: {selected_llm}") + console_log(f"[API_KEY_FLOW_PROBLEM_START] model_alias: {model_alias}") + console_log(f"[API_KEY_FLOW_PROBLEM_START] api_key_id: {api_key_id}") + console_log(f"[API_KEY_FLOW_PROBLEM_START] api_key_id type: {type(api_key_id)}") + console_log(f"[API_KEY_FLOW_PROBLEM_START] api_key_id length: {len(api_key_id) if api_key_id else 0}") else: model_alias = selected_llm api_key_id = get_api_key_for_analysis(plugin_settings, 'basic_analysis', content_language, selected_llm) # обычно совпадает с model_alias @@ -4362,6 +4384,7 @@ async def _analyze_composition_vs_description(description: str, composition: str # Используем выбранную LLM и API-ключ result_str = await ozon_analyzer_server._call_ai_model(model_alias, prompt, api_key_id=api_key_id) + console_log(f"[API_KEY_FLOW_PROBLEM_END] LLM call completed") console_log("[DIAGNOSTIC] ===== КОНЕЦ ВЫЗОВА _call_ai_model() =====") # [DIAGNOSTIC] ===== РЕЗУЛЬТАТ ВЫЗОВА AI МОДЕЛИ ===== @@ -5114,9 +5137,9 @@ def get_api_key_for_analysis(plugin_settings: Dict[str, Any], analysis_type: str api_keys = safe_dict_get(plugin_settings, 'api_keys', {}) if api_keys and isinstance(api_keys, dict): # Формируем ключ для поиска API ключа - # Формат: {plugin_id}-{analysis_type}-{language} или просто имя LLM + # Формат: {plugin_id}.{analysis_type}.{language} или просто имя LLM (используем точки как в UI) key_variants = [ - f"ozon-analyzer-{analysis_type}-{content_language}", # ozon-analyzer-basic_analysis-ru + f"ozon-analyzer.{analysis_type}.{content_language}", # ozon-analyzer.basic_analysis.ru f"{analysis_type}_{content_language}", # basic_analysis_ru f"{selected_llm}", # имя выбранной LLM f"{analysis_type}", # просто тип анализа @@ -5125,8 +5148,8 @@ def get_api_key_for_analysis(plugin_settings: Dict[str, Any], analysis_type: str # Для default LLM добавляем дополнительные варианты поиска if selected_llm == 'default': key_variants.extend([ - f"ozon-analyzer-{analysis_type}-{content_language}-default", # ozon-analyzer-basic_analysis-ru-default - f"ozon-analyzer-default", # ozon-analyzer-default (как сохраняется в UI) + f"ozon-analyzer.{analysis_type}.{content_language}.default", # ozon-analyzer.basic_analysis.ru.default + f"ozon-analyzer.default", # ozon-analyzer.default (как сохраняется в UI) "default", # просто default ]) @@ -5165,58 +5188,6 @@ def get_api_key_for_analysis(plugin_settings: Dict[str, Any], analysis_type: str return None -def get_default_model_id_from_manifest(analysis_type: str, content_language: str, plugin_settings: Dict[str, Any]) -> str: - """ - Получить ID модели по умолчанию из манифеста для указанного типа анализа. - - Args: - analysis_type: Тип анализа ('basic_analysis', 'deep_analysis', etc.) - content_language: Язык контента ('ru', 'en') - plugin_settings: Настройки плагина (для доступа к manifest) - - Returns: - ID модели по умолчанию или 'basic_analysis' если не найдено - """ - try: - console_log(f"📋 get_default_model_id_from_manifest: analysis_type={analysis_type}, content_language={content_language}") - - # Получаем manifest из plugin_settings - manifest = None - if plugin_settings and isinstance(plugin_settings, dict): - manifest = safe_dict_get(plugin_settings, 'manifest', None) - - if manifest and isinstance(manifest, dict): - # Ищем в ai_models секции манифеста - ai_models = safe_dict_get(manifest, 'ai_models', {}) - if ai_models and isinstance(ai_models, dict): - # Сначала ищем точное совпадение типа анализа - model_id = safe_dict_get(ai_models, analysis_type, None) - if model_id: - console_log(f"✅ Найдена модель по умолчанию для {analysis_type}: {model_id}") - return model_id - - # Fallback: ищем похожие типы анализа - fallbacks = { - 'basic_analysis': ['compliance_check', 'scraping_fallback'], - 'deep_analysis': ['detailed_comparison', 'basic_analysis'], - 'detailed_comparison': ['deep_analysis', 'basic_analysis'], - 'compliance_check': ['basic_analysis', 'scraping_fallback'], - 'scraping_fallback': ['basic_analysis', 'compliance_check'] - } - - for fallback_type in safe_dict_get(fallbacks, analysis_type, []): - model_id = safe_dict_get(ai_models, fallback_type, None) - if model_id: - console_log(f"✅ Найдена модель fallback для {analysis_type} -> {fallback_type}: {model_id}") - return model_id - - # Последний fallback - возвращаем тип анализа как имя модели - console_log(f"ℹ️ Модель по умолчанию не найдена в manifest, используем: {analysis_type}") - return analysis_type - - except Exception as e: - console_log(f"❌ Ошибка в get_default_model_id_from_manifest: {str(e)}") - return analysis_type def _generate_fallback_analogs(categories: List[str], product_type: str, content_language: str = "ru") -> List[Dict[str, Any]]: diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json index e6aa5e6e..cb45de43 100755 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1492", + "version": "0.27.1519", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/pyodide/test.py b/chrome-extension/public/pyodide/test.py deleted file mode 100755 index bea2ace4..00000000 --- a/chrome-extension/public/pyodide/test.py +++ /dev/null @@ -1,11 +0,0 @@ -print("Hello from Pyodide inline execution!") -print(f"Python version: {__import__('sys').version}") - -# Test basic math -result = 42 * 2 + 10 -print(f"Math result: {result}") - -# Test js bridge -js.console.log("Message from Python to JS console") - -print("test completed successfully") \ No newline at end of file diff --git a/chrome-extension/public/test_api_keys.html b/chrome-extension/public/test_api_keys.html deleted file mode 100644 index 516d2082..00000000 --- a/chrome-extension/public/test_api_keys.html +++ /dev/null @@ -1,393 +0,0 @@ - - - - - - API Key Diagnostics Test - - - -
-

🔑 API Key Diagnostics Test

-

This test will diagnose why GET_API_KEY messages are timing out in the offscreen document.

- - - -
- - - -
- -
- Click a button above to run tests and see results here... -
- -
-

📋 Test Instructions:

-
    -
  1. Make sure the Chrome extension is loaded and active
  2. -
  3. Open this test page in a new tab
  4. -
  5. Click "Run API Key Retrieval Test" first
  6. -
  7. Then try "Check Storage for Encrypted Keys"
  8. -
  9. Analyze the console output and results for timeout issues
  10. -
-
-
- - - - \ No newline at end of file diff --git a/chrome-extension/public/test_api_keys.js b/chrome-extension/public/test_api_keys.js deleted file mode 100644 index 941c4dbc..00000000 --- a/chrome-extension/public/test_api_keys.js +++ /dev/null @@ -1,276 +0,0 @@ -// API Key Diagnostics Test Script -let originalConsoleLog = console.log; -let originalConsoleError = console.error; -let originalConsoleWarn = console.warn; -let capturedLogs = []; - -// Capture console output -function captureConsoleOutput() { - console.log = function(...args) { - capturedLogs.push(['log', ...args]); - originalConsoleLog.apply(console, args); - }; - console.error = function(...args) { - capturedLogs.push(['error', ...args]); - originalConsoleError.apply(console, args); - }; - console.warn = function(...args) { - capturedLogs.push(['warn', ...args]); - originalConsoleWarn.apply(console, args); - }; -} - -// Restore console output -function restoreConsoleOutput() { - console.log = originalConsoleLog; - console.error = originalConsoleError; - console.warn = originalConsoleWarn; -} - -// Format captured logs for display -function formatCapturedLogs() { - return capturedLogs.map(([level, ...args]) => { - const timestamp = new Date().toLocaleTimeString(); - const prefix = `[${timestamp}] ${level.toUpperCase()}:`; - return `${prefix} ${args.join(' ')}`; - }).join('\n'); -} - -// Clear captured logs -function clearCapturedLogs() { - capturedLogs = []; -} - -// Update status display -function updateStatus(message, type = 'info') { - const statusEl = document.getElementById('status'); - statusEl.textContent = message; - statusEl.className = `status ${type}`; - statusEl.style.display = 'block'; -} - -// Update results display -function updateResults(content) { - const resultsEl = document.getElementById('results'); - resultsEl.textContent = content; -} - -// Enable/disable buttons -function setButtonsEnabled(enabled) { - document.getElementById('testAPIKeyBtn').disabled = !enabled; - document.getElementById('checkStorageBtn').disabled = !enabled; -} - -// Test if offscreen document is accessible -async function testOffscreenAccess() { - try { - // Try to access the offscreen document - const offscreenUrl = chrome.runtime.getURL('offscreen.html'); - const response = await fetch(offscreenUrl); - if (response.ok) { - return { success: true, message: 'Offscreen document accessible' }; - } else { - return { success: false, message: `Offscreen document returned ${response.status}` }; - } - } catch (error) { - return { success: false, message: `Cannot access offscreen document: ${error.message}` }; - } -} - -// Run API Key Retrieval Test -async function runAPIKeyTest() { - updateStatus('🔄 Running API Key Retrieval Test...', 'running'); - setButtonsEnabled(false); - clearCapturedLogs(); - captureConsoleOutput(); - - try { - // First check if we can access the offscreen document - const offscreenCheck = await testOffscreenAccess(); - console.log('[TEST_FRAMEWORK] Offscreen access check:', offscreenCheck); - - // Try to communicate with offscreen document to test API key retrieval - console.log('[TEST_FRAMEWORK] Testing direct API key retrieval via GET_API_KEY message...'); - - // Test the specific key ID that was timing out: 'ozon-analyzer-basic_analysis-ru' - const testKeyId = 'ozon-analyzer-basic_analysis-ru'; - console.log(`[TEST_FRAMEWORK] Testing key ID: ${testKeyId}`); - - const startTime = Date.now(); - const firstTestMessage = { - type: 'GET_API_KEY', - data: { keyId: testKeyId }, - testName: 'api_key_timeout_test', - requestId: 'test_' + Date.now(), - timestamp: Date.now() - }; - - console.log('[TEST_FRAMEWORK] Sending GET_API_KEY message:', JSON.stringify(firstTestMessage, null, 2)); - - const firstResponse = await new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error(`Message timeout after 10000ms for keyId: ${testKeyId}`)); - }, 10000); // 10 second timeout - - chrome.runtime.sendMessage(firstTestMessage, (response) => { - clearTimeout(timeoutId); - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - - const endTime = Date.now(); - const duration = endTime - startTime; - - console.log(`[TEST_FRAMEWORK] Response received in ${duration}ms`); - console.log('[TEST_FRAMEWORK] Response:', firstResponse); - - if (firstResponse && firstResponse.apiKey) { - console.log('[TEST_FRAMEWORK] ✅ API key retrieved successfully'); - console.log(`[TEST_FRAMEWORK] Key length: ${firstResponse.apiKey.length} characters`); - console.log(`[TEST_FRAMEWORK] Key starts with: ${firstResponse.apiKey.substring(0, 10)}...`); - - // Test another key ID to verify the fix - const testKeyId2 = 'ozon-analyzer-deep_analysis-en'; - console.log(`[TEST_FRAMEWORK] Testing second key ID: ${testKeyId2}`); - - const testMessage2 = { - type: 'GET_API_KEY', - data: { keyId: testKeyId2 }, - testName: 'api_key_timeout_test_2', - requestId: 'test2_' + Date.now(), - timestamp: Date.now() - }; - - const startTime2 = Date.now(); - const response2 = await new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error(`Message timeout after 10000ms for keyId: ${testKeyId2}`)); - }, 10000); - - chrome.runtime.sendMessage(testMessage2, (response) => { - clearTimeout(timeoutId); - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - - const endTime2 = Date.now(); - const duration2 = endTime2 - startTime2; - - console.log(`[TEST_FRAMEWORK] Second response received in ${duration2}ms`); - console.log('[TEST_FRAMEWORK] Second response:', response2); - - if (response2 && response2.apiKey) { - console.log('[TEST_FRAMEWORK] ✅ Second API key retrieved successfully'); - console.log(`[TEST_FRAMEWORK] Second key length: ${response2.apiKey.length} characters`); - } else { - console.log('[TEST_FRAMEWORK] ❌ Second API key retrieval failed'); - console.log('[TEST_FRAMEWORK] Second response error:', response2?.error); - } - - } else { - console.log('[TEST_FRAMEWORK] ❌ API key retrieval failed'); - console.log('[TEST_FRAMEWORK] Response error:', firstResponse?.error); - } - - // Test completed immediately - no need for additional wait - // Wait a moment for all logs to be captured - setTimeout(() => { - restoreConsoleOutput(); - const results = formatCapturedLogs(); - updateResults(results); - updateStatus('✅ API Key Retrieval Test Completed', 'success'); - setButtonsEnabled(true); - }, 1000); - - } catch (error) { - restoreConsoleOutput(); - console.error('[TEST_FRAMEWORK] Test execution failed:', error); - updateResults(`❌ Test execution failed: ${error.message}\n\nCaptured logs:\n${formatCapturedLogs()}`); - updateStatus('❌ Test Failed', 'error'); - setButtonsEnabled(true); - } -} - -// Run Storage Check -async function runStorageCheck() { - updateStatus('🔄 Checking Storage for Encrypted Keys...', 'running'); - setButtonsEnabled(false); - clearCapturedLogs(); - captureConsoleOutput(); - - try { - console.log('[TEST_FRAMEWORK] Starting storage check...'); - - const testMessage = { - type: 'EXECUTE_PYTHON_CODE', - code: ` -import js -# Call the storage check function if available -if hasattr(js, 'checkEncryptedAPIKeys'): - result = js.checkEncryptedAPIKeys() - print("[PYTHON_TEST] checkEncryptedAPIKeys called") -else: - print("[PYTHON_TEST] checkEncryptedAPIKeys not available in js bridge") - `, - testName: 'storage_check', - requestId: 'storage_' + Date.now(), - timestamp: Date.now() - }; - - console.log('[TEST_FRAMEWORK] Sending storage check message to offscreen:', testMessage.type); - - const response = await new Promise((resolve, reject) => { - chrome.runtime.sendMessage(testMessage, (response) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - - console.log('[TEST_FRAMEWORK] Received response from offscreen:', response); - - // Wait a moment for all logs to be captured - setTimeout(() => { - restoreConsoleOutput(); - const results = formatCapturedLogs(); - updateResults(results); - updateStatus('✅ Storage Check Completed', 'success'); - setButtonsEnabled(true); - }, 1000); - - } catch (error) { - restoreConsoleOutput(); - console.error('[TEST_FRAMEWORK] Storage check failed:', error); - updateResults(`❌ Storage check failed: ${error.message}\n\nCaptured logs:\n${formatCapturedLogs()}`); - updateStatus('❌ Storage Check Failed', 'error'); - setButtonsEnabled(true); - } -} - -// Clear results -function clearResults() { - updateResults('Results cleared. Click a button above to run tests...'); - document.getElementById('status').style.display = 'none'; - clearCapturedLogs(); -} - -// Initialize -document.addEventListener('DOMContentLoaded', function() { - console.log('[TEST_FRAMEWORK] API Key Diagnostics Test page loaded'); - updateStatus('Ready to run tests', 'success'); -}); - -// Make functions globally available -window.runAPIKeyTest = runAPIKeyTest; -window.runStorageCheck = runStorageCheck; -window.clearResults = clearResults; \ No newline at end of file diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index 49b06444..69a24411 100755 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -1319,6 +1319,17 @@ async function executeWorkflowInOffscreen( console.log(`[WORKFLOW_EXECUTION] HTML data length: ${htmlData?.length || 0}`); console.log(`[WORKFLOW_EXECUTION] Plugin settings:`, pluginSettings); + // [API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_START'); + console.log('[API_KEY_FLOW] executeWorkflowInOffscreen called with pluginId:', pluginId); + console.log('[API_KEY_FLOW] Plugin settings received:', { + hasApiKeys: !!pluginSettings.api_keys, + hasPrompts: !!pluginSettings.prompts, + settingsKeys: Object.keys(pluginSettings), + apiKeysCount: pluginSettings.api_keys ? Object.keys(pluginSettings.api_keys).length : 0, + promptsCount: pluginSettings.prompts ? Object.keys(pluginSettings.prompts).length : 0 + }); + // Получить API ключ для Gemini let geminiApiKey: string | null | undefined = apiKey; if (!geminiApiKey) { @@ -1351,8 +1362,20 @@ async function executeWorkflowInOffscreen( console.log('[BACKGROUND] 🔑 Response language в настройках:', pluginSettings?.response_language); console.log('[BACKGROUND] 📊 Все настройки для передачи в worker:', pluginSettings); + console.log('[API_KEY_FLOW] Workflow payload being sent to offscreen:', { + type: workflowPayload.type, + pluginId: workflowPayload.pluginId, + hasPluginSettings: !!workflowPayload.pluginSettings, + pluginSettingsKeys: Object.keys(workflowPayload.pluginSettings), + hasApiKeys: !!(workflowPayload.pluginSettings as any).api_keys, + hasPrompts: !!(workflowPayload.pluginSettings as any).prompts, + hasGeminiApiKey: !!workflowPayload.geminiApiKey + }); + try { + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_MESSAGE_TO_OFFSCREEN_START'); const result = await chrome.runtime.sendMessage(workflowPayload); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_MESSAGE_TO_OFFSCREEN_END'); if (chrome.runtime.lastError) { throw new Error(chrome.runtime.lastError.message); } @@ -1367,6 +1390,8 @@ async function executeWorkflowInOffscreen( console.error(`[WORKFLOW_EXECUTION] ❌ Workflow execution error:`, error); throw error; } + + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_END'); } // === MAIN MESSAGE HANDLERS === @@ -1473,6 +1498,43 @@ chrome.runtime.onMessage.addListener( // Load prompts from manifest.json for all plugins let enrichedPluginSettings = { ...pluginSettings }; + + // Добавляем manifest в настройки для доступа в Python + if (manifest) { + (enrichedPluginSettings as any).manifest = manifest; + } + + // Попытаемся дополнить настройками api_keys для Default LLM (по комбинациям promptType/language) + try { + const defaultCombos = [ + { type: 'basic_analysis', lang: 'ru' }, + { type: 'basic_analysis', lang: 'en' }, + { type: 'deep_analysis', lang: 'ru' }, + { type: 'deep_analysis', lang: 'en' }, + ]; + + const apiKeys: Record = {}; + for (const combo of defaultCombos) { + const keyId = `ozon-analyzer.${combo.type}.${combo.lang}.default`; + try { + const key = await APIKeyManager.getDecryptedKey(keyId); + if (key && typeof key === 'string' && key.length > 0) { + apiKeys[keyId] = key; + } + } catch (e) { + // пропускаем отсутствие ключа + } + } + + if (Object.keys(apiKeys).length > 0) { + (enrichedPluginSettings as any).api_keys = { + ...(enrichedPluginSettings as any).api_keys, + ...apiKeys, + }; + } + } catch (e) { + console.warn('[BACKGROUND] Failed to enrich api_keys for plugin', msg.pluginId, e); + } // Load prompts from manifest.json if available if (manifest?.options?.prompts) { @@ -1626,13 +1688,45 @@ chrome.runtime.onMessage.addListener( // Используем enrichedPluginSettings (с prompts из manifest.json) вместо обычных pluginSettings const settingsToSend = enrichedPluginSettings; + // [API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_START'); + console.log('[API_KEY_FLOW] Plugin ID:', msg.pluginId); + console.log('[API_KEY_FLOW] Settings to send keys:', Object.keys(settingsToSend)); + console.log('[API_KEY_FLOW] Settings to send has api_keys:', !!settingsToSend.api_keys); + if (settingsToSend.api_keys) { + console.log('[API_KEY_FLOW] Available API key IDs:', Object.keys(settingsToSend.api_keys)); + console.log('[API_KEY_FLOW] API keys structure:', Object.keys(settingsToSend.api_keys).map(key => ({ + keyId: key, + hasValue: !!(settingsToSend.api_keys as any)[key], + valueLength: (settingsToSend.api_keys as any)[key] ? (settingsToSend.api_keys as any)[key].length : 0 + }))); + } + console.log('[API_KEY_FLOW] Settings to send has prompts:', !!settingsToSend.prompts); + if (settingsToSend.prompts) { + console.log('[API_KEY_FLOW] Prompts structure:', Object.keys(settingsToSend.prompts)); + } + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_END'); + + // [API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_START'); + console.log('[API_KEY_FLOW] Transmission mode:', htmlTransmissionMode); + console.log('[API_KEY_FLOW] HTML size:', pageHtml.length, 'chars'); + if (htmlTransmissionMode === 'direct') { // ПРЯМАЯ ПЕРЕДАЧА HTML console.log('[background][RUN_WORKFLOW] 📨 Using DIRECT HTML transmission'); console.log('[background][RUN_WORKFLOW] HTML size:', pageHtml.length, 'chars'); try { + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_HTML_DIRECTLY_START'); + console.log('[API_KEY_FLOW] Calling sendHtmlDirectly with pluginId:', msg.pluginId); + console.log('[API_KEY_FLOW] Settings being passed to sendHtmlDirectly:', { + hasApiKeys: !!settingsToSend.api_keys, + hasPrompts: !!settingsToSend.prompts, + settingsKeys: Object.keys(settingsToSend) + }); await sendHtmlDirectly(msg.pluginId, pageKey, pageHtml, requestId, transferId, settingsToSend); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_HTML_DIRECTLY_END'); console.log('[background][RUN_WORKFLOW] ✅ Direct transmission completed'); } catch (directError) { console.log('[background][RUN_WORKFLOW] ❌ Direct transmission failed, switching to chunked mode'); @@ -1723,10 +1817,21 @@ chrome.runtime.onMessage.addListener( activeTransfers.set(transferId, transferState); // Отправляем chunks + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_CHUNKS_TO_OFFSCREEN_START'); + console.log('[API_KEY_FLOW] Calling sendChunksToOffscreen with transferId:', transferId); + console.log('[API_KEY_FLOW] Metadata being passed to sendChunksToOffscreen:', { + hasPluginSettings: !!transferState.metadata.pluginSettings, + pluginSettingsKeys: transferState.metadata.pluginSettings ? Object.keys(transferState.metadata.pluginSettings) : [], + hasApiKeys: !!(transferState.metadata.pluginSettings as any)?.api_keys, + hasPrompts: !!(transferState.metadata.pluginSettings as any)?.prompts + }); await sendChunksToOffscreen(transferId, chunkingResult.chunks, transferState.metadata); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_CHUNKS_TO_OFFSCREEN_END'); console.log('[background][RUN_WORKFLOW] ✅ Chunked transmission completed'); } + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_END'); + console.log('[background][RUN_WORKFLOW] ===== RUN_WORKFLOW HANDLER COMPLETED ====='); sendResponse({ success: true }); @@ -2201,6 +2306,9 @@ const handleHostApiMessage = async ( break; } case 'llm_call': { + console.log('[HOST API] ===== LLM_CALL HANDLER START ====='); + console.log('[HOST API] Message received:', message); + console.log('[HOST API] Message data:', message.data); try { const { modelAlias, options, pluginId, apiKeyId } = message.data as { modelAlias: string; @@ -2234,8 +2342,31 @@ const handleHostApiMessage = async ( console.log(`[DEBUG] manifestResponse status:`, manifestResponse.status); const aiModels = manifest.ai_models || {}; + // Проверяем, является ли это Default LLM с curl_file + const prompts = manifest.options?.prompts; + let isDefaultLLMWithCurl = false; + let curlFile = null; + + if (prompts && modelAlias) { + // Ищем в prompts для всех комбинаций тип/язык + for (const [promptType, promptConfig] of Object.entries(prompts)) { + if (promptType === modelAlias) { + for (const [language, langConfig] of Object.entries(promptConfig as any)) { + const llmConfig = (langConfig as any).LLM; + if (llmConfig?.default?.curl_file) { + isDefaultLLMWithCurl = true; + curlFile = llmConfig.default.curl_file; + console.log(`[HOST API] Found Default LLM with curl_file for ${modelAlias}.${language}: ${curlFile}`); + break; + } + } + if (isDefaultLLMWithCurl) break; + } + } + } + const actualModel = aiModels[modelAlias]; - if (!actualModel) { + if (!actualModel && !isDefaultLLMWithCurl) { sendResponse({ error: true, error_message: `Модель с алиасом '${modelAlias}' не найдена в манифесте плагина` @@ -2243,26 +2374,49 @@ const handleHostApiMessage = async ( return true; } - console.log('[HOST API] Using model:', actualModel, 'for alias:', modelAlias); + // Выбираем модель для вызова + let modelToUse; + if (isDefaultLLMWithCurl) { + // Для Default LLM с curl_file используем специальный обработчик + modelToUse = 'default-curl'; + console.log('[HOST API] Using Default LLM with curl_file:', curlFile); + } else { + modelToUse = actualModel; + console.log('[HOST API] Using model:', actualModel, 'for alias:', modelAlias); + } // Получаем API-ключ: если apiKeyId указан, используем его, иначе используем model alias - const keyId = apiKeyId || actualModel; + // Учитываем вариант, когда apiKeyId передается внутри options (из Python/pyodide) + const keyId = apiKeyId || (options && options.apiKeyId) || actualModel; console.log('[HOST API] Getting API key for:', keyId); const apiKey = await APIKeyManager.getDecryptedKey(keyId); if (!apiKey) { sendResponse({ error: true, - error_message: `API ключ для модели ${actualModel} не найден. KeyId: ${keyId}` + error_message: `API ключ для модели ${modelToUse} не найден. KeyId: ${keyId}` }); return true; } try { - const aiResponse = await callAiModel(actualModel, apiKey, options.prompt || ''); - sendResponse({ - response: aiResponse - }); + if (isDefaultLLMWithCurl) { + // Для Default LLM с curl_file используем специальную обработку + console.log('[HOST API] Processing Default LLM with curl_file:', curlFile); + // TODO: Реализовать обработку curl_file + // Пока используем fallback на обычную модель + const fallbackModel = actualModel || 'gemini-flash-lite'; + console.log('[HOST API] Using fallback model:', fallbackModel); + const aiResponse = await callAiModel(fallbackModel, apiKey, options.prompt || ''); + sendResponse({ + response: aiResponse + }); + } else { + const aiResponse = await callAiModel(modelToUse, apiKey, options.prompt || ''); + sendResponse({ + response: aiResponse + }); + } } catch (aiError) { console.error('[HOST API] AI API error:', aiError); sendResponse({ @@ -2277,6 +2431,7 @@ const handleHostApiMessage = async ( error_message: (error as Error).message }); } + console.log('[HOST API] ===== LLM_CALL HANDLER END ====='); break; } case 'GET_API_KEY': { diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json index e5610469..0a59b7fd 100755 --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.942", + "version": "1.0.969", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/package.json b/package.json index b641319b..5379a2bd 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1467", + "version": "1.0.1494", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 5f469eb0..91090c56 100755 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 5e2785f9..0a68217d 100755 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1472", + "version": "0.5.1499", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index f9289ecc..58c7e02c 100755 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index c71aff0e..99b1d9cc 100755 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index 652efaa5..c61d4e6a 100755 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 530b6744..cfb30547 100755 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 239dca93..62189ba9 100755 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 04156264..992554c2 100755 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 626bce15..c33ad049 100755 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 6c7868f1..85699a91 100755 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 0ff5bff0..c5eec8bf 100755 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1493", + "version": "0.5.1520", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 92cb5cb6..01664a43 100755 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index 6cd46fd7..b59df2b9 100755 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json index 50e70891..a4da970e 100755 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index 6d3caaed..5a3adc86 100755 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 752b19f9..74a9898a 100755 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index b09bd25a..c5464092 100755 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 5c4a0a79..8bea167d 100755 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index 174e8f06..db072729 100755 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.1485", + "version": "0.5.1512", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index e0cc4453..de810a05 100755 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1490", + "version": "0.27.1517", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index e6aa5e6e..cb45de43 100755 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1492", + "version": "0.27.1519", "description": "The Pyodide JavaScript package", "keywords": [ "python", From 6e4bd550de870fd883d099bf59adbc1b2f86c5d0 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Thu, 30 Oct 2025 07:59:58 +0500 Subject: [PATCH 15/15] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D0=B0=D1=80?= =?UTF-8?q?=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B5=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B1=D0=BE=D1=80=D0=B0=20LLM=20=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=BA=D1=83=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BB=D0=B0=D0=B3=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=20ozon-analyzer,=20=D0=B2=D0=BA=D0=BB=D1=8E=D1=87=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8F,=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B0=D0=BC=D0=B8=20=D0=B8=20=D0=B8=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8E=20=D1=81=20Python.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../architecture/llm-selection-data-flow.md | 348 ++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 memory-bank/architecture/llm-selection-data-flow.md diff --git a/memory-bank/architecture/llm-selection-data-flow.md b/memory-bank/architecture/llm-selection-data-flow.md new file mode 100644 index 00000000..25e0a6ff --- /dev/null +++ b/memory-bank/architecture/llm-selection-data-flow.md @@ -0,0 +1,348 @@ +# LLM Selection and Data Flow Architecture + +## Overview + +This document describes the comprehensive architecture for LLM selection and data flow in the Agent Plugins Platform, focusing on the ozon-analyzer plugin as the primary example. The system enables users to configure different LLM models for different analysis types (basic analysis vs deep analysis) and languages (Russian vs English), with sophisticated API key management and settings persistence. + +updated: 2025.10.30-07:30:00 + +## 1. UI Architecture + +### Plugin Options Interface + +The user interface for LLM configuration is implemented in `pages/options/src/components/PluginDetails.tsx` and `pages/options/src/components/LLMSelector.tsx`. The interface provides: + +#### PluginDetails Component +- **Location**: `pages/options/src/components/PluginDetails.tsx` +- **Purpose**: Main plugin configuration interface +- **Key Features**: + - Displays plugin information (name, version, author, description) + - Shows host permissions and extension permissions + - Contains `PromptsEditor` component for prompt customization + - Manages plugin enable/disable and autorun settings + +#### PromptsEditor Component +- **Location**: Within `PluginDetails.tsx` (lines 48-337) +- **Purpose**: Manages prompt types and language selection +- **Features**: + - Tab-based interface for switching between `basic_analysis` and `deep_analysis` + - Language tabs for `ru` (Russian) and `en` (English) + - Side-by-side display of original prompts (read-only) and custom prompts (editable) + - Copy button to transfer original prompts to custom editing area + - Save functionality to persist custom prompts + +#### LLMSelector Component +- **Location**: `pages/options/src/components/LLMSelector.tsx` +- **Purpose**: Binds specific LLMs to prompt types and languages +- **Key Props**: + - `promptType`: `'basic_analysis' | 'deep_analysis'` + - `language`: `'ru' | 'en'` + - `globalAIKeys`: Array of available platform LLM configurations + - `defaultLLMCurl`: Default LLM model name from manifest + - `hasDefaultLLM`: Boolean indicating if default LLM is available + - `onLLMChange`: Callback for LLM selection changes + +#### LLM Selection Options +The LLM selector provides two types of LLM configurations: + +1. **Default LLM**: Uses `gemini-flash-lite` for basic analysis, `gemini-pro` for deep analysis + - Requires API key input in a password field + - API key stored encrypted with key ID pattern: `ozon-analyzer.{promptType}.{language}.default` + +2. **Platform LLMs**: Custom LLM configurations from `globalAIKeys` + - Pre-configured API keys managed at platform level + - No manual API key input required + +### Legacy Options Interface + +A legacy options interface exists at `chrome-extension/public/options/index.html`: +- **Purpose**: Basic HTML transmission mode settings +- **Limitations**: Does not support LLM selection or prompt customization +- **Status**: Superseded by React-based interface in `pages/options/` + +## 2. Data Flow Architecture + +### Settings Storage Flow + +1. **User Selection** → `LLMSelector.handleLLMChange()` +2. **Validation** → Update local component state +3. **Settings Update** → `usePluginSettings.updateBasicAnalysisSettings()` or `updateDeepAnalysisSettings()` +4. **Encryption** → API keys encrypted via `APIKeyManager.saveEncryptedKey()` +5. **Persistence** → Settings saved to `chrome.storage.local` with key `'plugin-ozon-analyzer-settings'` +6. **Global State** → Settings propagated to `pyodide.globals.pluginSettings` + +### Settings Structure + +```typescript +interface PluginSettings { + basic_analysis: { + ru: { llm: string; custom_prompt: string }; + en: { llm: string; custom_prompt: string }; + }; + deep_analysis: { + ru: { llm: string; custom_prompt: string }; + en: { llm: string; custom_prompt: string }; + }; + api_keys: { + default: string; // encrypted + [key: string]: string; // other encrypted keys + }; +} +``` + +### Data Propagation Path + +``` +User Selection → LLMSelector → usePluginSettings → chrome.storage.local + ↓ +pyodide.globals.pluginSettings (via PluginDetails useEffect) + ↓ +Python mcp_server.py (get_user_prompts, get_selected_llm_for_analysis, get_api_key_for_analysis) + ↓ +LLM API Call (via js.llm_call in offscreen.js) +``` + +## 3. Plugin Configuration (manifest.json) + +### LLM Configuration Structure + +The `manifest.json` defines LLM options in the `ai_models` and `options.prompts` sections: + +```json +{ + "ai_models": { + "basic_analysis": "gemini-flash-lite", + "compliance_check": "gemini-flash-lite", + "detailed_comparison": "gemini-pro", + "deep_analysis": "gemini-pro", + "scraping_fallback": "gemini-flash-lite" + }, + "options": { + "prompts": { + "basic_analysis": { + "ru": { + "type": "text", + "default": "[Russian prompt content]", + "label": "Базовый промпт (русский)", + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-ru.curl" + } + } + }, + "en": { + "type": "text", + "default": "[English prompt content]", + "label": "Basic Prompt (English)", + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-en.curl" + } + } + } + }, + "deep_analysis": { + "ru": { /* similar structure */ }, + "en": { /* similar structure */ } + } + } + } +} +``` + +### Manifest Parsing + +- **Location**: `PluginDetails.tsx` (lines 65-103) +- **Functions**: + - `getDefaultLLMForPrompt()`: Maps prompt types to default LLM models + - `hasDefaultLLMForPrompt()`: Checks LLM availability for specific prompts + - `getOriginalPrompt()`: Retrieves default prompts from manifest + +## 4. Settings Storage + +### Chrome Storage Architecture + +#### Primary Storage: chrome.storage.local +- **Key**: `'plugin-ozon-analyzer-settings'` +- **Content**: Complete PluginSettings object with encrypted API keys +- **Encryption**: API keys encrypted using `APIKeyManager` before storage + +#### API Key Encryption +- **Location**: `pages/options/src/utils/encryption.ts` +- **Method**: AES encryption with derived keys +- **Key IDs**: Follow pattern `ozon-analyzer.{promptType}.{language}.default` + +#### Storage Flow +``` +User Input → APIKeyManager.saveEncryptedKey() → chrome.storage.local + ↓ +Retrieval: chrome.storage.local → APIKeyManager.getDecryptedKey() → Component State +``` + +### Pyodide Globals Integration + +Settings are propagated to Python execution environment: + +```javascript +// In PluginDetails.tsx useEffect (lines 362-398) +const updatedPluginSettings = { + ...pluginSettings, + selected_llms: { + basic_analysis: { ru: llm, en: llm }, + deep_analysis: { ru: llm, en: llm } + }, + api_keys: { /* encrypted keys */ } +}; + +pyodide.globals.pluginSettings = updatedPluginSettings; +``` + +## 5. Python Integration (mcp_server.py) + +### Settings Retrieval Functions + +#### get_user_prompts() +- **Location**: `mcp_server.py` (lines 214-407) +- **Purpose**: Loads prompts from plugin settings or manifest defaults +- **Priority Order**: + 1. Custom prompts from `pluginSettings.prompts` + 2. Default prompts from `manifest.options.prompts` + 3. Built-in fallback prompts + +#### get_selected_llm_for_analysis() +- **Location**: `mcp_server.py` (lines 5076-5113) +- **Purpose**: Retrieves selected LLM for specific analysis type and language +- **Returns**: LLM identifier ('default', 'gemini-flash-lite', etc.) + +#### get_api_key_for_analysis() +- **Location**: `mcp_server.py` (lines 5119-5184) +- **Purpose**: Retrieves API key for specific analysis configuration +- **Key Variants Checked**: + - `ozon-analyzer.{analysis_type}.{language}` + - `{analysis_type}_{language}` + - Selected LLM name + - Fallback to global settings + +### LLM Execution Flow + +1. **Analysis Initiation** → `analyze_ozon_product()` function +2. **Settings Loading** → `get_user_prompts()`, `get_selected_llm_for_analysis()` +3. **API Key Retrieval** → `get_api_key_for_analysis()` +4. **LLM Call** → `js.llm_call()` with model alias and options +5. **Response Processing** → Parse and return analysis results + +## 6. API Key Management + +### Default LLM Keys +- **Storage**: Encrypted in `chrome.storage.local` +- **Key ID Pattern**: `ozon-analyzer.{promptType}.{language}.default` +- **Encryption**: AES via `APIKeyManager` +- **Access**: Direct from encrypted storage + +### Platform LLM Keys +- **Storage**: Managed at platform level in `globalAIKeys` +- **Access**: Via background script message passing +- **Message Type**: `'GET_API_KEY'` +- **Response**: `{ apiKey: string }` + +### API Key Flow in offscreen.js + +#### For Default LLMs +```javascript +// Direct encrypted storage access +const apiKey = await APIKeyManager.getDecryptedKey(keyId); +``` + +#### For Platform LLMs +```javascript +// Message passing to background +const response = await safeSendMessage({ + type: 'GET_API_KEY', + data: { keyId: keyId } +}); +``` + +### Enhanced LLM Call (offscreen.js lines 2262-2413) + +The `js.llm_call` function in offscreen.js provides enhanced API key management: + +1. **Model Alias Processing**: Strips `:generateContent` suffix +2. **API Key Priority**: + - Direct API key in options + - Plugin settings API keys + - Background script retrieval + - Window global fallback +3. **Gemini API Integration**: Direct HTTP calls for Default LLMs +4. **Background Delegation**: Platform LLMs routed through background script + +## 7. Execution Flow + +### Complete Path from User Selection to LLM API Call + +``` +1. User selects LLM in PluginDetails.tsx LLMSelector + ↓ +2. LLMSelector.handleLLMChange() updates settings + ↓ +3. usePluginSettings saves to chrome.storage.local (encrypted) + ↓ +4. PluginDetails useEffect propagates to pyodide.globals.pluginSettings + ↓ +5. Python execution: analyze_ozon_product() called + ↓ +6. get_selected_llm_for_analysis() retrieves LLM choice + ↓ +7. get_api_key_for_analysis() retrieves API key + ↓ +8. js.llm_call() invoked with model and options + ↓ +9. offscreen.js enhanced llm_call processes request + ↓ +10. For Default LLMs: Direct Gemini API HTTP call + For Platform LLMs: Background script delegation + ↓ +11. Response returned through Pyodide bridge + ↓ +12. Analysis results processed and displayed +``` + +### Key Integration Points + +#### JavaScript ↔ Python Bridge +- **Location**: `offscreen.js` jsBridge object (lines 533-1111) +- **Functions**: + - `sendMessageToChat()`: Sends messages to chat interface + - `llm_call()`: Enhanced LLM API integration + - `get_setting()`: Settings retrieval + - `updateContext()`: Context management + +#### Settings Propagation +- **Trigger**: PluginDetails component mount and settings changes +- **Method**: Direct assignment to `pyodide.globals.pluginSettings` +- **Content**: Complete settings object with selected LLMs and API keys + +#### Error Handling +- **API Key Errors**: Graceful fallback with user-friendly messages +- **Network Errors**: Timeout handling and retry logic +- **Configuration Errors**: Validation and default fallbacks + +## 8. Architecture Patterns + +### Separation of Concerns +- **UI Layer**: React components handle user interaction +- **Settings Layer**: usePluginSettings manages persistence +- **Bridge Layer**: offscreen.js provides JS↔Python communication +- **Execution Layer**: Python handles LLM calls and analysis + +### Security Considerations +- **API Key Encryption**: All keys encrypted at rest +- **Isolated Execution**: Python runs in Pyodide sandbox +- **Message Validation**: All cross-context communication validated + +### Performance Optimizations +- **Lazy Loading**: Settings loaded on demand +- **Caching**: API key caching with TTL +- **Chunking**: Large HTML data transferred in chunks +- **Async Processing**: Non-blocking UI updates + +This architecture provides a robust, scalable system for LLM configuration and execution while maintaining security, performance, and user experience standards. \ No newline at end of file