diff --git a/openspec/changes/photo-color-editing/.openspec.yaml b/openspec/changes/photo-color-editing/.openspec.yaml new file mode 100644 index 0000000..f56fe13 --- /dev/null +++ b/openspec/changes/photo-color-editing/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-23 diff --git a/openspec/changes/photo-color-editing/design.md b/openspec/changes/photo-color-editing/design.md new file mode 100644 index 0000000..f46d82a --- /dev/null +++ b/openspec/changes/photo-color-editing/design.md @@ -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 + +Нет — все проектные решения (объём цвета, живой превью с паритетом) согласованы. diff --git a/openspec/changes/photo-color-editing/proposal.md b/openspec/changes/photo-color-editing/proposal.md new file mode 100644 index 0000000..f49ee94 --- /dev/null +++ b/openspec/changes/photo-color-editing/proposal.md @@ -0,0 +1,55 @@ +# Proposal + +## Why + +В инструменте правок фото сейчас только раздел «Свет» (экспозиция, контраст, света, +тени, белые, чёрные, точечная кривая). Пользователи не могут скорректировать цвет: +баланс белого, вибрацию, насыщенность, отдельные оттенки и тонирование. Добавить +цветовые правки — востребованное расширение неразрушающего редактирования, которое +уже работает в Galo. + +## What Changes + +- Расширить `EditParams` новым блоком `Color`: баланс белого (Темп, Оттенок), + Вибрация (Vibrance), Насыщенность, Цветовой микшер (Тон/Насыщенность/Яркость + по оттенкам), Точечный цвет и Цветокоррекция. +- Сервер (Go): рендер цветовых правок на пикселях исходного объекта, порядок + операций — свет, затем цвет; результат записывается в те же роли `edit`, + `edit_thumb`, `edit_medium`, `edit_preview`. +- Клиент (TypeScript): цветовые ползунки и элементы управления в + `PhotoEditPanel`, живой превью через `light.wasm` с тестом паритета + (`wasmMatchesReference`). +- WASM (Rust): расширение `frontend/wasm/light/` цветовыми операциями и + пересборка `static/light.wasm`. +- Хранение: `photo.edit_params` остаётся `map[string]any` JSON-блоба — старые + фото без ключа `color` рендерятся без цветовых правок, миграций схемы не + требуется. + +## Capabilities + +### Modified Capabilities + +- `photos`: требование «Неразрушающее редактирование фотографии» расширяется + цветовыми параметрами правок. Потребуется дельта-спека + `specs/photos/spec.md` в рамках этого изменения. + +### New Capabilities + +Нет — цветовое редактирование входит в существующую способность `photos`. + +## Impact + +- `backend/internal/photos/edit.go` — `ColorParams`, `EditParams.Color`, + `Normalized`/`IsEmpty`/`JSON`. +- `backend/internal/photos/edit_render.go` — `applyColor` / `applyColorRows`, + `decodeAndAdjust` применяет свет и цвет. +- `backend/internal/photos/edit_service.go` — валидация и сохранение без + изменений логики (параметр `color` добавится в JSON). +- `frontend/src/lib/edit.ts` — `ColorParams`, цветовые ползунки, + `readEditParams`/`isEmptyEditParams`/`normalizeEditParams` с цветом, + `applyColorToPixels`. +- `frontend/src/lib/light-wasm.ts` — вызов WASM для цвета, поддержка паритета. +- `frontend/src/lib/components/PhotoEditPanel.svelte` — новый раздел «Цвет». +- `frontend/src/lib/components/Viewer.svelte` — превью с цветом. +- `frontend/wasm/light/` (Rust) + `static/light.wasm` — цветовые операции. +- Тесты: `edit_test.go`, `edit.test.ts`, `light-wasm.test.ts`. diff --git a/openspec/changes/photo-color-editing/specs/photos/spec.md b/openspec/changes/photo-color-editing/specs/photos/spec.md new file mode 100644 index 0000000..57721a0 --- /dev/null +++ b/openspec/changes/photo-color-editing/specs/photos/spec.md @@ -0,0 +1,42 @@ +# Spec Delta + +## MODIFIED Requirements + +### Requirement: Неразрушающее редактирование фотографии +Система SHALL поддерживать неразрушающее редактирование фотографий. Параметры правок (свет и цвет) SHALL храниться в метаданных фото, а отредактированная версия SHALL создаваться как отдельные объекты (роли `edit`, `edit_thumb`, `edit_medium`, `edit_preview`) рядом с исходными вариантами. Рендер SHALL выполняться по исходному объекту фото, а не по его уменьшенным вариантам. Исходный объект SHALL оставаться неизменным, а скачивание SHALL отдавать оригинал. Сброс правок SHALL удалять и параметры, и отредактированную версию. Правки SHALL применяться только к изображениям. + +#### Scenario: Сохранение правок +- **WHEN** пользователь сохраняет правки фото (свет и/или цвет) +- **THEN** параметры сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов + +#### Scenario: Фоновая подготовка версий +- **WHEN** правки фото сохранены +- **THEN** система готовит варианты сетки и полноразмерную версию в фоне и сообщает о готовности + +#### Scenario: Рендер по оригиналу +- **WHEN** готовится отредактированная версия фото +- **THEN** световые и цветовые правки применяются к пикселям исходного объекта, а не к его уменьшенной копии + +#### Scenario: Неразрушачность +- **WHEN** фото отредактировано +- **THEN** исходный объект и его варианты остаются неизменными + +#### Scenario: Сброс правок +- **WHEN** пользователь сбрасывает правки +- **THEN** параметры и отредактированная версия удаляются, а фото снова показывается в исходном виде + +#### Scenario: Права на правки +- **WHEN** правки сохраняет пользователь без прав на это фото +- **THEN** система отклоняет запрос + +#### Scenario: Редактирование видео +- **WHEN** пользователь пытается отредактировать видео +- **THEN** система отклоняет запрос + +#### Scenario: Цветовые правки сохраняются и рендерятся +- **WHEN** пользователь сохраняет цветовые правки фото (баланс белого, вибрацию, насыщенность, цветовой микшер или точечный цвет) +- **THEN** параметры цвета сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов + +#### Scenario: Цветовые правки применяются к пикселям исходного объекта +- **WHEN** готовится отредактированная версия фото с цветовыми правками +- **THEN** цветовые правки применяются к пикселям исходного объекта после световых правок, а не к его уменьшенной копии diff --git a/openspec/changes/photo-color-editing/tasks.md b/openspec/changes/photo-color-editing/tasks.md new file mode 100644 index 0000000..c4853a7 --- /dev/null +++ b/openspec/changes/photo-color-editing/tasks.md @@ -0,0 +1,35 @@ +# 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 + +- Архивировать изменение после удовлетворения требований ревью проекта. +- Проверить архивированный результат.