From 7a012e6c01a1b73715eeec5aca68e28d129d6af7 Mon Sep 17 00:00:00 2001 From: Igor Lebedev Date: Wed, 16 Jul 2025 00:51:17 +0500 Subject: [PATCH] fix(e2e): use ES module import for fs in page-side-panel.test.ts to satisfy eslint rules --- .husky/pre-push | 2 + bash-scripts/prevent-main-develop-push.sh | 6 + chrome-extension/package.json | 2 +- chrome-extension/public/pyodide/package.json | 2 +- chrome-extension/src/background/index.ts | 33 +- .../src/background/plugin-chat-api.ts | 55 ++-- docs/best-practices/cursor-rules-system.md | 288 ------------------ .../testing-and-troubleshooting.md | 26 -- docs/best-practices/transferability.md | 31 -- docs/developer-commands.md | 76 +++++ .../README.md | 17 ++ .../ai-first-documentation.md | 0 .../ci-integration.md | 28 +- .../development-principles.md | 0 .../roadmap-sync.md | 0 .../testing-and-troubleshooting.md | 63 ++++ docs/for-ai-best-practices/transferability.md | 5 + docs/transferability.ru.md | 100 ++++++ .../chrome-extension-boilerplate-react-vite | 1 + external/sidepanel-template | 1 + memory-bank/errors.md | 35 ++- 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-panel/package.json | 2 +- pages/devtools/package.json | 2 +- pages/new-tab/package.json | 2 +- pages/options/package.json | 2 +- pages/popup/package.json | 2 +- pages/side-panel/package.json | 2 +- .../src/components/PluginControlPanel.tsx | 96 ++++-- pages/side-panel/src/hooks/useLazyChatSync.ts | 38 ++- platform-core/public/pyodide/package.json | 2 +- public/pyodide/package.json | 2 +- tests/e2e/helpers/theme.ts | 4 +- tests/e2e/package.json | 2 +- tests/e2e/specs/page-side-panel.test.ts | 91 +++++- 50 files changed, 602 insertions(+), 448 deletions(-) create mode 100755 .husky/pre-push create mode 100755 bash-scripts/prevent-main-develop-push.sh delete mode 100644 docs/best-practices/cursor-rules-system.md delete mode 100644 docs/best-practices/testing-and-troubleshooting.md delete mode 100644 docs/best-practices/transferability.md create mode 100644 docs/developer-commands.md rename docs/{best-practices => for-ai-best-practices}/README.md (59%) rename docs/{best-practices => for-ai-best-practices}/ai-first-documentation.md (100%) rename docs/{best-practices => for-ai-best-practices}/ci-integration.md (59%) rename docs/{best-practices => for-ai-best-practices}/development-principles.md (100%) rename docs/{best-practices => for-ai-best-practices}/roadmap-sync.md (100%) create mode 100644 docs/for-ai-best-practices/testing-and-troubleshooting.md create mode 100644 docs/for-ai-best-practices/transferability.md create mode 100644 docs/transferability.ru.md create mode 160000 external/chrome-extension-boilerplate-react-vite create mode 160000 external/sidepanel-template diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 00000000..292dc996 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,2 @@ +#!/bin/sh +bash bash-scripts/prevent-main-develop-push.sh diff --git a/bash-scripts/prevent-main-develop-push.sh b/bash-scripts/prevent-main-develop-push.sh new file mode 100755 index 00000000..19e2b515 --- /dev/null +++ b/bash-scripts/prevent-main-develop-push.sh @@ -0,0 +1,6 @@ +#!/bin/bash +branch=$(git rev-parse --abbrev-ref HEAD) +if [[ "$branch" == "main" || "$branch" == "develop" ]]; then + echo "\e[31mDirect push to $branch is forbidden! Use PRs and merge only from allowed branches.\e[0m" + exit 1 +fi diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 06fef197..7711c3e7 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension", - "version": "0.5.164", + "version": "0.5.169", "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 06a7fab2..1fc4db17 100644 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.171", + "version": "0.27.176", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index ffe6eb5f..440e8f9b 100644 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -200,10 +200,35 @@ chrome.runtime.onMessage.addListener( if (msg.type === 'GET_PLUGIN_CHAT' && msg.pluginId && msg.pageKey) { const { pluginId, pageKey } = msg; const chatKey = `${pluginId}::${getPageKey(pageKey)}`; - pluginChatApi.getOrLoadChat(chatKey).then(chat => { - console.log('[background] sendResponse(GET_PLUGIN_CHAT):', chat); - sendResponse(chat || { messages: [] }); - }); + pluginChatApi + .getOrLoadChat(chatKey) + .then(chat => { + let safeChat = chat; + if (chat && Array.isArray(chat.messages) && chat.messages.length > 50) { + safeChat = { ...chat, messages: chat.messages.slice(-50) }; + } + try { + const serializable = JSON.parse(JSON.stringify(safeChat)); + console.log( + '[background] sendResponse(GET_PLUGIN_CHAT):', + serializable, + 'chatKey:', + chatKey, + 'pageKey:', + pageKey, + ); + sendResponse(serializable || { messages: [] }); + } catch (err) { + console.error('[background] Ошибка сериализации чата:', err, safeChat); + sendResponse({ error: 'serialization failed', details: String(err) }); + } + console.log('[background] return true после sendResponse(GET_PLUGIN_CHAT)'); + }) + .catch(err => { + console.error('[background] Ошибка в getOrLoadChat:', err); + sendResponse({ error: String(err) }); + console.log('[background] return true после sendResponse(GET_PLUGIN_CHAT) [catch]'); + }); return true; } diff --git a/chrome-extension/src/background/plugin-chat-api.ts b/chrome-extension/src/background/plugin-chat-api.ts index b2906170..d5f8cba6 100644 --- a/chrome-extension/src/background/plugin-chat-api.ts +++ b/chrome-extension/src/background/plugin-chat-api.ts @@ -39,7 +39,7 @@ const pluginChatApi = { return new Promise(resolve => { chrome.storage.local.get([chatKey], result => { const chat = result[chatKey] || null; - console.log('[pluginChatApi] getOrLoadChat:', chatKey, chat); + console.log('[pluginChatApi] getOrLoadChat:', chatKey, chat, chat?.messages); resolve(chat); }); }); @@ -48,17 +48,29 @@ const pluginChatApi = { // Сохранить сообщение в чат async saveMessage(pluginId: string, pageKey: string, message: ChatMessage): Promise<{ success: boolean }> { const chatKey = `${pluginId}::${getPageKey(pageKey)}`; - const chat = await this.getOrLoadChat(chatKey); + console.log('[pluginChatApi][saveMessage] BEFORE', { chatKey, pluginId, pageKey, message }); + let chat = await this.getOrLoadChat(chatKey); if (!chat) { - console.warn('[pluginChatApi] saveMessage: чат не найден, создаём новый'); + console.warn('[pluginChatApi][saveMessage] чат не найден, создаём новый'); await this.createChatIfNotExists(pluginId, pageKey); - return this.saveMessage(pluginId, pageKey, message); + // После создания чата — получить его снова + chat = await this.getOrLoadChat(chatKey); + if (!chat) { + // Если всё равно не найден — ошибка + console.error('[pluginChatApi][saveMessage] не удалось создать чат!'); + return { success: false }; + } } chat.messages.push(message); + console.log('[pluginChatApi][saveMessage] chat.messages после push:', chat.messages); chat.updatedAt = Date.now(); await new Promise(resolve => { chrome.storage.local.set({ [chatKey]: chat }, () => { - console.log('[pluginChatApi] saveMessage: добавляем сообщение в чат', chat); + console.log('[pluginChatApi][saveMessage] AFTER set:', { chatKey, chat }); + // Проверка: что реально лежит в storage после set + chrome.storage.local.get([chatKey], result => { + console.log('[pluginChatApi][saveMessage] ПРОВЕРКА storage после set:', result[chatKey]); + }); resolve(); }); }); @@ -87,9 +99,10 @@ const pluginChatApi = { text, updatedAt: Date.now(), }; + console.log('[pluginChatApi][saveDraft] BEFORE', { draftKey, pluginId, pageKey, text }); await new Promise(resolve => { chrome.storage.local.set({ [draftKey]: draft }, () => { - console.log('[pluginChatApi] saveDraft:', draft); + console.log('[pluginChatApi][saveDraft] AFTER', { draftKey, pluginId, pageKey, text, draft }); resolve(); }); }); @@ -99,11 +112,12 @@ const pluginChatApi = { // Получить черновик async getDraft(pluginId: string, pageKey: string): Promise<{ draftText: string }> { const draftKey = `${pluginId}::${getPageKey(pageKey)}::draft`; + console.log('[pluginChatApi][getDraft] BEFORE', { draftKey, pluginId, pageKey }); return new Promise(resolve => { chrome.storage.local.get([draftKey], result => { const draft = result[draftKey]; const draftText = draft && typeof draft.text === 'string' ? draft.text : ''; - console.log('[pluginChatApi] getDraft:', draftKey, draft, 'draftText:', draftText); + console.log('[pluginChatApi][getDraft] AFTER', { draftKey, pluginId, pageKey, draft, draftText }); resolve({ draftText }); }); }); @@ -112,9 +126,10 @@ const pluginChatApi = { // Удалить черновик async deleteDraft(pluginId: string, pageKey: string): Promise<{ success: boolean }> { const draftKey = `${pluginId}::${getPageKey(pageKey)}::draft`; + console.log('[pluginChatApi][deleteDraft] BEFORE', { draftKey, pluginId, pageKey }); await new Promise(resolve => { chrome.storage.local.remove([draftKey], () => { - console.log('[pluginChatApi] deleteDraft:', draftKey); + console.log('[pluginChatApi][deleteDraft] AFTER', { draftKey, pluginId, pageKey }); resolve(); }); }); @@ -127,11 +142,13 @@ const pluginChatApi = { chrome.storage.local.get(null, result => { const drafts = Object.values(result).filter( (item: unknown): item is ChatDraft => - item && - typeof item === 'object' && - 'draftKey' in item && - 'pluginId' in item && - (item as ChatDraft).pluginId === pluginId, + !!( + item && + typeof item === 'object' && + 'draftKey' in item && + 'pluginId' in item && + (item as ChatDraft).pluginId === pluginId + ), ); console.log('[pluginChatApi] listDraftsForPlugin:', pluginId, drafts); resolve(drafts); @@ -145,11 +162,13 @@ const pluginChatApi = { chrome.storage.local.get(null, result => { const chats = Object.values(result).filter( (item: unknown): item is PluginChat => - item && - typeof item === 'object' && - 'chatKey' in item && - 'pluginId' in item && - (item as PluginChat).pluginId === pluginId, + !!( + item && + typeof item === 'object' && + 'chatKey' in item && + 'pluginId' in item && + (item as PluginChat).pluginId === pluginId + ), ); console.log('[pluginChatApi] listChatsForPlugin:', pluginId, chats); resolve(chats as PluginChat[]); diff --git a/docs/best-practices/cursor-rules-system.md b/docs/best-practices/cursor-rules-system.md deleted file mode 100644 index 1fa5657c..00000000 --- a/docs/best-practices/cursor-rules-system.md +++ /dev/null @@ -1,288 +0,0 @@ -# Cursor Rules System — Modular, AI-First, Automated - -> This is a proven, modular rule/documentation system. Copy/adapt for any project. - -This document describes the principles, structure, automation, and best practices for organizing `.cursor/rules/` in a modern, AI-friendly, and scalable way. Use this as a template to implement the same system in any project. - ---- - -## 1. Introduction - -The `.cursor/rules/` system is designed for: -- Maximum clarity and maintainability for both humans and AI assistants -- Fast navigation, search, and automation -- Seamless scaling as the number of rules grows -- Full compatibility with AI-first workflows and best practices -- Automated cross-referencing and documentation - ---- - -## 2. Core Principles & Rationale - -- **English-First**: All rules, docs, and comments are in English for maximum AI and team compatibility. (User commands may remain in the original language for clarity.) -- **One Rule — One File**: Each `.mdc` file contains exactly one rule or standard. This enables modularity, easy linking, and granular automation. -- **Topic-Based Folders**: Rules are grouped by topic (e.g., `dev-principles/`, `architecture/`, `plugin/`, `ui/`, `workflow/`, `doc/`). -- **Category Expansion**: For large projects, use more granular folders: `security/`, `workflow/`, `code-style/`, `automation/`, `memorybank/`, `dev-experience/`. -- **Explicit Metadata**: Each rule file includes `description:`, `globs:`, `alwaysApply:`, and (optionally) `related:` and `examples:`. -- **Cross-Reference Policy**: Every rule or doc should include links to all relevant files (related rules, onboarding, progress, user commands, architecture, graveyard, etc.) in a `Cross-References` section. -- **Automation-First**: Index, summary, and cross-references are auto-generated and checked by scripts for all `.md`/`.mdc` files. -- **AI-First Documentation**: All rules are written with clarity for both humans and AI, with explicit logic, rationale, and cross-links. - ---- - -## 3. Directory & File Structure - -``` -.cursor/ - rules/ - README.md - index.mdc - dev-principles/ - 01-do-no-harm.mdc - ... - architecture/ - project-structure.mdc - ... - plugin/ - structure.mdc - ... - ui/ - accessibility.mdc - ... - workflow/ - branches.mdc - ... - doc/ - ai-first.mdc - user-commands.mdc - ... - security/ - security-principles.mdc - ... - code-style/ - typescript-best-practices.mdc - ... - automation/ - automation.mdc - ... - memorybank/ - memorybank-quality.mdc - knowledge-map.mdc - ... - dev-experience/ - user-commands.mdc - ... -``` - ---- - -## 4. Rule File Format (`.mdc`) - -Each rule file must contain: -- `# Heading` — short, clear title -- **Category** (optional but recommended for large projects) -- Main rule body (bulleted or short paragraphs) -- **Rationale** — why this rule is important (AI/Dev/Team context) -- **Example** — code/config/example if relevant -- `related:` — (optional) list of related rule files -- **Cross-References** — (optional but recommended) links to onboarding, progress, user commands, architecture, graveyard, etc. -- `description:` — short summary for index/search -- `globs:` — file globs for rule application -- `alwaysApply:` — usually `false` -- `---` — end of metadata - -**Example:** -``` -# Plugin Error Handling - -**Category:** plugin - -- 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 - -## Rationale -Clear error handling ensures plugins do not break the user experience and are easy to debug for both developers and AI. - -## Example -If a plugin fails, show a user-friendly error and log the details for debugging. - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -## Cross-References -- [Onboarding](../../docs/onboarding.md) — for new developer guidance -- [Progress](../../memory-bank/progress.md) — for current project status -- [User Commands](../../docs/USER_COMMANDS.md) — for automation commands -- [Architecture](../../memory-bank/codebase-architecture.md) — for system overview - -description: Error handling requirements for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -``` - ---- - -## 5. Automation & CLI - -- **CLI Scripts** (in `.cursor/rules/`): - - `create-rule.cjs` — Interactive CLI to create a new rule file from a template. Updates index/README automatically. - - `generate-rules-index.cjs` — Scans all `.mdc` files (recursively) and regenerates `index.mdc` and the structure section in `README.md`. - - `check-rules-structure.cjs` — Validates all `.mdc` files for required sections, uniqueness, and valid related links. Used in CI. -- **Advanced CLI/Helper Scripts** (optional): - - `list` — Show structure of all rules by folder - - `search ` — Search rules by keyword - - `add ` — Create a new rule from template - - `edit ` — Edit a rule by relative path - - `generate-toc` — Recursively updates ToC, summary, and required cross-references for all `.md`/`.mdc` files - - `security-audit` — Checks Docker, .env, dependencies, outputs `audit-report.md` -- **Usage:** - - `node .cursor/rules/create-rule.cjs` - - `node .cursor/rules/generate-rules-index.cjs` - - `node .cursor/rules/check-rules-structure.cjs` - ---- - -## 6. CI Integration - -- **GitHub Actions** workflow (`.github/workflows/rules-check.yml`): - - Runs on every push/PR to `.cursor/rules/` - - Regenerates index/README and checks for uncommitted changes - - Validates all rules for structure and related links - - Fails the build if any issues are found -- **Advanced CI (optional):** - - Runs ToC/cross-reference automation, security/performance audit, and roadmap sync - -**Example:** -```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/generate-rules-index.cjs - - name: Check for uncommitted changes - run: | - git diff --exit-code || (echo 'index.mdc or README.md is outdated. Please run generate-rules-index.cjs and commit the result.' && exit 1) - - name: Check rules structure - run: node .cursor/rules/check-rules-structure.cjs -``` - ---- - -## 7. How to Add/Update Rules - -- Use `create-rule.cjs` to add new rules (enforces template and updates navigation) -- Always provide a clear heading, rationale, example, and cross-references -- Use English for all content except user commands (which may remain in the original language) -- Run `generate-rules-index.cjs` after any manual changes -- Run `check-rules-structure.cjs` to validate before commit/PR -- For advanced projects, use the CLI/ToC/cross-reference automation and developer helper scripts - ---- - -## 8. How to Port to Another Project - -1. Copy `.cursor/rules/` (with all subfolders, scripts, and templates) to the new project -2. Add the GitHub Actions workflow to `.github/workflows/` -3. Update or add rules as needed for the new context -4. Run the CLI scripts to generate index/README and validate structure -5. (Optional) Update globs and descriptions for the new codebase -6. Document any project-specific conventions in `README.md` -7. (Optional) Add advanced automation for ToC, cross-references, and roadmap sync - ---- - -## 9. Advanced Practices: Roadmap & Synchronization - -- **User-Facing Roadmap:** - - All current and planned automation, DevOps, and AI improvements are published in `docs/PLANS.md` for easy access by users and contributors. - - This file is always in sync with the technical/AI roadmap in `memory-bank/progress.md`. -- **AI/Dev Roadmap:** - - `memory-bank/progress.md` contains the full, detailed, and always up-to-date list of planned improvements, technical context, and project status for AI/LLM and the core team. -- **Synchronization Mechanism:** - - The section `## Planned Automation & Improvements` is automatically synchronized between `memory-bank/progress.md` and `docs/PLANS.md` using a script (e.g., `tools/sync_plans.py`). - - This script can be run manually or automatically in CI. - - Any update to plans in one file is reflected in the other, ensuring both user and AI/Dev audiences always see the latest roadmap. -- **Best Practice:** - - Always update plans in `memory-bank/progress.md` (the source of truth), then run the sync script or let CI handle it. - - Reference `docs/PLANS.md` in onboarding, README, and user docs for maximum transparency. - ---- - -## 10. FAQ & Troubleshooting - -- **Q: Why English?** - - For maximum AI compatibility and international team support -- **Q: Why one rule per file?** - - For modularity, easy linking, and granular automation -- **Q: What if I need a new topic?** - - Just create a new folder and add rules there -- **Q: How to avoid duplicates?** - - Use the CLI search and always check before adding -- **Q: How to update navigation?** - - Run the ToC/summary automation after any changes - ---- - -## 11. Appendix: Templates & Examples - -**Rule Template:** -``` -# [Rule Title] - -**Category:** [security|workflow|code-style|automation|memorybank|dev-experience] - -## Rule -Describe the rule clearly and concisely. - -## Rationale -Why is this rule important? (AI/Dev/Team context) - -## Example -Provide a code/config/example if relevant. - -related: - - [other-rule.mdc] - -## Cross-References -- [Related Rule 1](../security/another-rule.mdc) — similar security policy -- [Graveyard](../../memory-bank/graveyard.md) — see failed solutions -- [Onboarding](../../docs/onboarding.md) — for new developer guidance -- [Progress](../../memory-bank/progress.md) — for current project status -- [User Commands](../../docs/USER_COMMANDS.md) — for automation commands -- [Architecture](../../memory-bank/codebase-architecture.md) — for system overview - -description: [Short summary] -globs: - - [glob patterns] -alwaysApply: false ---- -``` - -**CLI Usage:** -- `node .cursor/rules/create-rule.cjs` -- `node .cursor/rules/generate-rules-index.cjs` -- `node .cursor/rules/check-rules-structure.cjs` - ---- - -**This system is proven, scalable, and AI-friendly. Copy, adapt, and use it in any project!** \ No newline at end of file diff --git a/docs/best-practices/testing-and-troubleshooting.md b/docs/best-practices/testing-and-troubleshooting.md deleted file mode 100644 index f16d62e7..00000000 --- a/docs/best-practices/testing-and-troubleshooting.md +++ /dev/null @@ -1,26 +0,0 @@ -# Testing & Troubleshooting Documentation Best Practice - -Document all testing and troubleshooting procedures in modular, cross-linked guides. This ensures rapid onboarding, effective debugging, and AI assistant support. - -## Key Practices -- Write step-by-step guides for all major testing and troubleshooting flows -- Separate usage, troubleshooting, and advanced diagnostics into distinct files -- Cross-link guides for navigation (usage → troubleshooting → advanced) -- Use clear, reproducible steps and code snippets -- Reference relevant scripts, automation, and best practices -- Update guides as features or workflows change - -## Example Guides -- [DevTools Testing Guide](../devtools-testing-guide.md) -- [DevTools Panel Troubleshooting](../devtools-panel-troubleshooting.md) -- [DevTools Panel Usage](../devtools-panel-usage.md) - -## Rationale -- Reduces time to diagnose and fix issues -- Enables AI assistants to help with debugging and onboarding -- Makes knowledge easily transferable to new projects - -## Cross-References -- [AI-First Documentation](./ai-first-documentation.md) -- [Cursor Rules System](./cursor-rules-system.md) -- [Project README](../../README.md) \ No newline at end of file diff --git a/docs/best-practices/transferability.md b/docs/best-practices/transferability.md deleted file mode 100644 index e259e290..00000000 --- a/docs/best-practices/transferability.md +++ /dev/null @@ -1,31 +0,0 @@ -# Transferability & Project Bootstrap Best Practice - -Reuse this folder and its methods to rapidly bootstrap new projects with proven, AI-friendly, automation-ready practices. - -## How to Transfer -1. Copy `docs/best-practices/` to your new project -2. Copy `.cursor/rules/` and all automation scripts (CLI, sync, CI) -3. Add or update rules, docs, and scripts for your context -4. Reference best-practices in your README and onboarding -5. Set up CI for rule validation and roadmap sync -6. Update cross-links as needed - -## Checklist -- [ ] Modular rule system in `.cursor/rules/` -- [ ] Best-practices folder in `docs/` -- [ ] CLI scripts for rule management -- [ ] Roadmap sync script and dual-file system -- [ ] CI workflow for docs/rules -- [ ] AI-first documentation everywhere -- [ ] Testing/troubleshooting guides - -## Rationale -- Enables rapid, high-quality project setup -- Ensures all team members and AI assistants have the best context -- Reduces duplication and onboarding time - -## Cross-References -- [Cursor Rules System](./cursor-rules-system.md) -- [AI-First Documentation](./ai-first-documentation.md) -- [Roadmap Synchronization](./roadmap-sync.md) -- [Project README](../../README.md) \ No newline at end of file diff --git a/docs/developer-commands.md b/docs/developer-commands.md new file mode 100644 index 00000000..497be69d --- /dev/null +++ b/docs/developer-commands.md @@ -0,0 +1,76 @@ +# Developer Commands + +Список часто используемых команд для сборки, очистки и разработки проекта. + +## Очистка и сборка + +```bash +cd /home/igor/Документы/Проекты/agent-plugins-platform +rm -rf dist +pnpm run build +``` + +- Удаляет папку dist и пересобирает проект. +- Используйте при ошибках сборки, после обновления зависимостей или для чистого билда. + +## Ещё команды + +```bash + cd /home/igor/Документы/Проекты/agent-plugins-platform + pnpm run dev +``` + +- Для разработки удалять dist не нужно + +1. Когда нужно удалять dist? +Перед pnpm run build +Удалять dist (через rm -rf dist) перед продакшен-сборкой — это хорошая практика. +Это гарантирует, что в папке не останется “мусора” от старых сборок, и финальный артефакт будет чистым и актуальным. +Перед pnpm run dev +Обычно удалять dist перед запуском dev-сервера НЕ нужно! +Dev-сервер (pnpm run dev) работает с исходниками, использует свой внутренний кэш и HMR, и не требует чистой папки dist. +Более того, некоторые dev-серверы могут использовать содержимое dist для ускорения старта (например, если там лежит что-то от предыдущей сборки). +2. Когда НЕ нужно удалять dist? +Если вы просто запускаете pnpm run dev для разработки — не удаляйте dist. +Если вы не меняли глобальные зависимости, не обновляли конфиг сборки, не делали “глубокий” рефакторинг — не нужно чистить dist для dev. +Удаление dist перед dev может даже замедлить старт, если dev-сервер что-то кэширует. +3. Когда всё-таки стоит удалить dist перед dev? +Если dev-сервер явно жалуется на “битые” артефакты в dist (редко, но бывает). +Если вы подозреваете, что dev-сервер использует старые файлы из dist и это вызывает баги. +После крупных изменений в конфиге сборки, обновления инструментов, перехода между ветками с несовместимыми артефактами. + +_Добавьте сюда другие команды, которые часто нужны для разработки, тестирования, запуска e2e и т.д._ + +Запуск chrome для тестов из Cursor: +flatpak run --branch=stable --arch=x86_64 --command=/app/bin/chrome com.google.Chrome --remote-debugging-port=9222 + +Chrome в manjaro ставится только через Flatpack, но можно установить Chromium + +chromium --remote-debugging-port=9222 + +--- + +## [Manjaro] Решение проблемы с remote debugging (9222) для Chrome/Chromium + +**Симптомы:** +- Порт 9222 не слушается при запуске Chrome Flatpak с флагом `--remote-debugging-port=9222`. +- В Chromium отсутствует бинарник в /usr/bin/, запуск из /usr/lib/chromium не работает. + +**Решение:** +1. Установить Chromium из официального репозитория: + ```bash + sudo pacman -Syu chromium + ``` +2. Запустить с remote debugging: + ```bash + chromium --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug + ``` +3. Проверить доступность: + ```bash + curl http://localhost:9222/json/version + ``` + +**Важно:** +- Flatpak Chrome не подходит для remote debugging и автоматизации — используйте только системный Chromium/Chrome. + +--- diff --git a/docs/best-practices/README.md b/docs/for-ai-best-practices/README.md similarity index 59% rename from docs/best-practices/README.md rename to docs/for-ai-best-practices/README.md index a9e0589f..6d7d5944 100644 --- a/docs/best-practices/README.md +++ b/docs/for-ai-best-practices/README.md @@ -27,4 +27,21 @@ This folder collects modular, transferable best practices, organizational method --- +## Safe Delete & Automation Safety + +- All automation scripts must use safe delete functions to prevent accidental removal of protected directories (e.g., docs/for-ai-best-practices, memory-bank, platform-core, chrome-extension/public/plugins). +- Use safe_rm in bash scripts and safeDelete in Node.js scripts (see ci-integration.md for details). +- Any attempt to delete a protected directory should be blocked and logged. +- CI workflows should notify maintainers if such an attempt is detected. + +## Cross-References +- [Project README](../../README.md) +- [Cursor Rules System](../cursor-rules-system.md) +- [Roadmap & Progress](../../memory-bank/progress.md) +- [Developer Setup](../../DEVELOPER_SETUP.md) +- [Plugin Development](../../PLUGIN_DEVELOPMENT.md) +- [CI Integration & Safe Delete](./ci-integration.md) + +--- + **Contribute new best practices as you discover them!** \ No newline at end of file diff --git a/docs/best-practices/ai-first-documentation.md b/docs/for-ai-best-practices/ai-first-documentation.md similarity index 100% rename from docs/best-practices/ai-first-documentation.md rename to docs/for-ai-best-practices/ai-first-documentation.md diff --git a/docs/best-practices/ci-integration.md b/docs/for-ai-best-practices/ci-integration.md similarity index 59% rename from docs/best-practices/ci-integration.md rename to docs/for-ai-best-practices/ci-integration.md index 08b8de7c..71005f3f 100644 --- a/docs/best-practices/ci-integration.md +++ b/docs/for-ai-best-practices/ci-integration.md @@ -39,12 +39,34 @@ jobs: run: node .cursor/rules/check-rules-structure.cjs ``` +--- + +## Safe Delete & Notification in CI/CD + +- All automation and CI scripts must protect critical directories from accidental deletion. +- Use safe_rm (bash) or safeDelete (Node.js) in all scripts that may remove files/directories. +- Maintain a list of protected directories (e.g., docs/for-ai-best-practices, memory-bank, platform-core, chrome-extension/public/plugins). +- Any attempt to delete a protected directory should: + - Be blocked + - Log a warning (locally or in CI) + - Optionally, send a notification (e.g., GitHub Actions annotation, Slack, email) + +### Example (GitHub Actions warning) +```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)'; then + echo '::warning::Attempted deletion of a protected directory! Review required.' + fi +``` + ## Rationale -- Ensures documentation and rules are always up-to-date and valid -- Prevents human error and outdated navigation -- Enables rapid onboarding and AI context +- Prevents accidental or malicious loss of critical project data +- Ensures all destructive actions are intentional and reviewed +- Increases trust in automation and onboarding safety ## Cross-References - [Cursor Rules System](./cursor-rules-system.md) +- [Safe Delete Best Practice](./README.md) - [Roadmap Synchronization](./roadmap-sync.md) - [Project README](../../README.md) \ No newline at end of file diff --git a/docs/best-practices/development-principles.md b/docs/for-ai-best-practices/development-principles.md similarity index 100% rename from docs/best-practices/development-principles.md rename to docs/for-ai-best-practices/development-principles.md diff --git a/docs/best-practices/roadmap-sync.md b/docs/for-ai-best-practices/roadmap-sync.md similarity index 100% rename from docs/best-practices/roadmap-sync.md rename to docs/for-ai-best-practices/roadmap-sync.md diff --git a/docs/for-ai-best-practices/testing-and-troubleshooting.md b/docs/for-ai-best-practices/testing-and-troubleshooting.md new file mode 100644 index 00000000..4b1aa3d0 --- /dev/null +++ b/docs/for-ai-best-practices/testing-and-troubleshooting.md @@ -0,0 +1,63 @@ +# Testing & Troubleshooting Documentation Best Practice + +Document all testing and troubleshooting procedures in modular, cross-linked guides. This ensures rapid onboarding, effective debugging, and AI assistant support. + +## Key Practices +- Write step-by-step guides for all major testing and troubleshooting flows +- Separate usage, troubleshooting, and advanced diagnostics into distinct files +- Cross-link guides for navigation (usage → troubleshooting → advanced) +- Use clear, reproducible steps and code snippets +- Reference relevant scripts, automation, and best practices +- Update guides as features or workflows change + +## Example Guides +- [DevTools Testing Guide](../devtools-testing-guide.md) +- [DevTools Panel Troubleshooting](../devtools-panel-troubleshooting.md) +- [DevTools Panel Usage](../devtools-panel-usage.md) + +## Rationale +- Reduces time to diagnose and fix issues +- Enables AI assistants to help with debugging and onboarding +- Makes knowledge easily transferable to new projects + +## Cross-References +- [AI-First Documentation](./ai-first-documentation.md) +- [Cursor Rules System](./cursor-rules-system.md) +- [Project README](../../README.md) + +--- + +## How to Analyze External Repositories as Submodules + +To analyze, search, and track changes in an external git repository (e.g., for studying Chrome extension patterns or sidepanel-background communication), add it as a git submodule: + +### Step-by-step: +1. In your monorepo root, run: + ```bash + git submodule add external/ + git submodule update --init --recursive + ``` + Example: + ```bash + git submodule add https://github.com/QizhengMo/chrome-extension-sidepanel-template.git external/sidepanel-template + git submodule update --init --recursive + ``` +2. Open the monorepo in Cursor/VSCode. The submodule will be indexed and available for code search, navigation, and analysis. +3. To update the submodule to the latest version: + ```bash + cd external/sidepanel-template + git pull origin main + ``` +4. To remove the submodule (if no longer needed): + ```bash + git submodule deinit -f external/sidepanel-template + git rm -f external/sidepanel-template + rm -rf .git/modules/external/sidepanel-template + ``` + +**Benefits:** +- Keeps external code isolated from your main git history +- Allows easy updates and independent versioning +- Enables full code search and analysis in Cursor/VSCode + +**Use this method for any external codebase you want to analyze or reference in your monorepo.** \ No newline at end of file diff --git a/docs/for-ai-best-practices/transferability.md b/docs/for-ai-best-practices/transferability.md new file mode 100644 index 00000000..14f0292b --- /dev/null +++ b/docs/for-ai-best-practices/transferability.md @@ -0,0 +1,5 @@ + + +# Перенесено + +Инструкция теперь находится в файле [../transferability.ru.md](../transferability.ru.md) (на русском языке, для разработчиков). \ No newline at end of file diff --git a/docs/transferability.ru.md b/docs/transferability.ru.md new file mode 100644 index 00000000..05f930a6 --- /dev/null +++ b/docs/transferability.ru.md @@ -0,0 +1,100 @@ +# Инструкция по переносу окружения и best practices для разработчиков + +--- +Основной репозиторий проекта: https://github.com/LebedevIV/agent-plugins-platform-boilerplate +--- + +## 1. Клонирование сторонних репозиториев как submodule + +**Зачем:** +- Позволяет анализировать внешний код в рамках одного монорепозитория, не смешивая git-истории. +- Упрощает навигацию, сравнение, обновление шаблонов и best practices. +- Позволяет Cursor и другим инструментам индексировать весь код для поиска и AI-анализа. + +**Как добавить submodule:** + +```bash +# Клонировать внешний репозиторий как submodule в папку external/имя: +git submodule add external/<имя> +git submodule update --init --recursive +``` + +**Как обновить submodule:** + +```bash +cd external/<имя> +git fetch +git checkout +cd ../.. +git add external/<имя> +git commit -m "update submodule <имя>" +``` + +**Как удалить submodule:** + +```bash +git submodule deinit -f external/<имя> +git rm -f external/<имя> +rm -rf .git/modules/external/<имя> +``` + +**Возможности:** +- Быстрый доступ к коду для анализа, сравнения, копирования решений. +- Возможность отслеживать upstream-изменения и обновлять шаблоны. +- Использование внешних best practices без смешивания с основной историей проекта. + +--- + +## 2. Рекомендуемый перечень данных для базы знаний Cursor/AI + +**Зачем:** +- Позволяет быстро восстановить или перенести рабочее окружение. +- Гарантирует, что AI-ассистент и команда будут учитывать все важные нюансы проекта. +- Упрощает onboarding новых участников и автоматизацию. + +**Что рекомендуется сохранять:** + +- **Операционная система:** + - Пример: Manjaro Linux (Plasma), macOS, Windows 11 +- **Версия браузера:** + - Пример: Chrome 138.0.7204.100 (Flatpak) +- **IDE и инструменты:** + - Пример: Cursor, VSCode, WebStorm + - Установленные расширения Cursor (если есть) +- **Связанные репозитории:** + - Основной проект, внешние submodules, шаблоны, тестовые репозитории +- **Наличие git CLI:** + - Версия git, особенности конфигурации (user.name, user.email, hooks) +- **Правила языка и коммуникации:** + - Пример: Всегда отвечать на русском языке + - Язык документации (например, английский для config) +- **Правила git-ветвления:** + - develop — основная ветка разработки + - feature/… — новые фичи + - fix/… — багфиксы + - merge только через pull request +- **Стандарты и best practices:** + - ESM-only для внутренних пакетов + - Стандарты для package.json (main, exports) + - Использование chrome.storage.sync вместо localStorage + - React 19+, Vite 6+, @vitejs/plugin-react-swc + - Проверка единственной версии react/react-dom +- **Инструменты для отладки:** + - Debugger for Chrome (Flatpak, remote-debugging-port) + - Composer Web + - Рекомендации по запуску и инспекции расширения +- **Особенности окружения:** + - Как запускать Chrome с нужными флагами + - Как инспектировать боковую панель/вкладку расширения +- **AI-правила:** + - Всегда проявлять инициативу, предлагать улучшения, критиковать конструктивно + - Сохранять все важные правила и environment в AI memory-bank + +**Пример запроса для сохранения в базе знаний:** + +> Сохрани в базе знаний: Manjaro Linux (Plasma), Chrome 138 (Flatpak), Cursor, английский для config, всегда отвечать на русском, develop/feature/fix-ветки, ESM-only, Debugger for Chrome, Composer Web, инспекция sidepanel через ПКМ, инициативность ассистента. + +--- + +**Рекомендация:** +- После переустановки IDE или смены окружения — первым делом восстановить эти данные в базе знаний AI/assistent, чтобы не потерять контекст и правила работы. \ No newline at end of file diff --git a/external/chrome-extension-boilerplate-react-vite b/external/chrome-extension-boilerplate-react-vite new file mode 160000 index 00000000..2087bc9c --- /dev/null +++ b/external/chrome-extension-boilerplate-react-vite @@ -0,0 +1 @@ +Subproject commit 2087bc9ce162cf84120588a6189b2b53437c43b6 diff --git a/external/sidepanel-template b/external/sidepanel-template new file mode 160000 index 00000000..0fd7acde --- /dev/null +++ b/external/sidepanel-template @@ -0,0 +1 @@ +Subproject commit 0fd7acdebf993cfc2fb78a8614619a839acfd9da diff --git a/memory-bank/errors.md b/memory-bank/errors.md index 45c1b93d..86b3d3af 100644 --- a/memory-bank/errors.md +++ b/memory-bank/errors.md @@ -142,4 +142,37 @@ const getCurrentUrl = async () => { ## Другие ошибки -*Здесь будут добавляться другие решенные проблемы* \ No newline at end of file +*Здесь будут добавляться другие решенные проблемы* + +--- + +### [Manjaro] Remote debugging (9222) не работает в Chrome Flatpak и решается установкой Chromium из репозитория + +**Проблема:** +- При запуске Chrome через Flatpak с флагом `--remote-debugging-port=9222` порт не слушается, DevTools протокол недоступен (`curl http://localhost:9222/json/version` не отвечает). +- Аналогично, если Chromium не установлен как системный пакет, а только присутствует в /usr/lib/chromium (нет бинарника в /usr/bin/), remote debugging не работает. + +**Причина:** +- Flatpak изолирует сетевые порты, sandbox не позволяет пробросить 9222 наружу. +- В Manjaro/Arch бинарник Chromium может отсутствовать или быть неисполняемым, если пакет установлен не полностью. + +**Решение:** +1. Установить Chromium из официального репозитория: + ```bash + sudo pacman -Syu chromium + ``` +2. Запустить с remote debugging и отдельным профилем: + ```bash + chromium --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug + ``` +3. Проверить, что порт слушается: + ```bash + ss -ltnp | grep 9222 + curl http://localhost:9222/json/version + ``` +4. После этого DevTools протокол доступен, можно использовать Composer Web, Puppeteer, VSCode DevTools и др. + +**Примечание:** +- Для Flatpak Chrome remote debugging практически всегда не работает из-за sandbox. Используйте только системный Chromium/Chrome для автоматизации и интеграции. + +--- \ No newline at end of file diff --git a/package.json b/package.json index 98a2db69..02c4966f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.148", + "version": "1.0.153", "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 bf8e5674..bf523f60 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/env/package.json b/packages/env/package.json index 386af9ca..cd05170e 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/hmr/package.json b/packages/hmr/package.json index 5e587f17..e76de434 100644 --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 9e386d84..5a42e884 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json index eae4473e..197930ad 100644 --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/shared/package.json b/packages/shared/package.json index 4b2d616f..b49119a9 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/storage/package.json b/packages/storage/package.json index 5e22ee8c..bd3e3896 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json index 6f102563..ae19e7e4 100644 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 7a2c7d64..c177a739 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/package.json b/packages/ui/package.json index 3a516eb2..434469ca 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - ui components", "type": "module", "private": true, diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json index 8253ada6..bbedecc1 100644 --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.172", + "version": "0.5.177", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/zipper/package.json b/packages/zipper/package.json index 3bb5d952..1a71d676 100644 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - zipper", "type": "module", "private": true, diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json index f50157b0..7451e470 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.164", + "version": "0.5.169", "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 4a7d6737..310043f2 100644 --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content/package.json b/pages/content/package.json index b47186b1..cb290cf4 100644 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/devtools-panel/package.json b/pages/devtools-panel/package.json index 6c08dce2..22c15214 100644 --- a/pages/devtools-panel/package.json +++ b/pages/devtools-panel/package.json @@ -1,6 +1,6 @@ { "name": "Agent Platform Tools", - "version": "0.5.164", + "version": "0.5.169", "description": "Agent Plugins Platform: инструменты и логи для отладки плагинов.", "type": "module", "private": true, diff --git a/pages/devtools/package.json b/pages/devtools/package.json index 5e033d4b..769ea6c7 100644 --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json index 74c18818..44d293a8 100644 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/options/package.json b/pages/options/package.json index 6c4e8710..9e9fcb45 100644 --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/popup/package.json b/pages/popup/package.json index 947e068c..f850b687 100644 --- a/pages/popup/package.json +++ b/pages/popup/package.json @@ -1,6 +1,6 @@ { "name": "@extension/popup", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - popup", "type": "module", "private": true, diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json index f3950075..ab7a83eb 100644 --- a/pages/side-panel/package.json +++ b/pages/side-panel/package.json @@ -1,6 +1,6 @@ { "name": "@extension/sidepanel", - "version": "0.5.164", + "version": "0.5.169", "description": "chrome extension - side panel", "type": "module", "private": true, diff --git a/pages/side-panel/src/components/PluginControlPanel.tsx b/pages/side-panel/src/components/PluginControlPanel.tsx index 85107303..dde8e672 100644 --- a/pages/side-panel/src/components/PluginControlPanel.tsx +++ b/pages/side-panel/src/components/PluginControlPanel.tsx @@ -81,37 +81,41 @@ export const PluginControlPanel: React.FC = ({ setLoading(true); setError(null); try { + console.log('[PluginControlPanel] loadChat запрос:', { pluginId, pageKey }); const chat = await chrome.runtime.sendMessage({ type: 'GET_PLUGIN_CHAT', pluginId, pageKey, }); + console.log('[PluginControlPanel] chat из background:', { + pluginId, + pageKey, + chat, + typeofChat: typeof chat, + chatKeys: chat ? Object.keys(chat) : undefined, + messages: chat?.messages, + isArray: Array.isArray(chat?.messages), + messagesLength: chat?.messages?.length, + }); console.log( '[PluginControlPanel] loadChat RAW:', chat, typeof chat, chat && chat.messages, Array.isArray(chat?.messages), + Object.keys(chat || {}), + chat?.messages && chat?.messages.length, + chat?.messages && chat?.messages[0], ); - if (chat && Array.isArray(chat.messages)) { - setMessages( - chat.messages.map( - (msg: { - id?: string; - content?: string; - text?: string; - role?: string; - isUser?: boolean; - timestamp?: number; - }) => ({ - id: msg.id || String(msg.timestamp || Date.now()), - text: msg.content || msg.text, - isUser: msg.role ? msg.role === 'user' : !!msg.isUser, - timestamp: msg.timestamp || Date.now(), - }), - ), - ); - console.log('[PluginControlPanel] setMessages:', chat.messages); + const messages = chat?.messages || chat?.chat?.messages; + if (Array.isArray(messages)) { + console.log('[PluginControlPanel] messages для setMessages:', messages); + setMessages(messages); + } else if (chat && chat.error) { + console.error('[PluginControlPanel] Ошибка получения чата:', chat.error, chat.details); + alert('Ошибка получения чата: ' + chat.error + (chat.details ? '\n' + chat.details : '')); + setMessages([]); + return; } else { setMessages([]); console.log('[PluginControlPanel] setMessages: []'); @@ -178,15 +182,33 @@ export const PluginControlPanel: React.FC = ({ } }, [currentView, loadDraft]); + // Логирование каждого рендера и ключевых параметров + useEffect(() => { + console.log('[PluginControlPanel] === РЕНДЕР ===', { + pluginId, + pageKey, + draftText, + message, + currentView, + isRunning, + isPaused, + }); + }); + // --- Синхронизация message с draftText после загрузки черновика --- useEffect(() => { - // Подставлять draftText только если message пустое (т.е. при инициализации) - if (typeof message === 'string' && message === '' && draftText) { + // Если draftText пустой, подставляем автотестовый текст + if (typeof draftText === 'string' && draftText === '') { + setMessage('Тестовое сообщение для диагностики'); + console.log('[PluginControlPanel] draftText пустой, подставлен автотестовый текст'); + } else if (typeof draftText === 'string') { setMessage(draftText); + console.log('[PluginControlPanel] draftText подставлен в поле ввода:', draftText); } - }, [draftText]); + }, [draftText, setMessage]); const handleSendMessage = async (): Promise => { + console.log('[PluginControlPanel] handleSendMessage: попытка отправки', { message }); if (!message.trim()) return; const newMessage: ChatMessage = { id: Date.now().toString(), @@ -195,7 +217,6 @@ export const PluginControlPanel: React.FC = ({ timestamp: Date.now(), }; setMessage(''); // Очищаем сообщение через хук - // Удалить все вызовы setSyncStatus(...) try { await chrome.runtime.sendMessage({ type: 'SAVE_PLUGIN_CHAT_MESSAGE', @@ -207,12 +228,12 @@ export const PluginControlPanel: React.FC = ({ timestamp: newMessage.timestamp, }, }); - // Удалить все вызовы setSyncStatus(...) + console.log('[PluginControlPanel] handleSendMessage: сообщение отправлено', newMessage); await loadChat(); // Перезагружаем историю чата после отправки await clearDraft(); // Сбрасываем черновик после отправки - } catch { - // Удалить все вызовы setSyncStatus(...) + } catch (e) { setError('Ошибка сохранения сообщения'); + console.error('[PluginControlPanel] handleSendMessage: ошибка отправки', e); } }; @@ -256,6 +277,10 @@ export const PluginControlPanel: React.FC = ({ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); + useEffect(() => { + console.log('[PluginControlPanel] messages после setMessages:', messages); + }, [messages]); + // Фокус на поле ввода при открытии чата useEffect(() => { if (currentView === 'chat') { @@ -265,7 +290,7 @@ export const PluginControlPanel: React.FC = ({ const handleTextareaChange = (event: React.ChangeEvent): void => { setMessage(event.target.value); // Используем хук вместо setMessage - + console.log('[PluginControlPanel] handleTextareaChange: новое значение', event.target.value); // Автоматическое изменение высоты const textarea = event.target; textarea.style.height = 'auto'; @@ -351,12 +376,17 @@ export const PluginControlPanel: React.FC = ({
{loading &&

Загрузка сообщений...

} {error &&

{error}

} - {messages.map(msg => ( -
-

{msg.text}

- {new Date(msg.timestamp).toLocaleTimeString()} -
- ))} + {/* Диагностический вывод сообщений */} + {messages.map((msg, idx) => { + console.log('[PluginControlPanel] render message:', idx, msg); + return ( +
+ {JSON.stringify(msg)} +
+ ); + })} {messagesEndRef.current &&
}
diff --git a/pages/side-panel/src/hooks/useLazyChatSync.ts b/pages/side-panel/src/hooks/useLazyChatSync.ts index e7ddc36f..0501456f 100644 --- a/pages/side-panel/src/hooks/useLazyChatSync.ts +++ b/pages/side-panel/src/hooks/useLazyChatSync.ts @@ -14,6 +14,7 @@ interface UseLazyChatSyncReturn { draftError: string | null; loadDraft: () => Promise; clearDraft: () => Promise; + draftText: string; } export const useLazyChatSync = ({ @@ -25,6 +26,7 @@ export const useLazyChatSync = ({ const [isDraftSaved, setIsDraftSaved] = useState(false); const [isDraftLoading, setIsDraftLoading] = useState(false); const [draftError, setDraftError] = useState(null); + const [draftText, setDraftText] = useState(''); const debounceRef = useRef(null); const lastSavedText = useRef(''); @@ -33,7 +35,7 @@ export const useLazyChatSync = ({ const saveDraft = useCallback( async (text: string) => { if (text === lastSavedText.current) return; // Не сохраняем, если текст не изменился - + console.log('[useLazyChatSync] saveDraft: попытка сохранить draft', { pluginId, pageKey, text }); try { await chrome.runtime.sendMessage({ type: 'SAVE_PLUGIN_CHAT_DRAFT', @@ -44,8 +46,10 @@ export const useLazyChatSync = ({ lastSavedText.current = text; setIsDraftSaved(true); setDraftError(null); + setDraftText(text); + console.log('[useLazyChatSync] saveDraft: успешно сохранено', { pluginId, pageKey, text }); } catch (error) { - console.error('Error saving draft:', error); + console.error('[useLazyChatSync] Error saving draft:', error); setDraftError('Ошибка сохранения черновика'); setIsDraftSaved(false); } @@ -57,25 +61,28 @@ export const useLazyChatSync = ({ const loadDraft = useCallback(async () => { setIsDraftLoading(true); setDraftError(null); - + console.log('[useLazyChatSync] loadDraft: загружаем draft', { pluginId, pageKey }); try { const response = await chrome.runtime.sendMessage({ type: 'GET_PLUGIN_CHAT_DRAFT', pluginId, pageKey, }); - if (response?.draftText) { setMessageState(response.draftText); lastSavedText.current = response.draftText; setIsDraftSaved(true); + setDraftText(response.draftText); + console.log('[useLazyChatSync] loadDraft: найден draft', { pluginId, pageKey, draft: response.draftText }); } else { - setMessageState(''); // Явно очищаем если драфта нет + setMessageState(''); lastSavedText.current = ''; setIsDraftSaved(false); + setDraftText(''); + console.log('[useLazyChatSync] loadDraft: draft не найден', { pluginId, pageKey }); } } catch (error) { - console.error('Error loading draft:', error); + console.error('[useLazyChatSync] Error loading draft:', error); setDraftError('Ошибка загрузки черновика'); } finally { setIsDraftLoading(false); @@ -84,6 +91,7 @@ export const useLazyChatSync = ({ // Функция для очистки черновика const clearDraft = useCallback(async () => { + console.log('[useLazyChatSync] clearDraft: очищаем draft', { pluginId, pageKey }); try { await chrome.runtime.sendMessage({ type: 'SAVE_PLUGIN_CHAT_DRAFT', @@ -94,8 +102,11 @@ export const useLazyChatSync = ({ lastSavedText.current = ''; setIsDraftSaved(false); setDraftError(null); + setDraftText(''); + setMessageState(''); + console.log('[useLazyChatSync] clearDraft: успешно очищено', { pluginId, pageKey }); } catch (error) { - console.error('Error clearing draft:', error); + console.error('[useLazyChatSync] Error clearing draft:', error); setDraftError('Ошибка очистки черновика'); } }, [pluginId, pageKey]); @@ -110,13 +121,13 @@ export const useLazyChatSync = ({ if (text.length === 0) { clearDraft(); } else { - // Сохраняем драфт при любом непустом изменении debounceRef.current = setTimeout(() => { saveDraft(text); }, debounceMs); } + console.log('[useLazyChatSync] setMessage: новое значение', { pluginId, pageKey, text }); }, - [debounceMs, saveDraft, clearDraft], + [debounceMs, saveDraft, clearDraft, pluginId, pageKey], ); // Очистка таймера при размонтировании @@ -134,21 +145,19 @@ export const useLazyChatSync = ({ loadDraft(); }, [loadDraft]); - // Сброс состояния при изменении pageKey (смена страницы) + // При смене pageKey всегда загружаем draft, не сбрасываем message вручную useEffect(() => { console.log('[useLazyChatSync] pageKey изменился:', pageKey); - // setMessageState(''); // УБРАТЬ сброс message setIsDraftSaved(false); setIsDraftLoading(false); setDraftError(null); lastSavedText.current = ''; - - // Очищаем таймер при смене страницы if (debounceRef.current) { clearTimeout(debounceRef.current); debounceRef.current = null; } - }, [pageKey]); + loadDraft(); + }, [pageKey, loadDraft]); return { message, @@ -158,5 +167,6 @@ export const useLazyChatSync = ({ draftError, loadDraft, clearDraft, + draftText, }; }; diff --git a/platform-core/public/pyodide/package.json b/platform-core/public/pyodide/package.json index 06a7fab2..1fc4db17 100644 --- a/platform-core/public/pyodide/package.json +++ b/platform-core/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.171", + "version": "0.27.176", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/public/pyodide/package.json b/public/pyodide/package.json index 06a7fab2..1fc4db17 100644 --- a/public/pyodide/package.json +++ b/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.171", + "version": "0.27.176", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/tests/e2e/helpers/theme.ts b/tests/e2e/helpers/theme.ts index 9dfd6c15..7fdbda8e 100644 --- a/tests/e2e/helpers/theme.ts +++ b/tests/e2e/helpers/theme.ts @@ -4,10 +4,10 @@ export const canSwitchTheme = async () => { const LIGHT_THEME_CLASS = 'bg-slate-50'; const DARK_THEME_CLASS = 'bg-gray-800'; - const TOGGLE_BUTTON_TEXT = 'Toggle theme'; const app = await $('.App').getElement(); - const toggleThemeButton = await $(`button=${TOGGLE_BUTTON_TEXT}`).getElement(); + // Ищем по классу, а не по тексту + const toggleThemeButton = await $('.theme-toggle-btn').getElement(); await expect(app).toBeExisting(); await expect(toggleThemeButton).toBeExisting(); diff --git a/tests/e2e/package.json b/tests/e2e/package.json index a900a6a6..0118e765 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@extension/e2e", - "version": "0.5.164", + "version": "0.5.169", "description": "E2e tests configuration boilerplate", "private": true, "sideEffects": false, diff --git a/tests/e2e/specs/page-side-panel.test.ts b/tests/e2e/specs/page-side-panel.test.ts index abbbb1d3..079ea608 100644 --- a/tests/e2e/specs/page-side-panel.test.ts +++ b/tests/e2e/specs/page-side-panel.test.ts @@ -1,4 +1,7 @@ -import { canSwitchTheme } from '../helpers/theme.js'; +import { canSwitchTheme } from '../helpers/theme.ts'; +import fs from 'fs'; + +const OZON_URL = 'https://www.ozon.ru/product/kostyum-sportivnyy-north-dot-1438414833/'; describe('Webextension Side Panel', () => { it('should make side panel accessible', async () => { @@ -9,4 +12,90 @@ describe('Webextension Side Panel', () => { await expect(browser).toHaveTitle('Side Panel'); await canSwitchTheme(); }); + + it('should open ozon, open sidepanel, switch to chat, send message and read logs', async () => { + // 1. Открываем страницу Ozon + await browser.url(OZON_URL); + await browser.pause(2000); // ждём полной загрузки + + // 2. Открываем sidepanel (если есть кнопка/иконка — кликнуть, иначе напрямую) + const extensionPath = await browser.getExtensionPath(); + const sidePanelUrl = `${extensionPath}/side-panel/index.html`; + await browser.url(sidePanelUrl); + await browser.waitUntil(async () => (await browser.getTitle()) === 'Side Panel', { + timeout: 5000, + timeoutMsg: 'Sidepanel не загрузился', + }); + await expect(browser).toHaveTitle('Side Panel'); + + // 3. Переключаемся на вкладку "Чат" (замените селектор на ваш) + const chatTab = await $('[data-testid="chat-tab"]').getElement(); + await chatTab.click(); + + // 4. Читаем логи sidepanel и background + await browser.sessionSubscribe({ events: ['log.entryAdded'] }); + const logs = []; + const backgroundLogs = []; + browser.on('log.entryAdded', logEntry => { + logs.push(logEntry.text); + // Фильтруем логи background по ключевым словам или по структуре logEntry + if (logEntry.text.includes('[background]')) { + backgroundLogs.push(logEntry.text); + } + console.log('[Sidepanel/Background]', logEntry.text); + }); + + // 5. Вводим и отправляем сообщение + const chatInput = await $('[data-testid="chat-input"]').getElement(); + await chatInput.setValue('Тестовое сообщение из e2e'); + const sendBtn = await $('[data-testid="chat-send"]').getElement(); + await sendBtn.click(); + + // 6. Проверяем, что сообщение появилось, иначе собираем дампы для диагностики + let chatAppeared = false; + let errorMsg = ''; + try { + await browser.waitUntil( + async () => { + const messages = await $$('[data-testid="chat-message"]'); + return messages.some(async m => (await m.getText()).includes('Тестовое сообщение из e2e')); + }, + { timeout: 5000, timeoutMsg: 'Сообщение не появилось в чате' }, + ); + chatAppeared = true; + } catch (err) { + errorMsg = err.message || String(err); + } + + if (!chatAppeared) { + // Сохраняем дамп DOM sidepanel + const domDump = await browser.execute(() => document.documentElement.outerHTML); + fs.writeFileSync('sidepanel_dom_dump.html', domDump); + + // Сохраняем скриншот sidepanel + await browser.saveScreenshot('sidepanel_error.png'); + + // Сохраняем последние логи sidepanel + fs.writeFileSync('sidepanel_logs.txt', logs.join('\n')); + // Сохраняем логи background + fs.writeFileSync('background_logs.txt', backgroundLogs.join('\n')); + + // Пробуем получить состояние чата (input, send, tab) + const chatInputVal = await $('[data-testid="chat-input"]') + .getValue() + .catch(() => 'input not found'); + const chatTabExists = await $('[data-testid="chat-tab"]') + .isExisting() + .catch(() => false); + const chatMessagesCount = (await $$('[data-testid="chat-message"]')).length; + fs.writeFileSync( + 'sidepanel_chat_state.txt', + `input: ${chatInputVal}\ntab: ${chatTabExists}\nmessages: ${chatMessagesCount}`, + ); + + throw new Error( + `Сообщение не появилось в чате.\n${errorMsg}\nСм. sidepanel_dom_dump.html, sidepanel_error.png, sidepanel_logs.txt, background_logs.txt, sidepanel_chat_state.txt для диагностики.`, + ); + } + }); });