- photos: признак незавершённого рендера правок, мгновенный показ сброса, бесшовная подмена предпросмотра - realtime: события жизненного цикла рендера правок в photo.updated
50 KiB
photos Specification
Purpose
Загрузка, хранение в S3 и просмотр фотографий с генерацией вариантов и извлечением EXIF.
Requirements
Requirement: Загрузка фото
Пользователь с правом загрузки SHALL иметь возможность загружать медиа (изображения и
видео) по одному и массово; система SHALL принимать ввод image/* и video/*.
Scenario: Успешная загрузка
- WHEN участник с правом загрузки отправляет файл изображения
- THEN система сохраняет оригинал и создаёт запись фото с метаданными
Scenario: Успешная загрузка видео
- WHEN участник с правом загрузки отправляет видеофайл
- THEN система сохраняет оригинал и создаёт медиа-запись с признаком видео
Scenario: Загрузка без права
- WHEN наблюдатель пытается загрузить фото
- THEN система отклоняет запрос
Scenario: Неподдерживаемый файл
- WHEN загружается файл, не являющийся изображением или видео
- THEN система отклоняет загрузку
Requirement: Варианты изображений
Система SHALL генерировать четыре варианта: thumb (200×200, квадратный кроп), medium (800×800, квадратный кроп), preview (1200×1200, вписанный) и оригинал, в WebP, и извлекать EXIF (дата, координаты, размеры).
Scenario: Генерация вариантов
- WHEN фото загружено
- THEN система асинхронно генерирует thumb, medium и preview и заполняет EXIF-поля
Scenario: Форма вариантов сетки
- WHEN генерируются thumb и medium
- THEN они обрезаны до квадрата, а preview вписан с сохранением пропорций
Requirement: Листинг фото с пагинацией
Система SHALL возвращать фото альбома с cursor-пагинацией.
Scenario: Пагинация по курсору
- WHEN клиент запрашивает список с курсором и лимитом
- THEN система возвращает не более лимита фото и курсор следующей страницы
Requirement: Получение фото по требованию
Клиент SHALL иметь возможность получить актуальные данные фото с подписанным URL, обслуживаемым бэкендом, независимо от бэкенда хранения. Подпись SHALL быть детерминированной в пределах окна действия, чтобы повторные выдачи одного и того же объекта давали идентичный URL.
Scenario: Выдача свежего URL
- WHEN клиент запрашивает фото по id
- THEN система возвращает подписанный URL бэкенда для просмотра
Scenario: Повторная выдача совпадает
- WHEN один и тот же объект запрашивается повторно в пределах окна действия подписи
- THEN система возвращает идентичный URL
Requirement: Скачивание одиночного фото
Любой участник, включая viewer, SHALL иметь возможность скачать одиночное фото.
Scenario: Скачивание фото
- WHEN участник запрашивает скачивание фото
- THEN система возвращает подписанный URL оригинала, обслуживаемый бэкендом
Requirement: Удаление фото
Автор SHALL иметь возможность удалить своё фото; владелец и админ SHALL иметь возможность удалить любое фото. Удаление SHALL помещать фото в корзину и SHALL не удалять объекты хранилища.
Scenario: Удаление своего фото
- WHEN автор удаляет своё фото
- THEN фото помещается в корзину, а объекты хранилища сохраняются
Scenario: Удаление чужого фото участником
- WHEN участник с ролью member пытается удалить чужое фото
- THEN система отклоняет запрос
Requirement: Обложка по умолчанию
При загрузке фото система SHALL назначать его обложкой альбома, если обложка ещё не задана, то есть обложкой становится первое загруженное фото. Когда фото, выбранное обложкой, покидает альбом (помещено в корзину или перенесено в другой альбом), система SHALL снять обложку.
Scenario: Первое фото
- WHEN фото загружено, и у альбома нет обложки
- THEN система назначает это фото обложкой альбома
Scenario: Обложка уже задана
- WHEN фото загружено, а обложка уже задана
- THEN система не меняет обложку
Scenario: Фото-обложка в корзине
- WHEN фото, назначенное обложкой, помещается в корзину
- THEN система снимает обложку, и альбом отображается без обложки
Scenario: Фото-обложка перенесено
- WHEN фото, назначенное обложкой, переносится в другой альбом
- THEN система снимает обложку исходного альбома
Requirement: Переименование фото
Автор фото, владелец и админ альбома SHALL иметь возможность изменить имя файла фото; остальные SHALL получать отказ, а пустое имя SHALL отклоняться.
Scenario: Переименование автором
- WHEN автор меняет имя файла своего фото на непустое
- THEN система сохраняет новое имя и транслирует изменение подписчикам альбома
Scenario: Пустое имя
- WHEN пользователь подтверждает пустое имя файла
- THEN система отклоняет запрос
Scenario: Нет прав
- WHEN участник без прав переименовывает чужое фото
- THEN система отклоняет запрос
Requirement: Ограничение размера загрузки
Система SHALL ограничивать размер загружаемых файлов отдельно для изображений и видео и SHALL отвечать понятной ошибкой при превышении лимита, а не ошибкой разбора формы.
Scenario: Превышение лимита
- WHEN загружаемый файл превышает максимальный размер
- THEN система отклоняет загрузку с явной ошибкой о превышении размера
Scenario: Превышение лимита видео
- WHEN загружаемое видео превышает лимит видео
- THEN система отклоняет загрузку с явной ошибкой о превышении размера
Scenario: Файл в пределах лимита
- WHEN файл не превышает лимит
- THEN система принимает multipart-загрузку и обрабатывает фото
Requirement: Метаданные фото (EXIF)
При загрузке фото система SHALL извлекать распознаваемые EXIF-атрибуты и SHALL возвращать их вместе с данными фото; при отсутствии метаданных SHALL возвращать пустой набор.
Scenario: Фото с метаданными
- WHEN загружается фото с EXIF
- THEN данные фото содержат извлечённые атрибуты (камера, экспозиция, ISO, дата съёмки и др.)
Scenario: Фото без метаданных
- WHEN загружается фото без читаемых EXIF
- THEN данные фото не содержат атрибутов, а загрузка выполняется успешно
Requirement: Перенос и копирование фото между альбомами
Пользователь с правом удаления в исходном альбоме и правом загрузки в целевом SHALL иметь возможность перенести или скопировать фото в другой альбом. Операции SHALL изменять только метаданные и SHALL не изменять объекты хранилища.
Scenario: Перенос
- WHEN пользователь переносит фото в другой альбом
- THEN фото исчезает из исходного альбома и появляется в целевом, объекты хранилища не меняются
Scenario: Копирование
- WHEN пользователь копирует фото в другой альбом
- THEN в целевом альбоме появляется новая запись, ссылающаяся на те же объекты хранилища
Requirement: Корзина фото
Удаление фото SHALL помещать его в корзину (мягкое удаление). Восстановление SHALL возвращать фото в его альбом. Окончательное удаление SHALL удалять запись, а объекты хранилища SHALL удаляться только когда на них не остаётся ссылок ни в одном альбоме. Окончательное удаление SHALL быть доступно и для фото без альбома (например, после удаления альбома).
Scenario: Удаление в корзину
- WHEN пользователь удаляет фото
- THEN фото пропадает из альбома и появляется в корзине
Scenario: Восстановление
- WHEN пользователь восстанавливает фото из корзины
- THEN фото возвращается в свой альбом
Scenario: Окончательное удаление без ссылок
- WHEN пользователь удаляет фото из корзины навсегда, и на те же объекты нет ссылок в других альбомах
- THEN запись и объекты хранилища удаляются
Scenario: Окончательное удаление при наличии ссылок
- WHEN на объект хранилища ссылается копия в другом альбоме
- THEN запись удаляется, а объект хранилища сохраняется
Scenario: Окончательное удаление фото без альбома
- WHEN пользователь удаляет навсегда фото из корзины, у которого нет альбома
- THEN запись удаляется, а операция не завершается ошибкой отсутствующего альбома
Requirement: Удаление альбома и восстановление фото без альбома
Удаление альбома SHALL помещать его фото в корзину, не удаляя объекты хранилища. Если при восстановлении фото его альбом отсутствует, система SHALL предложить выбрать существующий альбом или создать новый.
Scenario: Удаление альбома
- WHEN владелец удаляет альбом
- THEN его фото оказываются в корзине без привязки к альбому, файлы сохраняются
Scenario: Восстановление без альбома
- WHEN пользователь восстанавливает фото, у которого нет альбома
- THEN система предлагает выбрать альбом или создать новый и восстанавливает фото в него
Requirement: Метки фото
Пользователь SHALL присваивать фото свои метки (область — пользователь): создавать новые и выбирать из списка с поиском; метки SHALL отдаваться вместе с фото и сниматься.
Scenario: Создание метки
- WHEN пользователь вводит новую метку и подтверждает
- THEN метка создаётся и привязывается к фото
Scenario: Выбор из списка с поиском
- WHEN пользователь вводит часть названия метки
- THEN показываются подходящие метки пользователя для выбора
Scenario: Снятие метки
- WHEN пользователь убирает метку с фото
- THEN метка отвязывается от фото
Requirement: Кэширование объектов
Доставка объектов SHALL помечать ответы как кэшируемые, чтобы браузер переиспользовал уже полученные превью, а повторное открытие альбома не вызывало повторную загрузку неизменившихся объектов.
Scenario: Заголовки кэширования
- WHEN клиент получает объект (оригинал, превью или thumb) по подписанному URL
- THEN ответ содержит заголовок кэширования с ограниченным сроком годности
Scenario: Повторный показ без перезагрузки
- WHEN сетка превью перерисовывается, а URL объектов не изменились
- THEN браузер отдаёт объекты из кэша без повторного запроса к бэкенду
Requirement: Медиа-запись фото
Данные фото SHALL включать вид медиа (image | video), длительность для видео и адрес
воспроизведения, когда видео-вариант готов; для изображений адрес воспроизведения
отсутствует.
Scenario: Запись изображения
- WHEN клиент получает данные изображения
- THEN вид медиа — изображение, длительность и адрес воспроизведения отсутствуют
Scenario: Запись видео без готового варианта
- WHEN клиент получает данные видео, а вариант ещё не готов
- THEN вид медиа — видео, длительность присутствует, адрес воспроизведения отсутствует
Scenario: Запись видео с готовым вариантом
- WHEN видео-вариант готов
- THEN данные медиа содержат адрес воспроизведения
Requirement: Очистка корзины
Пользователь SHALL иметь возможность окончательно удалить все элементы корзины одним действием. Система SHALL удалять только те элементы, которые пользователь вправе удалить навсегда, и SHALL пропускать остальные. Операция SHALL возвращать число удалённых элементов.
Scenario: Очистка корзины
- WHEN пользователь очищает корзину
- THEN все элементы, которые он вправе удалить навсегда, удаляются
Scenario: Чужие элементы
- WHEN в корзине есть фото, которые пользователь не вправе удалить навсегда
- THEN они остаются в корзине, а остальные удаляются
Scenario: Пустая корзина
- WHEN пользователь очищает пустую корзину
- THEN операция завершается успешно, удалив ноль элементов
Requirement: Контрольная сумма оригинала
Данные фото SHALL включать контрольную сумму содержимого оригинала, чтобы клиенты могли сопоставлять локальные файлы с серверными фото и избегать повторной загрузки. Контрольная сумма SHALL быть детерминированной для одного и того же содержимого.
Scenario: Выдача контрольной суммы
- WHEN клиент получает данные фото, у которого зарегистрирован объект-оригинал
- THEN ответ содержит контрольную сумму оригинала
Scenario: Одинаковое содержимое
- WHEN два фото ссылаются на объекты с одинаковым содержимым
- THEN их контрольные суммы совпадают
Scenario: Контрольная сумма доступна в списке
- WHEN клиент запрашивает список фото альбома
- THEN контрольные суммы входят в данные полученных фото
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 пользователь сохраняет цветовые правки фото: баланс белого (температура/оттенок), сочность (vibrance), насыщенность, цветовой микшер по оттенкам, цветокоррекцию (тени/средние тона/света, яркость зон, наложение, баланс) или поканальную коррекцию
- THEN параметры цвета сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов
Scenario: Цветовые правки применяются к пикселям исходного объекта
- WHEN готовится отредактированная версия фото с цветовыми правками
- THEN цветовые правки применяются к пикселям исходного объекта после световых правок, а не к его уменьшенной копии
Scenario: Пустые цветовые правки не сохраняются
- WHEN в цветовых параметрах заданы только наложение/баланс или оттенок колеса без силы и яркости
- THEN правки считаются пустыми и фото не получает цветовую версию
Scenario: Пустые правки эффектов не сохраняются
- WHEN во всех эффектах заданы только значения по умолчанию (сила эффекта, виньетки и зерна равна нулю)
- THEN правки считаются пустыми и фото не получает новую отредактированную версию
Requirement: Признак незавершённого рендера правок
Фото в ответах API и в событиях photo.updated SHALL содержать булево поле edit_pending. Оно SHALL быть true, пока серверный рендер сохранённых правок не завершён, и false, когда рендера нет или он завершён. Пока edit_pending равно true, URL отредактированных вариантов SHALL NOT считаться актуальными, даже если они присутствуют. Поле SHALL быть аддитивным: клиент, который его не читает, продолжает работать как раньше.
Scenario: Сохранение правок
- WHEN правки фото сохранены, а серверный рендер ещё не завершён
- THEN ответ на сохранение и событие
photo.updatedсодержатedit_pending=true
Scenario: Предыдущий рендер устарел
- WHEN у фото уже есть отрендеренная версия, а для новых сохранённых правок рендер ещё не завершён
- THEN фото содержит
edit_pending=true, а URL отредактированных вариантов не выдаются за актуальные
Scenario: Готовность рендера
- WHEN серверный рендер сохранённых правок завершён
- THEN
edit_pending=false, а URL отредактированных вариантов соответствуют сохранённым правкам
Scenario: Сброс правок
- WHEN правки фото сброшены
- THEN
edit_pending=falseи URL отредактированных вариантов отсутствуют
Requirement: Пипетка баланса белого
Клиент SHALL позволять выбрать нейтральную точку на фото и SHALL устанавливать температуру и оттенок так, чтобы цвет этой точки стал нейтральным. Выборка SHALL браться из исходного (неотредактированного) превью, а пресет баланса белого SHALL переходить в «Пользовательский».
Scenario: Выбор нейтральной точки
- WHEN пользователь включает пипетку и кликает по нейтральному участку фото
- THEN температура и оттенок обновляются, и цвет выбранной точки становится нейтральным
Scenario: Пипетка доступна только в просмотрщике
- WHEN фото не открыто в просмотрщике
- THEN пипетка недоступна
Requirement: Живой предпросмотр цветовых правок
Клиент SHALL показывать результат цветовых правок в просмотрщике до завершения серверного рендера. Пока серверный рендер сохранённых правок не завершён (edit_pending) или пока локальные правки не сохранены, клиент SHALL показывать клиентский предпросмотр поверх исходной версии. После завершения рендера SHALL показываться серверная версия.
Scenario: Правки видны сразу
- WHEN пользователь двигает цветовой контрол
- THEN просмотрщик обновляет изображение без ожидания серверного рендера
Scenario: Серверная версия заменяет предпросмотр
- WHEN серверный рендер цветовых правок завершён
- THEN просмотрщик показывает отрендеренную версию
Scenario: Бесшовная подмена предпросмотра
- WHEN серверный рендер завершён, но новое изображение ещё загружается
- THEN просмотрщик продолжает показывать предпросмотр и не показывает оригинал или предыдущую отрендеренную версию
Requirement: Тоновая кривая
Тоновая кривая SHALL задаваться контрольными точками для каналов RGB, R, G и B. Крайние точки SHALL существовать по умолчанию, SHALL двигаться по обеим осям и SHALL задавать плоские участки вне себя. Кривая SHALL быть плавной и монотонной между точками; первый клик по полю SHALL добавлять точку между крайними. Сброс SHALL возвращать линейную кривую.
Scenario: Крайние узлы существуют с самого начала
- WHEN пользователь открывает инструмент «Кривая»
- THEN у каждого канала уже есть крайние точки, и их можно перетаскивать
Scenario: Добавление точки
- WHEN пользователь кликает по свободному полю кривой
- THEN между крайними точками добавляется новая точка, а крайние остаются на месте
Scenario: Подвижные крайние узлы
- WHEN пользователь двигает крайнюю точку по x или y
- THEN вне крайних точек кривая остаётся плоской, а между точками — плавной
Scenario: Плавная интерполяция
- WHEN кривая сохранена и отрендерена
- THEN предпросмотр и итоговое фото используют одну и ту же плавную монотонную кривую
Scenario: Встроенные пресеты
- WHEN пользователь выбирает «Линейный», «Средний контраст» или «Сильный контраст»
- THEN кривые всех четырёх каналов заменяются кривыми пресета (контраст — в RGB, R/G/B — линейные), а подпись показывает выбранный пресет
Scenario: Пользовательская правка
- WHEN пользователь вручную меняет точки кривой
- THEN подпись показывает «Пользовательский»
Scenario: Сброс
- WHEN пользователь сбрасывает кривую
- THEN текущий канал возвращается к линейной кривой с крайними точками
Scenario: Независимые каналы
- WHEN пользователь выбирает канал R, G или B
- THEN он меняет только выбранный канал, остальные каналы остаются прежними
Requirement: Переключение фото в редакторе
При переключении фото правая панель SHALL сохранять выбранную вкладку. Несохранённые правки SHALL сохраняться тому фото, для которого они сделаны, в том числе если пользователь уже переключился на другое фото. Ответы фоновых запросов SHALL NOT подменять текущее фото или его параметры.
Scenario: Вкладка не меняется
- WHEN пользователь переключает фото
- THEN выбранная вкладка правой панели остаётся прежней
Scenario: Правки уходят текущему фото
- WHEN пользователь меняет настройки после переключения
- THEN изменения сохраняются текущему фото, а не предыдущему
Scenario: Фоновое сохранение не подменяет фото
- WHEN сохранение предыдущего фото завершается после переключения
- THEN панель продолжает показывать текущее фото, а его параметры не меняются
Requirement: Пользовательские пресеты кривой
Пользователь SHALL сохранять текущие кривые всех каналов (RGB, R, G и B) как именованный пресет, применять, переименовывать и удалять его. Пресет SHALL содержать кривую для каждого из четырёх каналов. Пресеты SHALL храниться в профиле пользователя и SHALL быть приватными. Импорт и экспорт SHALL выполняться JSON-файлом; импорт SHALL добавлять пресеты в профиль.
Scenario: Сохранение пресета
- WHEN пользователь сохраняет текущие кривые под именем
- THEN пресет появляется в списке и сохраняется в профиле вместе с кривыми RGB, R, G и B
Scenario: Применение пресета
- WHEN пользователь выбирает пользовательский пресет
- THEN кривые всех четырёх каналов заменяются сохранёнными, а текущий канал не меняется
Scenario: Переименование и удаление
- WHEN пользователь переименовывает или удаляет пресет
- THEN список пресетов и профиль обновляются
Scenario: Приватность пресетов
- WHEN другой пользователь открывает свои пресеты
- THEN он не видит пресеты первого пользователя
Scenario: Экспорт пресетов
- WHEN пользователь экспортирует пресеты
- THEN он получает JSON-файл с пользовательскими пресетами и кривыми всех четырёх каналов
Scenario: Импорт пресетов
- WHEN пользователь импортирует корректный JSON-файл
- THEN пресеты добавляются или обновляются в его профиле
Scenario: Некорректный файл или пресет
- WHEN имя или кривая хотя бы одного канала недопустимы
- THEN система отклоняет пресет и не меняет профиль
Requirement: Текстура
Клиент SHALL позволять настраивать текстуру фото в диапазоне -100..100. Положительные значения SHALL усиливать контраст мелких деталей, отрицательные — ослаблять его. Эффект SHALL применяться к исходному кадру после световых и цветовых правок.
Scenario: Усиление текстуры
- WHEN пользователь увеличивает значение текстуры
- THEN мелкие детали становятся контрастнее, а общая тональность кадра не меняется
Scenario: Ослабление текстуры
- WHEN пользователь уменьшает значение текстуры ниже нуля
- THEN мелкие детали сглаживаются
Scenario: Текстура в пределах допустимого
- WHEN клиент присылает значение текстуры вне диапазона -100..100
- THEN сервер ограничивает его ближайшей границей диапазона
Requirement: Чёткость
Клиент SHALL позволять настраивать чёткость фото в диапазоне -100..100. Положительные значения SHALL усиливать локальный контраст средних частот, отрицательные — смягчать его. Чёткость SHALL NOT создавать заметных ореолов на границах контрастных объектов. Эффект SHALL применяться после световых и цветовых правок.
Scenario: Усиление чёткости
- WHEN пользователь увеличивает значение чёткости
- THEN локальный контраст средних тонов растёт, а границы контрастных объектов остаются без заметных ореолов
Scenario: Ослабление чёткости
- WHEN пользователь уменьшает значение чёткости ниже нуля
- THEN изображение становится мягче
Requirement: Убрать дымку
Клиент SHALL позволять ослаблять или усиливать дымку в диапазоне -100..100. При положительных значениях сервер SHALL оценивать дымку по кадру и возвращать контраст и насыщенность удалённых участков; отрицательные значения SHALL добавлять дымку.
Scenario: Удаление дымки
- WHEN пользователь увеличивает значение «Убрать дымку» на кадре с дымкой
- THEN дальние планы становятся контрастнее и насыщеннее
Scenario: Добавление дымки
- WHEN пользователь уменьшает значение «Убрать дымку» ниже нуля
- THEN кадр становится менее контрастным, как при дымке
Requirement: Виньетка
Клиент SHALL позволять настраивать виньетку: силу -100..100, среднюю точку и растушёвку 0..100 (по умолчанию 50), округлость -100..100, света 0..100, а также режим «Свет», «Цвет» или «Закарасить». Режим «Свет» SHALL затемнять или осветлять края, «Цвет» — тонировать их выбранным цветом с сохранением светлоты, «Закарасить» — заливать их выбранным цветом.
Scenario: Затемнение краёв
- WHEN пользователь задаёт положительную силу виньетки в режиме «Свет»
- THEN края кадра затемняются, а центр остаётся без изменений
Scenario: Осветление краёв
- WHEN пользователь задаёт отрицательную силу виньетки в режиме «Свет»
- THEN края кадра осветляются
Scenario: Цветная виньетка
- WHEN пользователь выбирает режим «Цвет» или «Закарасить» и задаёт цвет виньетки
- THEN края кадра тонируются или заливаются этим цветом с силой, заданной ползунком виньетки
Scenario: Геометрия виньетки
- WHEN пользователь меняет среднюю точку, растушёвку или округлость
- THEN меняются размер, мягкость и форма зоны виньетки
Scenario: Защита светов
- WHEN пользователь увеличивает значение «Света» в режиме «Свет»
- THEN яркие участки в зоне виньетки сохраняют яркость
Scenario: Нулевая сила виньетки
- WHEN сила виньетки равна нулю
- THEN виньетка не влияет на изображение независимо от остальных параметров
Requirement: Зерно
Клиент SHALL позволять добавлять зерно с силой, размером и шероховатостью в диапазоне 0..100 (по умолчанию размер 25, шероховатость 50). Зерно SHALL быть детерминированным: повторный рендер одних и тех же параметров SHALL давать одинаковый шум.
Scenario: Добавление зерна
- WHEN пользователь увеличивает силу зерна
- THEN на изображении появляется шум, заметный в средних тонах
Scenario: Размер и шероховатость
- WHEN пользователь меняет размер или шероховатость зерна
- THEN меняются крупность и контрастность шума
Scenario: Детерминированность зерна
- WHEN сервер повторно рендерит фото с теми же параметрами зерна
- THEN рисунок шума совпадает с предыдущим рендером
Scenario: Нулевая сила зерна
- WHEN сила зерна равна нулю
- THEN зерно не влияет на изображение
Requirement: Живой предпросмотр эффектов
Клиент SHALL показывать эффекты в просмотрщике до завершения серверного рендера. Пока серверный рендер сохранённых правок не завершён (edit_pending) или локальные правки не сохранены, клиент SHALL показывать клиентский предпросмотр поверх исходной версии. После готовности клиент SHALL показывать серверную версию, не показывая в промежутке оригинал или предыдущую версию. Предпросмотр пространственных эффектов MAY отличаться от серверного рендера деталями, но SHALL передавать характер эффекта.
Scenario: Эффекты видны сразу
- WHEN пользователь двигает ползунок эффекта
- THEN просмотрщик обновляет изображение без ожидания серверного рендера
Scenario: Серверная версия заменяет предпросмотр
- WHEN серверный рендер эффектов завершён
- THEN просмотрщик показывает серверную версию
Scenario: Бесшовная подмена предпросмотра
- WHEN серверный рендер эффектов завершён, но новое изображение ещё загружается
- THEN просмотрщик продолжает показывать предпросмотр и не показывает оригинал или предыдущую отрендеренную версию
Scenario: Предпросмотр без WebGL
- WHEN аппаратное ускорение недоступно
- THEN клиент показывает эффекты доступным способом или показывает их после серверного рендера, не блокируя интерфейс
Requirement: Мгновенный показ сброса правок
Клиент SHALL показывать исходный вид фото сразу после возврата контролов редактора в значения по умолчанию или нажатия сброса, не дожидаясь отложенного сохранения на сервер. Пока сброс, выполненный возвратом контролов, ещё не отправлен, просмотрщик SHALL NOT показывать отредактированную версию фото.
Scenario: Сброс возвратом контролов
- WHEN пользователь возвращает контролы редактора в значения по умолчанию (двойной клик по ползунку, шкала в нуле)
- THEN просмотрщик сразу показывает исходный вид, не дожидаясь срабатывания отложенного сохранения
Scenario: Сброс кнопкой
- WHEN пользователь нажимает «Сброс»
- THEN просмотрщик показывает исходный вид не позже ответа на запрос сброса
Scenario: Сброс при готовом рендере
- WHEN сброс выполнен, а отредактированная версия ещё существует на сервере
- THEN просмотрщик показывает исходный вид, а не отредактированную версию
Requirement: Свёрнутые секции редактора
Редактор SHALL показывать все сворачиваемые секции инструментов свёрнутыми по умолчанию. Клиент SHALL сохранять состояние каждой секции (свёрнута или развёрнута) в браузере пользователя и восстанавливать его при повторном открытии редактора и перезагрузке страницы. Состояние SHALL NOT зависеть от выбранного фото.
Scenario: Первое открытие редактора
- WHEN пользователь впервые открывает редактор фото
- THEN все секции инструментов («Свет», «Цвет», «Эффекты» и их вложенные блоки) свёрнуты
Scenario: Состояние запоминается
- WHEN пользователь разворачивает секцию «Эффекты» и перезагружает страницу
- THEN секция «Эффекты» остаётся развёрнутой
Scenario: Состояние переживает смену фото
- WHEN пользователь переключает фото в редакторе
- THEN свёрнутость секций не меняется
Requirement: Редактирование только из просмотрщика
Клиент SHALL показывать вкладку «Редактирование» только когда фото открыто в просмотрщике. В режиме сетки правая панель SHALL показывать только «Сведения» и SHALL NOT отображать инструменты редактирования.
Scenario: Фото выбрано в сетке
- WHEN пользователь выбирает фото в сетке, не открывая просмотрщик
- THEN в правой панели отображается только вкладка «Сведения»
Scenario: Фото открыто в просмотрщике
- WHEN пользователь открывает фото в просмотрщике
- THEN в правой панели доступны «Сведения» и «Редактирование»
Scenario: Возврат из просмотрщика в сетку
- WHEN пользователь возвращается из просмотрщика в сетку
- THEN вкладка «Редактирование» скрыта, панель показывает «Сведения»