From fa20059ecd896d1cbffdc430649401c18892e664 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 21 Oct 2026 23:54:22 +0400 Subject: [PATCH 01/23] =?UTF-8?q?=D0=A1=D0=BF=D0=B5=D0=BA=D0=B0:=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9=D0=BD=20=D0=B2=D0=B5=D0=B1?= =?UTF-8?q?-=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9=D0=BD-=D1=81?= =?UTF-8?q?=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=B5=20AURA=20(#65)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- openspec/changes/redesign/.openspec.yaml | 2 + openspec/changes/redesign/design.md | 189 +++++++++++ openspec/changes/redesign/proposal.md | 40 +++ .../changes/redesign/specs/web-app/spec.md | 320 ++++++++++++++++++ 4 files changed, 551 insertions(+) create mode 100644 openspec/changes/redesign/.openspec.yaml create mode 100644 openspec/changes/redesign/design.md create mode 100644 openspec/changes/redesign/proposal.md create mode 100644 openspec/changes/redesign/specs/web-app/spec.md diff --git a/openspec/changes/redesign/.openspec.yaml b/openspec/changes/redesign/.openspec.yaml new file mode 100644 index 0000000..dc36fb1 --- /dev/null +++ b/openspec/changes/redesign/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-21 diff --git a/openspec/changes/redesign/design.md b/openspec/changes/redesign/design.md new file mode 100644 index 0000000..51cdd32 --- /dev/null +++ b/openspec/changes/redesign/design.md @@ -0,0 +1,189 @@ +## Context + +`main` 57bf1fa. Материалы дизайн-системы (вне репозитория, `~/projects/jira-bridge/design/`): +- `DESIGN-SYSTEM-RULES.md`, `STYLE-INVENTORY.md`, `REACT-COMPONENT-CATALOG.md` — правила и инвентарь AURA; +- `AURA-xlsx-tool-handoff-2026-09-29.zip` — исходники AURA: `design-system/tokens.json` (канон токенов), + `src/styles/tokens.css` (сгенерированные CSS-переменные), `src/styles.css`, `design-system/foundations.md`; +- `Overview.svg`, `Универсалки-2…7.svg` — экспорт Figma «Универсалки»: основы (цветовые роли, типографика, геометрия), + загрузка файлов, составные компоненты (плитки выбора, сообщения, панель действия), макеты, контейнер контента, + базовые компоненты (кнопки, табы, шаги, тема, навигация приложений). + +Что важно из системы: +- **Цвета (светлая):** канва `#f8f8f6`, поверхность `#fdfdfc`, приглушённый фон `#fafaf8`, текст `#17171a`, вторичный + `#818190`, плейсхолдер `#5c6260`, граница `#e5e5e0`, сильная граница `#cfcfc8`, действие `#568d32` (hover/active + `#477729`), выбранное — фон `#f4faf5` и граница `#568d32`, ошибка `#b42318` / фон `#fff1f0`, предупреждение `#d89614` + / фон `#fffbe6` / граница `#ffe58f`, фокус `rgb(42 122 70 / 35%)`. **Тёмная:** канва `#181818`, поверхность `#1f1f1f`, + приглушённый `#272727`, текст `#f2f4f2`, вторичный `#d6dcd7`, плейсхолдер `#aab2ac`, границы `rgb(232 238 233 / 10%)` и + `/ 17%`, действие `#69a93d` (hover `#83c657`), текст на действии `#112016`, ошибка `#e26c67` / фон `#402725`, + предупреждение `#e8c17a` / фон `#382f20` / граница `#8a6d1d`, фокус `rgb(116 184 255 / 55%)`. +- **Типографика:** заголовок раздела Bold 20/28, текст Regular 14/20, подпись Semibold 14/20, мелкий текст 12/16, + крупный 16/24. +- **Геометрия:** шаг 4 px; контрол 32 px (крупный 40), радиус контрола 10; кнопки — «таблетки» (радиус 999); + панели — радиус 16, отступ 16–24; окно/канва — радиус 20–24; флажок 16×16 радиус 4; контейнер контента 1440 px; + переходы 180 ms ease, нажатие 100 ms. +- **Компоненты из Figma:** кнопки (основная зелёная, вторичная контурная, опасная, с загрузкой), сегментный + переключатель на сером треке с белой активной «таблеткой» и зелёным текстом («Навигация приложений»), переключатель + темы (солнце / луна), сообщения успеха и ошибки (иконка в круге, заголовок, пояснение), плитки выбора, плашки + файлов, шаги процесса. + +Приложение сейчас (`web/src`): глобальные стили `styles/{theme,base,controls}.css` (переменные `--bg`, `--fg`, +`--muted`, `--line`, `--card`, `--panel`, `--soft`, `--accent` синий `#1f6f8b`, палитра видов работ `--k-*`, приоритетов +`--p-*`, стрелок `--edge*`, `--guide`), стили страниц `board/board.css` (165 строк, в нём же вход, список, карточка), +`issues/issues.css`, `projects/projects.css`, `shell/shell.css`. Шрифт IBM Plex Sans/Mono из `@fontsource`. Иконки — +символы текста (⋯ ⇄ ⚑ ▥ ✕ ▼). Обновление из Jira, ход пакета и тема живут в `board/useBoardSource.ts` и +`board/components/Toolbar.tsx`; тема — `shell/theme.ts` (ключ `graph-theme`: `""` системная, `light`, `dark`). + +## Goals / Non-Goals + +**Goals:** приложение выглядит как AURA в обеих темах; каждый экран собран из одних компонентов и токенов; общие +действия (обновление из Jira, тема, токен) — в оболочке; фильтры доски и списка сгруппированы и не перегружают экран; +поведение, адреса, ключи `localStorage` и логика доски — прежние. + +**Non-Goals:** см. proposal.md. + +## Decisions + +1. **Без Ant Design.** Приложение на своих компонентах; тянуть antd ради внешнего вида — тяжёлая зависимость и + переписывание всех контролов. Токены AURA переносятся в `web/src/styles/tokens.css` вручную с пометкой источника + (`tokens.json` 29.09.2026); скрипта синхронизации нет. +2. **Токены — два слоя.** Семантические имена AURA (`--color-bg-canvas`, `--color-text-secondary`, + `--color-action-primary`, `--space-*`, `--radius-*`, `--font-size-*`, `--component-control-height` …) для светлой и + тёмной темы плюс токены приложения, которых в AURA нет: палитра видов работ `--k-*`, приоритеты `--p-*`, стрелки + `--edge`, `--edge-strong`, `--guide`, дорожки доски. Старые имена (`--bg`, `--fg`, `--muted`, `--line`, `--card`, + `--panel`, `--soft`, `--accent`, `--accent-fg`) на время перехода — псевдонимы семантических; после переноса всех + страниц псевдонимы удаляются. `--edge-strong` и `--accent` становятся зелёными; `--guide` (передачи, критический + путь) остаётся оранжевым — его роль — выделение поверх зелёного. Палитра `--k-*` подбирается заново под тёплую + канву, оттенки остаются различимыми между собой и с зелёным действием. +3. **Цвета только из токенов.** Вне `tokens.css` в CSS нет литералов цвета (`#…`, `rgb(…)`, `hsl(…)`, именованных, + кроме `transparent`, `currentColor`, `inherit`); проверка — тест Vitest, который читает все `*.css` в `web/src`. + Так новая страница не может «забыть» тему. +4. **Шрифт — Mulish** (`@fontsource/mulish` 5.3.0, начертания 400/600/700, подмножества с кириллицей): ближайшая + свободная гарнитура к Proxima Nova по пропорциям; Montserrat шире и съедает место в плотных таблицах. Моноширинный — + IBM Plex Mono (ключи задач, код, исходник разметки), как сейчас. IBM Plex Sans удаляется из зависимостей. +5. **Иконки — свои SVG** в `web/src/ui/icons.tsx` (линейные 16/20 px, обводка 1.5, `currentColor`), набор под + нужды приложения: обновить, загрузка/файл, меню (⋯), закрыть, шеврон, поиск, фильтр, солнце, луна, внешняя ссылка, + предупреждение, галочка, ошибка, передачи, критический путь, по датам, соседи, связи, сетка, сброс, легенда. Без + новой зависимости; текстовые символы в интерфейсе заменяются иконками. +6. **Компоненты `web/src/ui/`:** `Button` (варианты `primary`, `secondary`, `danger`, `ghost`; размеры 32 и 40; + «таблетка»; состояние загрузки — крутилка вместо иконки, кнопка недоступна), `IconButton` (32×32, радиус 10, имя — + `aria-label`), `Segmented` (трек `--color-bg-subtle`/серый, активный сегмент — поверхность и зелёный текст; ссылки + или кнопки), `ThemeSwitch` (солнце/луна), `Field` (подпись Semibold 14/20 над контролом), `TextInput`, `SearchInput` + (иконка поиска, очистка), `Select` (родной `` без `color-scheme` рисуются + светлыми (тест в Task 1: `color-scheme: dark` в тёмных блоках `tokens.css`; скриншоты Task 11). +4. Смена шрифта и сохранённые размеры карточек: текст не вылезает за карточку, высота по содержимому считается тем же + слоем `.measure` (e2e «растяжка за уголок и возврат размера» в Task 7 остаётся зелёным). +5. Список с выбранными фильтрами, открытый по адресу в новой вкладке: панель «Фильтры» свёрнута, но плашки и счётчик + видны (тест в Task 8 «Фильтр в адресе»). + +--- + +### Task 1: Токены, шрифт, проверка цветов + +**Files:** +- Create: `web/src/styles/tokens.css`, `web/src/styles/tokens.test.ts` +- Modify: `web/src/styles/theme.css` (удалить файл; импорт заменить на `tokens.css` в `web/src/App.tsx`), + `web/src/styles/base.css`, `web/src/styles/controls.css`, `web/src/board/board.css`, `web/src/main.tsx`, + `web/package.json`, `web/package-lock.json` + +**Interfaces:** +- Produces: CSS-переменные для всех задач. Семантические (обе темы): `--color-bg-canvas`, `--color-bg-surface`, + `--color-bg-subtle`, `--color-bg-selected` (`#f4faf5` / `#272727` — фон выбранного), `--color-bg-hover` + (`#f2f2f0` / `#272727`), `--color-bg-track` (трек сегмента, `#eeeeeb` / `#272727`), `--color-text-primary`, + `--color-text-secondary`, `--color-text-placeholder`, `--color-text-disabled`, `--color-text-on-primary`, + `--color-border-default`, `--color-border-strong`, `--color-border-selected`, `--color-action-primary`, + `--color-action-primary-hover`, `--color-action-danger`, `--color-action-danger-hover`, `--color-status-success`, + `--color-status-success-bg`, `--color-status-error`, `--color-status-error-bg`, `--color-status-warning`, + `--color-status-warning-bg`, `--color-status-warning-border`, `--color-focus-ring`, `--color-overlay` + (`rgb(0 0 0 / 45%)`), `--shadow-popover`, `--shadow-dialog`. Шкалы: `--space-1…8` (4…32), `--radius-xs/sm/md/lg/xl/2xl/round` + (4/8/10/12/16/20/999), `--font-ui` (`"Mulish", system-ui, -apple-system, "Segoe UI", sans-serif`), `--font-mono`, + `--fs-caption` 12, `--fs-body` 14, `--fs-lg` 16, `--fs-section` 20, `--lh-caption` 16, `--lh-body` 20, `--lh-lg` 24, + `--lh-section` 28, `--control-h` 32, `--control-h-lg` 40, `--control-radius` 10, `--motion` `180ms ease`. + Токены доски: `--k-decision`, `--k-analytics`, `--k-dev`, `--k-ui`, `--k-test`, `--k-org`, `--p-crit`, `--p-high`, + `--p-mid`, `--p-low`, `--edge`, `--edge-strong` (= действие), `--guide` (оранжевый, прежний), `--lane-alt`. + Псевдонимы на переход (удаляются в Task 10): `--bg`→canvas, `--fg`→text-primary, `--muted`→text-secondary, + `--line`→border-default, `--card`→surface, `--panel`→surface, `--soft`→bg-hover, `--accent`→action-primary, + `--accent-fg`→text-on-primary. +- Тёмная тема — те же три блока, что сейчас в `theme.css`: `@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){…} }` + и `:root[data-theme="dark"]{…}`; в обоих `color-scheme: dark`, в светлом `:root` — `color-scheme: light`. + +- [ ] **Step 1: Тест `tokens.test.ts`** (Vitest, node `fs`): (а) «вне tokens.css нет литералов цвета» — обходит все + `web/src/**/*.css`, кроме `styles/tokens.css`, ищет `/#[0-9a-fA-F]{3,8}\b|\b(rgb|rgba|hsl|hsla)\(|:\s*(white|black|red|…)\b/` + (именованные — список CSS-цветов из 10–15 самых частых достаточно: white, black, red, green, blue, gray, grey, + orange, yellow, purple) вне комментариев; падение перечисляет `файл: литерал`. (б) «обе темы задают одни имена» — + множество `--color-*` в светлом `:root` равно множеству в `:root[data-theme="dark"]` и в медиа-блоке. (в) тёмные + блоки содержат `color-scheme: dark`. +- [ ] **Step 2:** `cd web && npx vitest run src/styles/tokens.test.ts` — FAIL (нет файла, литералы в `controls.css`, + `board.css`). +- [ ] **Step 3:** написать `tokens.css` по Interfaces и design.md «Context»; палитру `--k-*` подобрать под тёплую канву + (светлая — насыщенность и светлота как у нынешних, различимы с `#568d32`; тёмная — светлее); перенести литералы + `controls.css` (`#fff`, `rgba(0,0,0,.18)`) и `board.css` (`rgba(0,0,0,.18)`) в токены (`--color-text-on-primary`, + `--shadow-popover`); удалить `theme.css`, импорт `./styles/tokens.css` в `App.tsx` первым. +- [ ] **Step 4:** шрифт: `npm install --save-exact @fontsource/mulish@5.3.0`, `npm uninstall @fontsource/ibm-plex-sans`; + в `main.tsx` — `@fontsource/mulish/400.css`, `600.css`, `700.css`, `@fontsource/ibm-plex-mono/500.css`; `base.css`: + `body{font:var(--fs-body)/var(--lh-body) var(--font-ui);background:var(--color-bg-canvas);color:var(--color-text-primary)}`, + `h1{font-size:var(--fs-section);line-height:var(--lh-section);font-weight:700}`, `:focus-visible` — кольцо по + Global Constraints, `@media (prefers-reduced-motion: reduce){*{transition:none!important;animation:none!important}}`. +- [ ] **Step 5:** `npx vitest run` — весь набор зелёный (старые тесты не трогали разметку); `npm run lint` — чисто. +- [ ] **Step 6: Commit** `feat(web): токены AURA, шрифт Mulish, проверка цветов (#65)` + +### Task 2: Иконки и базовые компоненты + +**Files:** +- Create: `web/src/ui/icons.tsx`, `web/src/ui/Button.tsx`, `web/src/ui/Segmented.tsx`, `web/src/ui/ThemeSwitch.tsx`, + `web/src/ui/Badge.tsx`, `web/src/ui/Notice.tsx`, `web/src/ui/Panel.tsx`, `web/src/ui/PageHeader.tsx`, + `web/src/ui/ui.css`, `web/src/ui/index.ts`, `web/src/ui/ui.test.tsx` +- Modify: `web/src/shell/theme.ts`, `web/src/shell/theme.test.ts` + +**Interfaces:** +- Consumes: токены Task 1. +- Produces (все из `web/src/ui/index.ts`): + - `Icon` — компоненты `IconRefresh, IconUpload, IconMore, IconClose, IconChevronDown, IconSearch, IconFilter, + IconSun, IconMoon, IconExternal, IconWarning, IconCheck, IconError, IconInfo, IconHandoff, IconCritical, + IconCalendar, IconNeighbors, IconLink, IconGrid, IconReset, IconLegend, IconOpen` — `(p: {size?: 16|20}) => JSX`, + `

`, + подзаголовок 14/20 вторичным, действия справа (перенос на узких). +- `theme.ts`: `THEMES`, `readTheme`, `saveTheme`, `applyTheme` прежние; новые `systemDark(): boolean` + (`matchMedia("(prefers-color-scheme: dark)")`, без `matchMedia` — `false`) и + `effectiveDark(t: Theme): boolean` (`t === "dark" || (t === "" && systemDark())`). `THEME_NAMES` удалить, если не + используется. + +- [ ] **Step 1: Тесты `ui.test.tsx`:** `Button loading` — `aria-busy="true"`, недоступна, иконка не показана; + `Button aria-pressed` пробрасывается; `IconButton` — доступное имя из `label`; `Segmented` кнопками — `radiogroup`, + щелчок вызывает `onChange("b")`, у выбранного `aria-checked="true"`; `Segmented` ссылками — внутри `MemoryRouter`, + активная ссылка `aria-current="page"`; `ThemeSwitch` — без `graph-theme` и с `matchMedia` → dark показывает + `aria-checked="true"`, щелчок → `graph-theme` = `light`, `data-theme="light"`; второй щелчок → `dark`; `Chip` — + кнопка «Снять фильтр Спринт» вызывает `onRemove`; `Notice tone="error"` — `role="alert"`. + `theme.test.ts`: `effectiveDark("")` следует `matchMedia`, `effectiveDark("light") === false`. +- [ ] **Step 2:** `npx vitest run src/ui src/shell/theme.test.ts` — FAIL. +- [ ] **Step 3:** реализовать компоненты и `ui.css` (только переменные из Task 1; размеры — Global Constraints; + `Button primary` — фон действия, текст `--color-text-on-primary`, hover — `--color-action-primary-hover`; + `secondary` — прозрачный фон, граница `--color-border-strong`, текст основной, hover — граница действия; + `danger` — фон `--color-action-danger`; `ghost` — без рамки, текст действия; `lg` — 40 px, отступ 20). +- [ ] **Step 4:** `npx vitest run` и `npm run lint` — зелёные. +- [ ] **Step 5: Commit** `feat(web): иконки и базовые компоненты дизайн-системы (#65)` + +### Task 3: Компоненты форм, окно, меню + +**Files:** +- Create: `web/src/ui/Field.tsx`, `web/src/ui/inputs.tsx` (`TextInput`, `SearchInput`, `Select`, `Checkbox`, + `Textarea`), `web/src/ui/Dialog.tsx`, `web/src/ui/Menu.tsx`, `web/src/ui/forms.test.tsx` +- Modify: `web/src/ui/index.ts`, `web/src/ui/ui.css`, `web/src/board/components/Dropdown.tsx` (переносится в + `ui/Menu.tsx` как `Dropdown`; старый файл — реэкспорт или удаление с правкой импортов `MultiSelect`, `Toolbar`) + +**Interfaces:** +- Produces: + - `Field(p: {label: string; hint?: ReactNode; error?: ReactNode; children: ReactElement})` — `