docs(openspec): изменение photo-color-editing

Предложение, дельта-спека photos, дизайн и задачи для инструмента «Цвет».
This commit is contained in:
iu
2026-10-23 21:57:51 +03:00
parent d791b9a572
commit 502f2766d7
5 changed files with 222 additions and 0 deletions
@@ -0,0 +1,88 @@
# 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
Нет — все проектные решения (объём цвета, живой превью с паритетом) согласованы.