From 74625d55e3c7c52881d296b289f9c55438762732 Mon Sep 17 00:00:00 2001 From: Ivan Fontosh Date: Fri, 24 Apr 2026 07:36:40 +0800 Subject: [PATCH] Initial commit: workspace docs and Cursor config Made-with: Cursor --- .cursor/agents/frontend-senior.md | 36 +++++++ .cursor/agents/reviewer.md | 34 ++++++ .cursor/agents/unit-tests.md | 37 +++++++ .cursor/hooks.json | 10 ++ .cursor/hooks/final-verify.cjs | 94 +++++++++++++++++ .cursor/hooks/hooks.json | 10 ++ .cursor/pipeline-state.json | 6 ++ .cursor/pr-checklists/ui-mock-alignment.md | 114 +++++++++++++++++++++ .cursor/rules/project.mdc | 50 +++++++++ .cursor/skills/feature-pipeline/SKILL.md | 83 +++++++++++++++ WORKSPACE.md | 45 ++++++++ 11 files changed, 519 insertions(+) create mode 100644 .cursor/agents/frontend-senior.md create mode 100644 .cursor/agents/reviewer.md create mode 100644 .cursor/agents/unit-tests.md create mode 100644 .cursor/hooks.json create mode 100644 .cursor/hooks/final-verify.cjs create mode 100644 .cursor/hooks/hooks.json create mode 100644 .cursor/pipeline-state.json create mode 100644 .cursor/pr-checklists/ui-mock-alignment.md create mode 100644 .cursor/rules/project.mdc create mode 100644 .cursor/skills/feature-pipeline/SKILL.md create mode 100644 WORKSPACE.md diff --git a/.cursor/agents/frontend-senior.md b/.cursor/agents/frontend-senior.md new file mode 100644 index 0000000..b13d523 --- /dev/null +++ b/.cursor/agents/frontend-senior.md @@ -0,0 +1,36 @@ +--- +name: frontend-senior +description: Senior frontend engineer +model: auto +tools: all +--- + +Ты senior frontend engineer. + +Задача: + +- реализовать feature или fix +- писать production-quality React + TypeScript код + +Правила: + +- сначала изучи nearby components (обычно в репозитории **`dnd_player/`**) +- следуй существующим patterns +- не делай лишних изменений +- избегай overengineering +- используй composition + +UI: + +- учитывай loading / error / empty / disabled +- соблюдай accessibility + +Не делай: + +- large refactors без запроса +- новые dependencies без причины + +Output: + +- список изменённых файлов +- краткое описание изменений diff --git a/.cursor/agents/reviewer.md b/.cursor/agents/reviewer.md new file mode 100644 index 0000000..66ce02c --- /dev/null +++ b/.cursor/agents/reviewer.md @@ -0,0 +1,34 @@ +--- +name: reviewer +description: Strict code reviewer +model: auto +tools: all +--- + +Ты строгий reviewer. + +Цель: + +- найти проблемы в изменениях + +Проверяй: + +- correctness +- regressions +- type safety +- accessibility +- performance +- edge cases +- missing tests + +Формат: + +- Severity: high / medium / low +- Problem +- Why +- Fix + +Правила: + +- не переписывай код без причины +- предлагай minimal fixes diff --git a/.cursor/agents/unit-tests.md b/.cursor/agents/unit-tests.md new file mode 100644 index 0000000..aba55c6 --- /dev/null +++ b/.cursor/agents/unit-tests.md @@ -0,0 +1,37 @@ +--- +name: unit-tests +description: Unit test specialist +model: auto +tools: all +--- + +Ты unit-test specialist. + +Задача: + +- добавить/обновить tests +- добиться green status + +Подход: + +- test behavior, not implementation +- follow existing patterns +- избегай flaky tests + +Покрытие: + +- happy path +- edge case +- error case + +Workflow: + +- команды npm выполняй из каталога **`dnd_player/`** +- сначала run relevant tests: + `npm run test -- ` +- затем при необходимости весь suite + +Output: + +- какие тесты добавлены +- что они проверяют diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 0000000..7b029f8 --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "hooks": { + "stop": [ + { + "command": "node .cursor/hooks/final-verify.cjs" + } + ] + } +} diff --git a/.cursor/hooks/final-verify.cjs b/.cursor/hooks/final-verify.cjs new file mode 100644 index 0000000..4a48f47 --- /dev/null +++ b/.cursor/hooks/final-verify.cjs @@ -0,0 +1,94 @@ +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +const statePath = path.join(process.cwd(), ".cursor", "pipeline-state.json"); + +function readState() { + if (!fs.existsSync(statePath)) { + return { + implementation: "pending", + review: "pending", + tests: "pending", + }; + } + return JSON.parse(fs.readFileSync(statePath, "utf8")); +} + +function fail(msg) { + process.stdout.write(JSON.stringify({ followup_message: msg })); + process.exit(0); +} + +function getDndPlayerRoot() { + if (process.env.DND_PLAYER_ROOT) { + const r = path.resolve(process.env.DND_PLAYER_ROOT); + if (fs.existsSync(path.join(r, "package.json"))) return r; + } + const cwd = process.cwd(); + const candidates = [ + path.join(cwd, "..", "dnd_player"), + path.join(cwd, "dnd_player"), + cwd, + ]; + for (const root of candidates) { + const pkgPath = path.join(root, "package.json"); + if (!fs.existsSync(pkgPath)) continue; + try { + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); + if ( + pkg.scripts?.lint && + pkg.scripts?.typecheck && + pkg.scripts?.test + ) { + return root; + } + } catch { + continue; + } + } + return null; +} + +const state = readState(); + +if (state.implementation !== "done") { + fail("Run frontend-senior stage"); +} + +if (state.review !== "done") { + fail("Run reviewer stage"); +} + +if (state.tests !== "done") { + fail("Run unit-tests stage"); +} + +const dndPlayerRoot = getDndPlayerRoot(); +if (!dndPlayerRoot) { + fail( + "Cannot find dnd_player (expected sibling ../dnd_player or env DND_PLAYER_ROOT)", + ); +} + +const opts = { stdio: "pipe", cwd: dndPlayerRoot }; + +try { + execSync("npm run lint", opts); +} catch { + fail("Lint failed (run from dnd_player root)"); +} + +try { + execSync("npm run typecheck", opts); +} catch { + fail("Typecheck failed (run from dnd_player root)"); +} + +try { + execSync("npm run test", opts); +} catch { + fail("Tests failed (run from dnd_player root)"); +} + +process.exit(0); diff --git a/.cursor/hooks/hooks.json b/.cursor/hooks/hooks.json new file mode 100644 index 0000000..7b029f8 --- /dev/null +++ b/.cursor/hooks/hooks.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "hooks": { + "stop": [ + { + "command": "node .cursor/hooks/final-verify.cjs" + } + ] + } +} diff --git a/.cursor/pipeline-state.json b/.cursor/pipeline-state.json new file mode 100644 index 0000000..f9d3ed9 --- /dev/null +++ b/.cursor/pipeline-state.json @@ -0,0 +1,6 @@ +{ + "implementation": "done", + "review": "done", + "tests": "done", + "verify": "done" +} diff --git a/.cursor/pr-checklists/ui-mock-alignment.md b/.cursor/pr-checklists/ui-mock-alignment.md new file mode 100644 index 0000000..b726e31 --- /dev/null +++ b/.cursor/pr-checklists/ui-mock-alignment.md @@ -0,0 +1,114 @@ +# PR: UI — выравнивание под макеты (редактор + пульт) + +**База путей:** репозиторий **`dnd_player/`** (не каталог `cursorAi`). + +Связь с **feature-pipeline** (`.cursor/skills/feature-pipeline/SKILL.md`): критерии ниже сгруппированы по стадиям; после merge обновляют `.cursor/pipeline-state.json` по мере прохождения стадий. + +--- + +## Мета PR + +| Поле | Значение | +|------|----------| +| **Цель** | Визуальная и UX-полировка существующих экранов без новых продуктовых фич из макета | +| **Область** | `EditorApp`, `SceneGraph`, `ControlApp`, общие токены/мелкие UI-баги | +| **Вне скоупа** | Новые пункты меню, новые типы сцен, новые каналы микшера, если их нет в коде | + +--- + +## Stage 1 — Implementation (subagent: `frontend-senior`) + +### Чеклист PR + +- [ ] **P0-A** Карточка узла графа: статусы «Авто / Цикл / Аудио» отражают **реальные** поля сцены (`previewVideoAutostart`, `settings.loopVideo`, наличие `media.audios`), а не статичный текст. +- [ ] **P0-B** Пульт: для сцены с видео-превью явно подписано, что **кисть эффектов** недоступна (согласовано с `PresentationView`: эффекты только для image). +- [ ] **P1-A** Пульт: порядок секций **Предпросмотр → Варианты ветвления → Музыка сцены → Быстрый микшер** (вертикальный поток как в референсе). +- [ ] **P1-B** Сетка «Варианты ветвления» без «дыр»: `repeat(auto-fit, minmax(...))` **или** фиксированные слоты с disabled/placeholder **без новых сценариев** — только визуальная сетка. +- [ ] **P2-A** Редактор: меню «Файл» — цвет текста пункта на валидном токене (`--text0` / `--text1`), без несуществующего `--text`. +- [ ] **P2-B** Список сцен: убрать пустой статус-ряд у неактивных карточек (без лишнего вертикального зазора). + +### Критерии приёмки (Stage 1) + +1. На графе для сцены **без видео-превью** не отображаются вводящие в заблуждение чипы «Авто/Цикл» видео-превью; при наличии аудио — корректный индикатор аудио. +2. Для сцены с **видео-превью** чипы «Авто»/«Цикл» совпадают с `previewVideoAutostart` и `scene.settings.loopVideo`. +3. В пульте при `previewAssetType === 'video'` пользователь видит **одну строку-пояснение** (вторичный текст), почему нет панели эффектов. +4. Визуальный порядок блоков пульта соответствует чеклисту P1-A; отступы между `Surface` единообразны (`gap` 16). +5. Нет регрессий drag-and-drop сцен на граф и переключения веток. + +**Definition of Done (Stage 1):** все пункты чеклиста Stage 1 отмечены; из **`dnd_player/`**: `npm run build` успешен. + +--- + +## Stage 2 — Review (subagent: `reviewer`) + +### Чеклист PR + +- [ ] Нет «мёртвых» веток UI и ложных состояний (чипы/подписи соответствуют данным). +- [ ] a11y: фокус/клавиатура на интерактивах не сломаны; `aria-*` не ухудшены. +- [ ] Нет лишнего diff вне файлов задачи. + +### Критерии приёмки (Stage 2) + +1. Reviewer фиксирует замечания с **Severity**; все **high** устранены или явно отклонены с причиной в PR. +2. После правок по review снова выполняется из **`dnd_player/`**: `npm run build`. + +**Definition of Done (Stage 2):** review-замечания закрыты; state `review: done`. + +--- + +## Stage 3 — Tests (subagent: `unit-tests`) + +### Чеклист PR + +- [ ] Добавлены или обновлены тесты на **чистую логику** (например, хелпер разметки чипов по `Scene`, если вынесен в `app/shared`). +- [ ] Либо задокументировано в PR, что изменения только презентационные и покрыты smoke-тестом пайплайна. + +### Критерии приёмки (Stage 3) + +1. Из **`dnd_player/`**: `npm run test` завершается с кодом 0. +2. Новые тесты не flaky, не зависят от Electron UI. + +**Definition of Done (Stage 3):** `tests: done` в `.cursor/pipeline-state.json`. + +--- + +## Stage 4 — Verify (локально / hook `final-verify.cjs`) + +Выполнить подряд из **`dnd_player/`**: + +```bash +npm run lint +npm run typecheck +npm run test +``` + +**Если `npm run lint` падает на массовых `Delete ␍` (CRLF/LF) в файлах вне PR** — до отдельного chore-PR с нормализацией строк для этого чеклиста достаточно: + +```bash +npx eslint app/renderer/control/ControlApp.tsx app/renderer/editor/graph/SceneGraph.tsx app/renderer/shared/ui/sceneGraphChips.ts app/renderer/shared/ui/sceneGraphChips.test.ts --max-warnings 0 +npm run typecheck +npm run test +npm run build +``` + +### Критерии приёмки (Stage 4) + +1. Из **`dnd_player/`**: `npm run typecheck`, `npm run test`, `npm run build` — **exit 0**. +2. Линт: либо полный `npm run lint` — **exit 0**, либо (при известном eol-долге репозитория) scoped-ESLint по файлам PR — **exit 0**, как в блоке выше. +3. Из корня воркспейса (`cursorAi`): `node .cursor/hooks/final-verify.cjs` — успешное завершение пайплайна только когда полный `npm run lint` в **`dnd_player/`** зелёный (хук вызывает полный lint; при падении lint хук пишет `followup_message` в stdout). + +--- + +## Синхронизация с `.cursor/pipeline-state.json` + +После каждой стадии обновлять файл в **корне воркспейса Cursor** (каталог с `.cursor/`): + +```json +{ + "implementation": "done", + "review": "done", + "tests": "done" +} +``` + +До начала работы — все `"pending"`. diff --git a/.cursor/rules/project.mdc b/.cursor/rules/project.mdc new file mode 100644 index 0000000..64d6d06 --- /dev/null +++ b/.cursor/rules/project.mdc @@ -0,0 +1,50 @@ +--- +description: Workspace-wide workflow and conventions (dnd_project) +alwaysApply: true +--- + +# DNDGamePlayer / `dnd_project` — правила работы над задачами (future-pipeline) + +Эти правила применяются **только** когда запрос пользователя требует **изменений в репозитории** (код/конфиги/тесты). Для чисто текстовых задач (описания, маркетинг, переписка) pipeline не запускаем. + +**Корень воркспейса Cursor** — обычно каталог `cursorAi` с единым `.cursor/`. Исходники плеера — в соседнем репозитории **`dnd_player/`** (см. `WORKSPACE.md`). + +## future-pipeline (обязательный порядок) + +### 1) Implementation +- Прочитать релевантный код (минимум 1 файл), найти реальную причину бага/задачи. +- Делать **minimal, review-friendly diff** и следовать текущим паттернам проекта. +- Не добавлять зависимости без явной причины. + +### 2) Review +- Самопроверка изменений: edge-cases, состояние UI (loading/error/empty/disabled), a11y, регрессии. +- Если задача нетривиальная: запустить внутренний «строгий ревью» (под-агент reviewer). + +### 3) Tests +- Обновить/добавить тест(ы), если поведение изменилось или был баг. +- Для мелких правок допускается «облегчённый режим» без под-агентов, но тесты всё равно должны проходить там, где они есть. + +### 4) Verify (всегда, перед ответом — для **`dnd_player`**) + +Из каталога **`dnd_player/`** (или через хук из корня воркспейса, см. `.cursor/hooks/final-verify.cjs`): + +- `npm run lint` +- `npm run typecheck` +- `npm run test` + +Если задача касалась **только** `project-converter` или **DndGamePlayerLicenseServer** — выполни адекватную проверку для этого репозитория (`node --check`, локальный прогон) и явно укажи в ответе, что полный набор команд плеера не запускался (если он не нужен по смыслу). + +Если что-то упало — исправить и повторить до green. + +## Команды **`dnd_player`** (справка) + +Выполнять из **`../dnd_player`** относительно `cursorAi`, либо из корня клонированного `dnd_player`: + +- install: `npm install` +- dev: `npm run dev` +- build: `npm run build` +- lint: `npm run lint` +- typecheck: `npm run typecheck` +- test: `npm run test` + +Полная карта воркспейса: **`WORKSPACE.md`**. diff --git a/.cursor/skills/feature-pipeline/SKILL.md b/.cursor/skills/feature-pipeline/SKILL.md new file mode 100644 index 0000000..517e9ec --- /dev/null +++ b/.cursor/skills/feature-pipeline/SKILL.md @@ -0,0 +1,83 @@ +--- +name: feature-pipeline +description: Implementation → Review → Tests → Verify +--- + +# Workflow + +Контекст воркспейса: **`WORKSPACE.md`**. Основной код плеера — репозиторий **`dnd_player/`** (рядом с корнем воркспейса `cursorAi`). + +## Stage 1 — Implementation + +Используй subagent: frontend-senior + +- реализуй задачу +- сделай minimal diff + +Обнови state: + +{ + "implementation": "done" +} + +--- + +## Stage 2 — Review + +Используй subagent: reviewer + +- проверь изменения +- найди проблемы +- исправь минимально + +Обнови state: + +{ + "implementation": "done", + "review": "done" +} + +--- + +## Stage 3 — Tests + +Используй subagent: unit-tests + +- добавь/обнови tests +- добейся green status + +Обнови state: + +{ + "implementation": "done", + "review": "done", + "tests": "done" +} + +--- + +## Stage 4 — Verify + +Выполни из каталога **`dnd_player/`** (см. также хук `.cursor/hooks/final-verify.cjs`): + +- `npm run lint` +- `npm run typecheck` +- `npm run test` + +Если ошибка: + +- исправь +- повтори + +--- + +## Final Output + +- implementation summary +- review summary +- test summary +- verification status + +## PR-чеклисты (приёмка по задаче) + +Готовые чеклисты с критериями по стадиям лежат в `.cursor/pr-checklists/`. Для UI-выравнивания под макеты: `ui-mock-alignment.md`. Пути к файлам в чеклистах — относительно **`dnd_player/`**. diff --git a/WORKSPACE.md b/WORKSPACE.md new file mode 100644 index 0000000..301d720 --- /dev/null +++ b/WORKSPACE.md @@ -0,0 +1,45 @@ +# Воркспейс `dnd_project` (корень: `cursorAi`) + +Этот каталог — **единая точка входа** в Cursor для всей экосистемы DNDGamePlayer: общие правила, агенты, пайплайн и чеклисты лежат в **`.cursor/`** здесь, а не в отдельных репозиториях. + +Родительская папка на диске: **`D:\Work\my_projects\dnd_project`** (рядом с `cursorAi` лежат остальные репозитории). + +--- + +## Репозитории + +| Каталог | Репозиторий | Назначение | +|---------|-------------|------------| +| **`cursorAi`** | воркспейс Cursor | Документация (этот файл), единые настройки `.cursor/`. Отдельного приложения нет. | +| **`../dnd_player`** | DNDGamePlayer | Electron: редактор и проигрыватель игр (React, Pixi.js, `.dnd.zip`, лицензирование). **Основной** код и полный CI-набор: `lint`, `typecheck`, `test`, `build`. | +| **`../project-converter`** | dnd-project-converter | Утилита: конвертация `.dnd.zip`, добавление превью сцен (WebP). Свой `package.json`, отдельный Electron-пакет. | +| **`../DndGamePlayerLicenseServer`** | dndgameplayer-license-server | HTTP-сервис лицензий (Ed25519, активация, отзыв). Node ≥20, без npm-зависимостей в манифесте. | + +Связь клиента и сервера лицензий описана в README сервера и в коде плеера (`bundledPublicKey` и переменные окружения). + +--- + +## Сборка и проверки + +- **`dnd_player`**: из каталога репозитория — `npm install`, `npm run dev`, `npm run build`, `npm run lint`, `npm run typecheck`, `npm run test`. +- **`project-converter`**: отдельного `build` нет; `npm install`, `npm run dev`. Синтаксис: `node --check` по файлам в `src/`. +- **`DndGamePlayerLicenseServer`**: `npm start` (или `node src/server.mjs`); синтаксис: `node --check` для `src/` и `lib/`. + +Хук **`stop`** в Cursor (см. `.cursor/hooks.json`) запускает финальную проверку: стадии в `.cursor/pipeline-state.json` и команды **`lint` / `typecheck` / `test`** выполняются в каталоге **`dnd_player`**, если воркспейс открыт из `cursorAi` (см. логику в `.cursor/hooks/final-verify.cjs`). Переопределение пути: переменная окружения **`DND_PLAYER_ROOT`**. + +--- + +## Пайплайн и правила + +- Правила агента по умолчанию: **`.cursor/rules/project.mdc`** (future-pipeline: implementation → review → tests → verify). +- Пошаговый сценарий с под-агентами: **`.cursor/skills/feature-pipeline/SKILL.md`**. +- Под-агенты: **`.cursor/agents/`** (`frontend-senior`, `reviewer`, `unit-tests`). +- PR-чеклисты: **`.cursor/pr-checklists/`** (пути к файлам в чеклистах заданы относительно репозитория **`dnd_player/`**). + +При работе **только** в `project-converter` или `DndGamePlayerLicenseServer` соблюдай смысл пайплайна (минимальный diff, тесты по возможности), а формальные команды `npm run lint` и т.д. из плеера запускай, когда менялся общий контракт или есть смысл прогнать регрессию всего монорепо по смыслу (или по требованию задачи). + +--- + +## Multi-root в Cursor (по желанию) + +Чтобы видеть все папки в одном окне, можно добавить в файл `*.code-workspace` блок `folders` с путями к `cursorAi`, `dnd_player`, `project-converter`, `DndGamePlayerLicenseServer`. Корнем с правилами остаётся папка, где лежит актуальный **`.cursor/`** (рекомендуется **`cursorAi`**).