From 3b8406fb4cace498cedf8146f40829d130024f00 Mon Sep 17 00:00:00 2001 From: Iu Date: Fri, 23 Oct 2026 22:01:53 +0300 Subject: [PATCH] =?UTF-8?q?chore(openspec):=20=D0=B0=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D0=B2=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20photo-color-?= =?UTF-8?q?editing=20=D0=B8=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20photos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Дельта-спека, дизайн, предложение и задачи приведены к фактической реализации: WebGL2-предпросмотр, пипетка баланса белого, цветокоррекция по зонам. Изменение применено к основной спеке photos и перемещено в archive. --- .../.openspec.yaml | 0 .../2026-10-23-photo-color-editing/design.md | 112 ++++++++++++++++++ .../proposal.md | 36 +++--- .../specs/photos/spec.md | 30 ++++- .../2026-10-23-photo-color-editing/tasks.md | 59 +++++++++ .../changes/photo-color-editing/design.md | 88 -------------- openspec/changes/photo-color-editing/tasks.md | 35 ------ openspec/specs/photos/spec.md | 46 +++++-- 8 files changed, 258 insertions(+), 148 deletions(-) rename openspec/changes/{photo-color-editing => archive/2026-10-23-photo-color-editing}/.openspec.yaml (100%) create mode 100644 openspec/changes/archive/2026-10-23-photo-color-editing/design.md rename openspec/changes/{photo-color-editing => archive/2026-10-23-photo-color-editing}/proposal.md (57%) rename openspec/changes/{photo-color-editing => archive/2026-10-23-photo-color-editing}/specs/photos/spec.md (58%) create mode 100644 openspec/changes/archive/2026-10-23-photo-color-editing/tasks.md delete mode 100644 openspec/changes/photo-color-editing/design.md delete mode 100644 openspec/changes/photo-color-editing/tasks.md diff --git a/openspec/changes/photo-color-editing/.openspec.yaml b/openspec/changes/archive/2026-10-23-photo-color-editing/.openspec.yaml similarity index 100% rename from openspec/changes/photo-color-editing/.openspec.yaml rename to openspec/changes/archive/2026-10-23-photo-color-editing/.openspec.yaml diff --git a/openspec/changes/archive/2026-10-23-photo-color-editing/design.md b/openspec/changes/archive/2026-10-23-photo-color-editing/design.md new file mode 100644 index 0000000..d53acbd --- /dev/null +++ b/openspec/changes/archive/2026-10-23-photo-color-editing/design.md @@ -0,0 +1,112 @@ +# Design + +## Context + +Неразрушающее редактирование фото уже работает: параметры хранятся в +`photo.edit_params` (`map[string]any` JSON-блоб), сервер рендерит копию в роли +`edit`, `edit_thumb`, `edit_medium`, `edit_preview`, оригинал не меняется. +Исторически поддерживался только блок «Свет» (`LightParams`), живой превью шёл +через WASM (`frontend/wasm/light/`). Теперь добавлен блок «Цвет» и GPU-превью: +основной live-предпросмотр считает WebGL2, а CPU-путь (TypeScript/WASM) остаётся +фолбэком и эталоном паритета. Хранение в JSON-блобе миграции не требует: фото без +ключа `color` рендерятся как прежде. + +## Goals / Non-Goals + +**Goals:** +- Добавить «Цвет»: баланс белого (температура/оттенок + пипетка), сочность + (vibrance), насыщенность, цветовой микшер по оттенкам, цветокоррекция + (тени/средние тона/света + яркость зон, наложение, баланс), поканальная + коррекция. +- Показывать правки в просмотрщике сразу, не дожидаясь серверного рендера. +- Сохранить неразрушающий серверный рендер как источник истины и обратную + совместимость со старыми `edit_params`. + +**Non-Goals:** +- Синхронизация правок между устройствами и Android-клиент. +- Изменение ролей хранения или формата объектов. +- Миграция схемы базы данных. + +## Decisions + +**Цвет хранится в том же `edit_params`, рядом с `light`.** +- Обоснование: `EditParams` расширяется полем `Color ColorParams`; JSON-блоб и + клиентская модель принимают новый ключ без схемных изменений. +- Альтернатива: отдельная колонка/таблица — отвергнута, потребовала бы миграцию. + +**Порядок операций: свет, затем цвет.** +- Обоснование: цветовые коррекции логично применять к уже скорректированной по + тональности картинке; сервер и клиент следуют одному порядку. +- Альтернатива: цвет до света — отвергнуто, расходится с поведением Lightroom и + текущим конвейером. + +**Живой предпросмотр считается на GPU (WebGL2), CPU/WASM — фолбэк.** +- Обоснование: попиксельный CPU-фильтр на полном viewport блокировал main thread + (замеры: десятки мс на кадр). Шейдер повторяет свет/цвет и считает кадр на + видеокарте; текстура источника загружается один раз на смену изображения. +- Фолбэк: если WebGL2 недоступен или шейдер не собрался, используется прежний + CPU-путь (WASM, затем JS) с адаптивным буфером. Это медленнее, но сохраняет + функциональность. +- Альтернатива: вынести CPU-фильтр в Web Worker/OffscreenCanvas — отвергнуто как + более сложное при уже готовом GPU-пути. + +**Паритет реализаций ограничен явно.** +- Go (сервер) — источник истины для сохранённого фото; покрыт Go-тестами. +- TypeScript и Rust/WASM — эталонная пара для CPU-фолбэка; паритет проверяется + `wasmMatchesReference` на пробе и больших градиентах. +- GLSL/WebGL2 — быстрый предпросмотр; проверяется компиляцией/линковкой шейдера, + пиксельный паритет с сервером не гарантируется. Финальное качество даёт сервер. + +**Тональные зоны и быстрый путь.** +- Веса зон: тени `(1-l)^4`, света `l^4`, средние тона — остаток; это не даёт + колесу теней заметно задевать средние тона и уменьшает резкую границу на + контрастных снимках. +- `blending` по умолчанию 50 (диапазон 0..100), `balance` — -100..100. +- Яркость зон применяется аддитивным сдвигом, без деления на пиксель. +- Если микшер и насыщенность/вибрация не задействованы, цвет считается быстрым + путём без RGB→HSL→RGB; иначе — полный HSL-путь. + +**Пустота цветовых правок.** +- Наложение/баланс и оттенок колеса без силы и яркости не считаются правкой: + фото не получает цветовую версию, сброс остаётся доступным по другим полям. + +**Пипетка баланса белого.** +- Клиент сэмплирует пиксель из исходного (неотредактированного) превью и решает + обратную задачу баланса белого: подбирает температуру/оттенок, при которых + точка становится нейтральной. Пресет переходит в «Пользовательский». + +**Поканальная коррекция — в модели и рендере, но не в UI.** +- `ColorCorrection` (red/green/blue) принимается API и учитывается рендером, но в + панели не выведена: в макете «Цветокоррекция» — это колёса тональных зон. + +**UI.** +- Раздел «Цвет»: баланс белого с пресетами и пипеткой, сочность/насыщенность, + микшер (8 оттенков × тон/насыщенность/яркость), цветокоррекция (3 колеса, + яркость зон, наложение, баланс). +- Слайдеры с градиентными дорожками, цветовые колёса, двойной клик по контролу + сбрасывает значение. + +## Risks / Trade-offs + +[WebGL2 недоступен] → фолбэк на CPU/WASM с адаптивным буфером; функциональность +сохраняется, скорость ниже. + +[Расхождение шейдера и сервера] → серверный рендер остаётся источником истины; +шейдер проходит проверку компиляции, а CPU-пара JS/WASM — тесты паритета. + +[Большая текстура оригинала] → загрузка один раз на смену источника; при +переключении зума/источника возможна разовая пауза. + +[Резкая граница на контрастных снимках] → более узкие веса зон и аддитивная +яркость; полностью убрать границу нельзя, если она есть в самом снимке. + +[Обратная совместимость] → отсутствие `color` трактуется как нулевые правки. + +## Migration Plan + +Миграция схемы не требуется: новый ключ `color` дополняет существующий `light`. +Откат — удаление ключа `color` из `edit_params`. + +## Open Questions + +Нет. diff --git a/openspec/changes/photo-color-editing/proposal.md b/openspec/changes/archive/2026-10-23-photo-color-editing/proposal.md similarity index 57% rename from openspec/changes/photo-color-editing/proposal.md rename to openspec/changes/archive/2026-10-23-photo-color-editing/proposal.md index f49ee94..4de0132 100644 --- a/openspec/changes/photo-color-editing/proposal.md +++ b/openspec/changes/archive/2026-10-23-photo-color-editing/proposal.md @@ -10,18 +10,20 @@ ## What Changes -- Расширить `EditParams` новым блоком `Color`: баланс белого (Темп, Оттенок), - Вибрация (Vibrance), Насыщенность, Цветовой микшер (Тон/Насыщенность/Яркость - по оттенкам), Точечный цвет и Цветокоррекция. +- Расширить `EditParams` новым блоком `Color`: баланс белого (температура/оттенок + + пипетка по нейтральной точке), сочность (vibrance), насыщенность, цветовой + микшер (8 оттенков × тон/насыщенность/яркость), цветокоррекция (тени/средние + тона/света + яркость зон, наложение, баланс) и поканальная коррекция. - Сервер (Go): рендер цветовых правок на пикселях исходного объекта, порядок операций — свет, затем цвет; результат записывается в те же роли `edit`, `edit_thumb`, `edit_medium`, `edit_preview`. -- Клиент (TypeScript): цветовые ползунки и элементы управления в - `PhotoEditPanel`, живой превью через `light.wasm` с тестом паритета - (`wasmMatchesReference`). +- Клиент (TypeScript/Svelte): раздел «Цвет» с ползунками, цветовыми колёсами и + микшером; живой предпросмотр на WebGL2, CPU/WASM-фолбэк с тестом паритета + (`wasmMatchesReference`); пипетка баланса белого; двойной клик по контролу + сбрасывает значение. - WASM (Rust): расширение `frontend/wasm/light/` цветовыми операциями и - пересборка `static/light.wasm`. -- Хранение: `photo.edit_params` остаётся `map[string]any` JSON-блоба — старые + пересборка `static/light.wasm` (фолбэк предпросмотра). +- Хранение: `photo.edit_params` остаётся `map[string]any` JSON-блобом — старые фото без ключа `color` рендерятся без цветовых правок, миграций схемы не требуется. @@ -43,13 +45,17 @@ `Normalized`/`IsEmpty`/`JSON`. - `backend/internal/photos/edit_render.go` — `applyColor` / `applyColorRows`, `decodeAndAdjust` применяет свет и цвет. -- `backend/internal/photos/edit_service.go` — валидация и сохранение без - изменений логики (параметр `color` добавится в JSON). +- `backend/internal/photos/handlers.go` — приём `color` в `PATCH /photos/:id/edit`. - `frontend/src/lib/edit.ts` — `ColorParams`, цветовые ползунки, - `readEditParams`/`isEmptyEditParams`/`normalizeEditParams` с цветом, - `applyColorToPixels`. + `readEditParams`/`isEmptyEditParams`/`normalizeEditParams`, `applyColorToPixels`, + `whiteBalanceFromRGB`. - `frontend/src/lib/light-wasm.ts` — вызов WASM для цвета, поддержка паритета. -- `frontend/src/lib/components/PhotoEditPanel.svelte` — новый раздел «Цвет». -- `frontend/src/lib/components/Viewer.svelte` — превью с цветом. +- `frontend/src/lib/webgl-preview.ts` — WebGL2-шейдер и рендер превью. +- `frontend/src/lib/stores/eyedropper.svelte.ts` — режим пипетки. +- `frontend/src/lib/components/PhotoEditPanel.svelte` — раздел «Цвет». +- `frontend/src/lib/components/EditSlider.svelte`, `ColorWheel.svelte` — контролы. +- `frontend/src/lib/components/Viewer.svelte` — live-превью (WebGL/CPU) и сэмплирование пипетки. +- `frontend/src/lib/components/PhotoPanel.svelte` — передача признака просмотрщика. - `frontend/wasm/light/` (Rust) + `static/light.wasm` — цветовые операции. -- Тесты: `edit_test.go`, `edit.test.ts`, `light-wasm.test.ts`. +- Тесты: `edit_test.go`, `edit.test.ts`, `light-wasm.test.ts`, + `backend/internal/e2e/color_edit_test.go`. diff --git a/openspec/changes/photo-color-editing/specs/photos/spec.md b/openspec/changes/archive/2026-10-23-photo-color-editing/specs/photos/spec.md similarity index 58% rename from openspec/changes/photo-color-editing/specs/photos/spec.md rename to openspec/changes/archive/2026-10-23-photo-color-editing/specs/photos/spec.md index 57721a0..541d36b 100644 --- a/openspec/changes/photo-color-editing/specs/photos/spec.md +++ b/openspec/changes/archive/2026-10-23-photo-color-editing/specs/photos/spec.md @@ -34,9 +34,37 @@ - **THEN** система отклоняет запрос #### Scenario: Цветовые правки сохраняются и рендерятся -- **WHEN** пользователь сохраняет цветовые правки фото (баланс белого, вибрацию, насыщенность, цветовой микшер или точечный цвет) +- **WHEN** пользователь сохраняет цветовые правки фото: баланс белого (температура/оттенок), сочность (vibrance), насыщенность, цветовой микшер по оттенкам, цветокоррекцию (тени/средние тона/света, яркость зон, наложение, баланс) или поканальную коррекцию - **THEN** параметры цвета сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов #### Scenario: Цветовые правки применяются к пикселям исходного объекта - **WHEN** готовится отредактированная версия фото с цветовыми правками - **THEN** цветовые правки применяются к пикселям исходного объекта после световых правок, а не к его уменьшенной копии + +#### Scenario: Пустые цветовые правки не сохраняются +- **WHEN** в цветовых параметрах заданы только наложение/баланс или оттенок колеса без силы и яркости +- **THEN** правки считаются пустыми и фото не получает цветовую версию + +## ADDED Requirements + +### Requirement: Пипетка баланса белого +Клиент SHALL позволять выбрать нейтральную точку на фото и SHALL устанавливать температуру и оттенок так, чтобы цвет этой точки стал нейтральным. Выборка SHALL браться из исходного (неотредактированного) превью, а пресет баланса белого SHALL переходить в «Пользовательский». + +#### Scenario: Выбор нейтральной точки +- **WHEN** пользователь включает пипетку и кликает по нейтральному участку фото +- **THEN** температура и оттенок обновляются, и цвет выбранной точки становится нейтральным + +#### Scenario: Пипетка доступна только в просмотрщике +- **WHEN** фото не открыто в просмотрщике +- **THEN** пипетка недоступна + +### Requirement: Живой предпросмотр цветовых правок +Клиент SHALL показывать результат цветовых правок в просмотрщике до завершения серверного рендера. После завершения рендера SHALL показываться серверная версия. + +#### Scenario: Правки видны сразу +- **WHEN** пользователь двигает цветовой контрол +- **THEN** просмотрщик обновляет изображение без ожидания серверного рендера + +#### Scenario: Серверная версия заменяет предпросмотр +- **WHEN** серверный рендер цветовых правок завершён +- **THEN** просмотрщик показывает отрендеренную версию diff --git a/openspec/changes/archive/2026-10-23-photo-color-editing/tasks.md b/openspec/changes/archive/2026-10-23-photo-color-editing/tasks.md new file mode 100644 index 0000000..c467687 --- /dev/null +++ b/openspec/changes/archive/2026-10-23-photo-color-editing/tasks.md @@ -0,0 +1,59 @@ +# Tasks + +## 1. Backend: цветовая модель и рендер (Go) + +- [x] 1.1 Добавить `ColorParams` и `EditParams.Color` в `backend/internal/photos/edit.go`, обновить `Normalized`/`IsEmpty`/`JSON` с учетом цвета — проверить `go build ./internal/photos/` +- [x] 1.2 Реализовать `applyColor` / `applyColorRows` и включить цвет в `decodeAndAdjust` после света (`backend/internal/photos/edit_render.go`) — проверить `go test ./internal/photos/ -run TestColor -v` +- [x] 1.3 Добавить тесты цветовых параметров (`edit_test.go`): валидация диапазонов, `IsEmpty`, рендер с цветом — проверить `go test ./internal/photos/ -v` + +## 2. Фронтенд: цветовая модель (TypeScript) + +- [x] 2.1 Добавить `ColorParams`, цветовые ползунки, `emptyColor`, обновить `readEditParams`/`isEmptyEditParams`/`normalizeEditParams` в `frontend/src/lib/edit.ts` — проверить `npm run check` (в `frontend`) +- [x] 2.2 Реализовать `applyColorToPixels` в `edit.ts`, зеркальное серверной математике — проверить `npm run test` (в `frontend`, `edit.test.ts`) +- [x] 2.3 Поддержать цвет в `light-wasm.ts` и тест паритета `wasmMatchesReference` — проверить `npm run test` (в `frontend`, `light-wasm.test.ts`) + +## 3. WASM модуль цвета (Rust) + +- [x] 3.1 Реализовать цветовые операции (баланс белого, вибрация, насыщенность, микшер, точечный цвет) в `frontend/wasm/light/` — проверить сборку модуля +- [x] 3.2 Пересобрать `static/light.wasm` — проверить файл обновился + +## 4. UI: панель и превью цвета + +- [x] 4.1 Добавить раздел «Цвет» в `PhotoEditPanel.svelte` (баланс белого, сочность, насыщенность, микшер, цветокоррекция) — проверить открытие/сворачивание панели +- [x] 4.2 Поддержать цвет в live-превью `Viewer.svelte` — проверить изменение превью при движении ползунков +- [x] 4.3 Подключить сохранение цветовых параметров через `photosApi.edit` — проверить сохранение на сервере + +## 5. Интеграция и проверка + +- [x] 5.1 Проверить эндпоинт правки (`PATCH /api/photos/:id/edit`) с цветовыми параметрами — проверить ответ с `edit_url` +- [x] 5.2 Проверить сброс цветовых правок — проверить возврат к оригиналу +- [x] 5.3 Проверить обратную совместимость: фото без ключа `color` в `edit_params` рендерятся без цветовых правок — проверить `make test` (бэкенд) + +## 6. WebGL-предпросмотр + +- [x] 6.1 Реализовать `frontend/src/lib/webgl-preview.ts`: WebGL2-шейдер света/цвета, LUT-текстура кривых, загрузка текстуры изображения — проверить сборку `npm run build` +- [x] 6.2 Подключить GL-first и CPU/WASM-фолбэк в `Viewer.svelte` (два канваса, `useGL`) — проверить переключение при отсутствии WebGL2 +- [x] 6.3 Проверить компиляцию и линковку шейдера в headless Firefox — зелёный экран `OK` +- [x] 6.4 Пропускать свет, если он не менялся, и микшер, если он пуст — проверить `go test ./internal/photos/ ./internal/e2e/` +- [x] 6.5 Снизить стоимость CPU-фолбэка: адаптивный буфер, беззвучный быстрый путь без HSL и аддитивная яркость — проверить замер WASM/визуально + +## 7. Пипетка баланса белого + +- [x] 7.1 Добавить `frontend/src/lib/stores/eyedropper.svelte.ts` (режим, фото, сэмпл) — проверить `npm run check` +- [x] 7.2 Добавить `whiteBalanceFromRGB` в `edit.ts` (обратная задача баланса белого) — проверить `npm run test` (`edit.test.ts`) +- [x] 7.3 Сэмплировать пиксель базового превью в `Viewer.svelte` по клику — проверить ручным сценарием +- [x] 7.4 Подключить кнопку пипетки и подсказку в `PhotoEditPanel.svelte`, пресет «Пользовательский» — проверить `npm run check` +- [x] 7.5 Покрыть расчёт нейтральной точки тестом — проверить `npm run test` + +## 8. UI и тональные правки + +- [x] 8.1 Добавить `EditSlider.svelte` (градиентная/заполненная дорожка, двойной клик — сброс) — проверить `npm run check` +- [x] 8.2 Добавить `ColorWheel.svelte` (оттенок/насыщенность, двойной клик — сброс) — проверить открытие/вращение +- [x] 8.3 Переименовать «Красивость» → «Сочность» — проверить подпись ползунка +- [x] 8.4 Сузить веса зон теней/светов и сделать яркость аддитивной — проверить отсутствие резкой границы на контрастном снимке +- [x] 8.5 Добавить пресеты баланса белого и «Пользовательский» — проверить переключение пресетов + +## Workflow follow-up + +- Архивировать изменение после удовлетворения требований ревью проекта. +- Проверить архивированный результат. diff --git a/openspec/changes/photo-color-editing/design.md b/openspec/changes/photo-color-editing/design.md deleted file mode 100644 index f46d82a..0000000 --- a/openspec/changes/photo-color-editing/design.md +++ /dev/null @@ -1,88 +0,0 @@ -# Design - -## Context - -Неразрушающее редактирование фото уже реализовано: параметры правок хранятся в -`photo.edit_params` (`map[string]any` JSON-блоб), сервер рендерит отредактированную -копию как отдельные объекты ролей `edit`, `edit_thumb`, `edit_medium`, -`edit_preview` рядом с исходными, оригинал не меняется. На сегодня поддерживается -только блок «Свет» (`LightParams` в `backend/internal/photos/edit.go`, -зеркало на клиенте `frontend/src/lib/edit.ts`, живой превью через -`frontend/wasm/light/` → `static/light.wasm` с тестом паритета -`wasmMatchesReference`). Хранение в JSON-блобе обратноли не требует миграции: -старые фото без ключа `color` рендерятся без цветовых правок. - -## Goals / Non-Goals - -**Goals:** -- Добавить блок «Цвет» к неразрушающему редактированию: баланс белого - (Темп, Оттенок), Вибрация, Насыщенность, Цветовой микшер по оттенкам, - Точечный цвет, Цветокоррекция. -- Живой клиентский превью с паритетом трёх реализаций: сервер Go, клиент TypeScript, - WASM (Rust) — как для «Света». -- Рендер цветовых правок по пикселям исходного объекта, после световых правок. -- Обратная совместимость: фото без ключа `color` в `edit_params` рендерятся как - прежде. - -**Non-Goals:** -- Синхронизация правок между устройствами или через Android-клиент. -- Изменение ролей хранения или формата объектов (`edit`, `edit_thumb`, … остаются). -- Миграция схемы базы данных. - -## Decisions - -**Цвет хранится в том же `edit_params`, рядом с `light`.** -- Обоснование: `EditParams` уже расширяется полем `Color ColorParams`; `map[string]any` - на бэкенде и `EditParams` на клиенте принимают новый ключ без cambios схемы. -- Альтернатива: отдельная колонка/таблица для цветовых правок — отвергнута, добавила бы - миграцию и не сочетается с текущей моделью хранения. - -**Порядок операций при рендере: свет, затем цвет.** -- Обоснование: цветовые коррекции (баланс белого, насыщенность) логично применять - к уже скорректированной по тональности картинке; сервер и клиент должны следовать - одному порядку, иначе превью не совпадёт с результатом. -- Альтернатива: цвет до света — отвергнуто, несоответствие Lightroom-поведению и - текущему клиентскому конвейеру. - -**Цветовая математика реализована трижды и держится в паритете: Go (сервер), -TypeScript (клиент), Rust/WASM (живой превью).** -- Обоснование: текущий подход со «Светом» (`applyLight` в Go, `applyLightToPixels` - в TS, `light.wasm` в Rust, тест `wasmMatchesReference`) гарантирует, что превью - в браузере совпадает с финальным рендером. Для цвета повторяем эту схему. -- Альтернатива: считать цвет только на сервере и показывать превью после рендера — - отвергнуто, пользователь ожидает мгновенного отклика при движении ползунков, как - сейчас со светом. - -**Цветовые параметры применяются в пространстве RGB с промежуточным HSL.** -- Обоснование: баланс белого, вибрация, насыщенность и тонирование естественно - выражаются через HSL; пиксели хранятся в RGBA, поэтому пересчёт RGB→HSL→RGB - на пиксельном цикле. -- Альтернатива: чистые операции над RGB-каналами — отвергнуто, менее точно - соответствует цветовым инструментам Lightroom. - -## Risks / Trade-offs - -[Расхождение реализаций Go / TS / Rust] → mitigation: тест паритета -`wasmMatchesReference` расширяется на цвет, изменение любой реализации требует -обновления остальных и теста. - -[Сложность UI] → mitigation: цветовой микшер по 8 оттенкам и точечное тонирование -добавляют много элементов управления в `PhotoEditPanel`; раздел «Цвет» делаем -сворачиваемым, как «Свет». - -[Производительность рендера] → mitigation: цвет добавляет один пиксельный проход -(или объединяется с существующим циклом света); параллелизация по строкам -(`applyLightRows`) остаётся. - -[Обратная совместимость] → mitigation: отсутствие ключа `color` в `edit_params` -трактуется как нулевые цветовые правки; старые фото не затрагиваются. - -## Migration Plan - -Миграция схемы не требуется: `edit_params` — JSON-блоб, новый ключ `color` -дополняет существующий `light`. Откат: удаление ключа `color` из `edit_params` -возвращает фото к состоянию без цветовых правок. - -## Open Questions - -Нет — все проектные решения (объём цвета, живой превью с паритетом) согласованы. diff --git a/openspec/changes/photo-color-editing/tasks.md b/openspec/changes/photo-color-editing/tasks.md deleted file mode 100644 index c4853a7..0000000 --- a/openspec/changes/photo-color-editing/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -# Tasks - -## 1. Backend: цветовая модель и рендер (Go) - -- [x] 1.1 Добавить `ColorParams` и `EditParams.Color` в `backend/internal/photos/edit.go`, обновить `Normalized`/`IsEmpty`/`JSON` с учетом цвета — проверить `go build ./internal/photos/` -- [x] 1.2 Реализовать `applyColor` / `applyColorRows` и включить цвет в `decodeAndAdjust` после света (`backend/internal/photos/edit_render.go`) — проверить `go test ./internal/photos/ -run TestColor -v` -- [x] 1.3 Добавить тесты цветовых параметров (`edit_test.go`): валидация диапазонов, `IsEmpty`, рендер с цветом — проверить `go test ./internal/photos/ -v` - -## 2. Фронтенд: цветовая модель (TypeScript) - -- [x] 2.1 Добавить `ColorParams`, цветовые ползунки, `emptyColor`, обновить `readEditParams`/`isEmptyEditParams`/`normalizeEditParams` в `frontend/src/lib/edit.ts` — проверить `npm run check` (в `frontend`) -- [x] 2.2 Реализовать `applyColorToPixels` в `edit.ts`, зеркальное серверной математике — проверить `npm run test` (в `frontend`, `edit.test.ts`) -- [x] 2.3 Поддержать цвет в `light-wasm.ts` и тест паритета `wasmMatchesReference` — проверить `npm run test` (в `frontend`, `light-wasm.test.ts`) - -## 3. WASM модуль цвета (Rust) - -- [x] 3.1 Реализовать цветовые операции (баланс белого, вибрация, насыщенность, микшер, точечный цвет) в `frontend/wasm/light/` — проверить сборку модуля -- [x] 3.2 Пересобрать `static/light.wasm` — проверить файл обновился - -## 4. UI: панель и превью цвета - -- [x] 4.1 Добавить раздел «Цвет» в `PhotoEditPanel.svelte` (баланс белого, вибрация, насыщенность, микшер, точечный цвет) — проверить открытие/сворачивание панели -- [x] 4.2 Поддержать цвет вライブ-превью `Viewer.svelte` через `applyLight` + `applyColor` — проверить изменение превью при движении ползунков -- [x] 4.3 Подключить сохранение цветовых параметров через `photosApi.edit` — проверить сохранение на сервере - -## 5. Интеграция и проверка - -- [x] 5.1 Проверить эндпоинт правки (`POST /api/photos/:id/edit`) с цветовыми параметрами — проверить ответ с `edit_url` -- [x] 5.2 Проверить сброс цветовых правок — проверить возврат к оригиналу -- [x] 5.3 Проверить обратную совместимость: фото без ключа `color` в `edit_params` рендерятся без цветовых правок — проверить `make test` (бэкенд) - -## Workflow follow-up - -- Архивировать изменение после удовлетворения требований ревью проекта. -- Проверить архивированный результат. diff --git a/openspec/specs/photos/spec.md b/openspec/specs/photos/spec.md index 7e278a5..bcdc304 100644 --- a/openspec/specs/photos/spec.md +++ b/openspec/specs/photos/spec.md @@ -283,16 +283,10 @@ SHALL быть доступно и для фото без альбома (нап - **THEN** контрольные суммы входят в данные полученных фото ### Requirement: Неразрушающее редактирование фотографии -Система SHALL поддерживать неразрушающее редактирование фотографий. Параметры правок -SHALL храниться в метаданных фото, а отредактированная версия SHALL создаваться как -отдельные объекты (роли `edit`, `edit_thumb`, `edit_medium`, `edit_preview`) рядом с -исходными вариантами. Рендер SHALL выполняться по исходному объекту фото, а не по -его уменьшенным вариантам. Исходный объект SHALL оставаться неизменным, а скачивание -SHALL отдавать оригинал. Сброс правок SHALL удалять и параметры, и отредактированную -версию. Правки SHALL применять только к изображениям. +Система SHALL поддерживать неразрушающее редактирование фотографий. Параметры правок (свет и цвет) SHALL храниться в метаданных фото, а отредактированная версия SHALL создаваться как отдельные объекты (роли `edit`, `edit_thumb`, `edit_medium`, `edit_preview`) рядом с исходными вариантами. Рендер SHALL выполняться по исходному объекту фото, а не по его уменьшенным вариантам. Исходный объект SHALL оставаться неизменным, а скачивание SHALL отдавать оригинал. Сброс правок SHALL удалять и параметры, и отредактированную версию. Правки SHALL применяться только к изображениям. #### Scenario: Сохранение правок -- **WHEN** пользователь сохраняет правки фото +- **WHEN** пользователь сохраняет правки фото (свет и/или цвет) - **THEN** параметры сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов #### Scenario: Фоновая подготовка версий @@ -301,7 +295,7 @@ SHALL отдавать оригинал. Сброс правок SHALL удал #### Scenario: Рендер по оригиналу - **WHEN** готовится отредактированная версия фото -- **THEN** правки применяются к пикселям исходного объекта, а не к его уменьшенной копии +- **THEN** световые и цветовые правки применяются к пикселям исходного объекта, а не к его уменьшенной копии #### Scenario: Неразрушачность - **WHEN** фото отредактировано @@ -318,3 +312,37 @@ SHALL отдавать оригинал. Сброс правок SHALL удал #### Scenario: Редактирование видео - **WHEN** пользователь пытается отредактировать видео - **THEN** система отклоняет запрос + +#### Scenario: Цветовые правки сохраняются и рендерятся +- **WHEN** пользователь сохраняет цветовые правки фото: баланс белого (температура/оттенок), сочность (vibrance), насыщенность, цветовой микшер по оттенкам, цветокоррекцию (тени/средние тона/света, яркость зон, наложение, баланс) или поканальную коррекцию +- **THEN** параметры цвета сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов + +#### Scenario: Цветовые правки применяются к пикселям исходного объекта +- **WHEN** готовится отредактированная версия фото с цветовыми правками +- **THEN** цветовые правки применяются к пикселям исходного объекта после световых правок, а не к его уменьшенной копии + +#### Scenario: Пустые цветовые правки не сохраняются +- **WHEN** в цветовых параметрах заданы только наложение/баланс или оттенок колеса без силы и яркости +- **THEN** правки считаются пустыми и фото не получает цветовую версию + +### Requirement: Пипетка баланса белого +Клиент SHALL позволять выбрать нейтральную точку на фото и SHALL устанавливать температуру и оттенок так, чтобы цвет этой точки стал нейтральным. Выборка SHALL браться из исходного (неотредактированного) превью, а пресет баланса белого SHALL переходить в «Пользовательский». + +#### Scenario: Выбор нейтральной точки +- **WHEN** пользователь включает пипетку и кликает по нейтральному участку фото +- **THEN** температура и оттенок обновляются, и цвет выбранной точки становится нейтральным + +#### Scenario: Пипетка доступна только в просмотрщике +- **WHEN** фото не открыто в просмотрщике +- **THEN** пипетка недоступна + +### Requirement: Живой предпросмотр цветовых правок +Клиент SHALL показывать результат цветовых правок в просмотрщике до завершения серверного рендера. После завершения рендера SHALL показываться серверная версия. + +#### Scenario: Правки видны сразу +- **WHEN** пользователь двигает цветовой контрол +- **THEN** просмотрщик обновляет изображение без ожидания серверного рендера + +#### Scenario: Серверная версия заменяет предпросмотр +- **WHEN** серверный рендер цветовых правок завершён +- **THEN** просмотрщик показывает отрендеренную версию