diff --git a/docs/agent/COMPLETED_TASKS.md b/docs/agent/COMPLETED_TASKS.md index 5388932..6bc1174 100644 --- a/docs/agent/COMPLETED_TASKS.md +++ b/docs/agent/COMPLETED_TASKS.md @@ -343,3 +343,16 @@ - Репозиторий: `evening_detective_frontend` - Результат: добавлено создание игры из каталога сценариев, выбор даты и времени, отображение даты и исправлена передача RFC 3339 `startAt`. - Проверки: type-check, lint и lint:css пройдены; build указан в отчёте плана. + +### ED-024 — Общая дизайн-система фронтенда и библиотека компонентов + +- Статус: `DONE` +- Дата: 14.10.2026 +- Репозиторий: `evening_detective_frontend` +- Результат: формализована дизайн-система; по утверждённому плану выделены SiteThemeProvider, CaseFolder, CaseBoard, ObjectCover, StatusStamp, TextHighlight, SectionHeading и DossierFooter; сохранён общий DesignBrand. Образец сайта использует библиотеку, игровые компоненты включены без переработки. Добавлена dev-вкладка «Компоненты» с прямыми импортами, props, slots, событиями и локальными примерами Naive UI; сайт открывается первым, стрелки/Home/End работают для трёх вкладок. +- Поведение: данные обложек и доски передаются родителем; status необязателен и не управляет disabled. Цитаты повторных карточек получают уникальные useId и раскрываются независимо; open обрабатывается локально. Тема сайта изолирована от игры; основные цвета и reduced motion сохранены. В игровом образце добавлен отступ 6 px для выступающих металлических краёв панели. +- Документация: DESIGN_SYSTEM.md дополнен подключением provider и таблицей контрактов. Повторяемый smoke: tests/venue/design_system.py (Vite на 5198, Firefox), инструкция в tests/venue/README.md. +- Проверка: type-check, lint, lint:css, npm run build и git diff --check — успешно. Firefox: 375/768/1280 px, три вкладки, клавиатура, отсутствие переполнения внутренней прокрутки, разные данные, независимые цитаты, уникальность ID, optional status/disabled/loading, форма, окно и возврат фокуса, open, локальный игровой ход, темы, reduced motion. Просмотрены контрольные снимки и вручную проверен diff по FRONTEND_CODE_STANDARD.md. Проверены production-файлы: dev-маршрут и каталог исключены. +- Ограничения: рабочие страницы не мигрировали, игровые компоненты не унифицировали, зависимости не добавляли; commit/push не выполняли. Браузерные проверки выполнены в Firefox, другие браузеры не проверялись. + +- Уточнение ED-024: каталог перестроен в последовательные секции «заголовок → живой компонент → описание → код использования» одной колонкой; игровые и Naive UI примеры расположены рядом со своим кодом. Сохранены изоляция тем и локальные действия. Повторно пройдены type-check, lint, lint:css, сборка и Firefox smoke на 375/768/1280 px; diff проверен по стандарту. diff --git a/docs/agent/TASKS.md b/docs/agent/TASKS.md index 1607d93..3e21240 100644 --- a/docs/agent/TASKS.md +++ b/docs/agent/TASKS.md @@ -98,20 +98,6 @@ - Не входит: разработка рассылки и статистики, автоматизация всей очистки, налоговые вопросы, регистрация ИП/ООО, commit/push. - Следующий шаг: уточнить у владельца подачу уведомления и данные для него, проверить условия Gmail / Яндекс и фактические копии данных; затем определить исполнимые процедуры и согласовать вывод о запуске. -### ED-024 — Общая дизайн-система фронтенда - -- Статус: `TODO` -- Приоритет: `P1` -- Репозиторий: `evening_detective_frontend` -- Цель: создать единый источник правил, по которому можно последовательно делать новые экраны, компоненты и другие визуальные материалы. -- Контекст: во фронтенде уже есть визуальный язык на основе `base.css`, `main.css`, бумажных и металлических текстур, шрифта `a_OldTyper`, собственных компонентов и `naive-ui`; его нужно формализовать без смены общего характера. -- Решение: создать в фронтенде документ дизайн-системы и CSS-токены в существующем стиле проекта; описать цвета, типографику, размеры, отступы, сетку, адаптивность, поверхности, текстуры, состояния, фокус и доступность; зафиксировать правила для кнопок, полей, карточек, панелей, списков, таблиц, модальных окон, уведомлений, навигации, загрузки и ошибок; описать правила иконок, изображений, иллюстраций, именования и примеры правильного использования. -- Критерии: по системе можно без дополнительных устных решений спроектировать новый экран и написать его Vue/CSS-код; токены имеют однозначные имена и назначение; описаны основные состояния и доступность; правила согласуются с текущим визуальным языком; существующий фронтенд не меняет поведение и внешний вид в рамках этой задачи. -- Ограничения: не менять существующие страницы и компоненты; не делать редизайн приложения; не добавлять зависимости; переиспользовать текущие стили, ассеты и `naive-ui`; не создавать Figma-файл или полноценную отдельную библиотеку компонентов. -- Не входит: миграция текущих экранов на новые токены; реализация всех описанных компонентов; изменение API и продуктовой логики. -- Проверка: проверить полноту и однозначность документа и CSS-токенов; выполнить `cd evening_detective_frontend && npm run type-check && npm run lint && npm run lint:css`. -- Следующий шаг: изучить текущие `base.css`, `main.css`, стили и повторяющиеся паттерны компонентов, затем подготовить минимальный документ и набор токенов без миграции существующих экранов. - ### ED-018 — Удаление неиспользуемых пользовательских файлов - Статус: `TODO` diff --git a/tests/venue/README.md b/tests/venue/README.md index a554b6e..a08a259 100644 --- a/tests/venue/README.md +++ b/tests/venue/README.md @@ -210,3 +210,10 @@ TR3000 V1 2.5.27-20260804-183625 без изменения настроек ро Flow также проверяет переход заполненной БД с числовых ID команд на UUID v7, сохранность данных и ограничений, независимое создание команд в трёх схемах и полную Go-проверку на временном PostgreSQL. `team_uuid.py` вызывается из flow. + +## Дизайн-система (без сервера и БД) + +Запустить фронтенд командой `npm run dev -- --port 5198`, затем из корня +`python3 tests/venue/design_system.py`. Требуется установленный Firefox. +Smoke проверяет три ширины, клавиатурные вкладки, повторные карточки и ID, +локальную форму и окно, состояния, темы и reduced motion. diff --git a/tests/venue/design_system.py b/tests/venue/design_system.py new file mode 100644 index 0000000..ac7fdc3 --- /dev/null +++ b/tests/venue/design_system.py @@ -0,0 +1,107 @@ +"""Run with Vite on port 5198 and Firefox installed; no API required.""" +import json, pathlib, socket, subprocess, tempfile, time +profile=pathlib.Path(tempfile.mkdtemp(prefix='home-dossier-')) +with socket.socket() as s: + s.bind(('127.0.0.1',0));port=s.getsockname()[1] +(profile/'user.js').write_text(f'user_pref("marionette.port", {port});\nuser_pref("browser.shell.checkDefaultBrowser", false);') +log=(profile/'firefox.log').open('w') +firefox=subprocess.Popen(['firefox','--headless','--no-remote','--profile',str(profile),'--marionette','--remote-allow-system-access','about:blank'],stdout=log,stderr=log) +sock=None +try: + for _ in range(100): + try: sock=socket.create_connection(('127.0.0.1',port),timeout=1);break + except OSError: time.sleep(.1) + assert sock is not None, 'Firefox did not start' + sock.settimeout(20) + def read(): + size=b'' + while not size.endswith(b':'): size+=sock.recv(1) + data=b'';length=int(size[:-1]) + while len(data)e.children[0].tagName==="H2" && e.children[1].matches(".component-sample, .n-config-provider") && e.children[3].matches("pre") && e.children[3].querySelector("code"))'), 'Each component needs a heading, sample and usage code' + assert js('return [...document.querySelectorAll("[id]")].map(e=>e.id).length===new Set([...document.querySelectorAll("[id]")].map(e=>e.id)).size'), 'Duplicate DOM IDs' + assert js('return document.querySelectorAll(".catalog .status-stamp").length===2'), 'Optional folder status' + assert js('return !document.querySelectorAll(".catalog .folder-action")[0].disabled && document.querySelectorAll(".catalog .folder-action")[1].disabled'), 'Status controls availability' + ids=js('return [...document.querySelectorAll(".catalog .clue-toggle")].map(e=>e.getAttribute("aria-controls"))') + assert len(set(ids))==2 + js('document.querySelectorAll(".catalog .clue-toggle")[0].click();return true') + wait('document.querySelectorAll(".catalog .clue-toggle")[0].getAttribute("aria-expanded")==="true"') + assert js('return document.querySelectorAll(".catalog .clue-toggle")[1].getAttribute("aria-expanded")')=='false' + js('document.querySelectorAll(".catalog .clue-toggle")[1].click();return true') + wait('document.querySelectorAll(".catalog .clue-toggle")[1].getAttribute("aria-expanded")==="true"') + js('document.querySelectorAll(".catalog .clue-toggle")[0].click();return true') + wait('document.querySelectorAll(".catalog .clue-toggle")[0].getAttribute("aria-expanded")==="false"') + assert js('return document.querySelectorAll(".catalog .clue-toggle")[1].getAttribute("aria-expanded")')=='true' + assert js('return document.querySelector(".catalog .case-board").textContent.includes("Найден у двери")') + assert js('return document.querySelector(".catalog [data-component=ObjectCover] .object-cover").textContent.includes("Чердак")') + js('document.querySelector(".catalog form").dispatchEvent(new Event("submit",{bubbles:true,cancelable:true}));return true') + wait('document.querySelector(".catalog").textContent.includes("Укажите название команды")') + js('const e=document.querySelector("#catalog-team");e.value="Сыщики";e.dispatchEvent(new Event("input",{bubbles:true}));return true') + js('document.querySelector(".catalog form").dispatchEvent(new Event("submit",{bubbles:true,cancelable:true}));return true') + wait('document.querySelector(".catalog").textContent.includes("Сыщики» готова")') + js('document.querySelector(".catalog .folder-action").focus();document.querySelector(".catalog .folder-action").click();return true') + wait('!!document.querySelector("[role=dialog]")') + assert js('return document.querySelector("[role=dialog]").getAttribute("aria-label")')=='Повторное дело' + key('\ue00c') + wait('!document.querySelector("[role=dialog]")') + wait('document.activeElement===document.querySelector(".catalog .folder-action")') + assert js('return document.querySelector(".catalog .buttons button[disabled]")!==null && document.querySelector(".catalog .buttons .n-button--loading")!==null') + assert js('return getComputedStyle(document.querySelector(".catalog .buttons button")).backgroundColor')=='rgb(255, 212, 59)' + js('const e=document.querySelector(".game-input-run");e.value="Сад";e.dispatchEvent(new Event("input",{bubbles:true}));return true') + wait('!document.querySelector(".game-button-run").disabled') + js('document.querySelector(".game-button-run").click();return true') + wait('!!document.querySelector(".action-result")') + assert js('return getComputedStyle(document.querySelector(".action-result")).getPropertyValue("--n-icon-color").trim()')!='#ffd43b' + js('document.querySelector("#tab-components").focus();return true') + key('\ue014'); wait('!!document.querySelector(".site-preview")') + key('\ue012'); wait('!!document.querySelector(".catalog")') + key('\ue011'); wait('!!document.querySelector(".site-preview")') + key('\ue014'); wait('!!document.querySelector(".game-preview")') + key('\ue014'); wait('!!document.querySelector(".catalog")') + key('\ue011'); wait('!!document.querySelector(".site-preview")') + print(f'PASS {width}px: three tabs, quotes, IDs, data, states, form, open, modal, themes', flush=True) + command('Marionette:SetContext', {'value':'chrome'}) + js('Services.prefs.setIntPref("ui.prefersReducedMotion",1);return true') + command('Marionette:SetContext', {'value':'content'}) + wait('matchMedia("(prefers-reduced-motion: reduce)").matches') + js('document.querySelector(".clue-toggle").focus();return true') + assert js('return getComputedStyle(document.querySelector(".case-folder")).transform')=='none' + assert js('return getComputedStyle(document.querySelector(".case-folder")).transitionDuration')=='0s' + assert js('return getComputedStyle(document.querySelector(".clues"),"::before").transitionDuration')=='0s' + print('PASS reduced motion', flush=True) +finally: + if sock:sock.close() + firefox.terminate();firefox.wait(timeout=10);log.close()