Новый алгоритм редактирования: обработка идёт только на клиенте. - Фильтры «Свет» считает WASM-модуль на Rust (frontend/wasm/light, 1.5 КБ): он строит тональные кривые и обрабатывает пиксели. При недоступности модуля используется JS-реализация с тем же результатом — расхождение проверяется тестом. - Просмотр рисует видимую область фото с правками на canvas поверх исходной версии, поэтому изменения видны сразу, без ожидания рендера. - Правки уходят на сервер не сразу: через десять секунд без изменений или при переключении фото. Сервер сохраняет параметры и готовит варианты сетки и полноразмерную версию в фоне, о готовности сообщает через SSE — клиент подтягивает готовую версию.
62 KiB
ui-workspace Specification
Purpose
Рабочий стол веб-приложения: постоянный каркас с рейлом альбомов, контекстной панелью, режимом просмотра фото и адаптивным поведением, обеспечивающий цельный «lightroom»-вид.
Requirements
Requirement: Рабочий стол приложения
Аутентифицированные экраны SHALL отображаться внутри постоянного каркаса с топбаром, левым рейлом альбомов и контекстной правой панелью. Публичные экраны (вход, инвайт, share-ссылка) SHALL отображаться вне каркаса, но в общей теме оформления.
Scenario: Внутренний экран
- WHEN аутентифицированный пользователь открывает список альбомов или альбом
- THEN экран отрисован внутри каркаса с рейлом и панелью, рейл не перезагружается при переходе
Scenario: Публичный экран
- WHEN пользователь открывает публичную share-ссылку без входа
- THEN экран отрисован без рейла и контекстной панели, но в тех же цветовых токенах
Requirement: Рейл альбомов
Рейл SHALL постоянно показывать список доступных альбомов с их обложками и отмечать активный альбом.
Scenario: Активный альбом
- WHEN пользователь находится в альбоме
- THEN соответствующий элемент рейла выделен, остальные показывают обложки или плейсхолдер
Requirement: Создание альбома
Создание альбома SHALL выполняться через иконочную кнопку с подсказкой, открывающую модальное окно ввода названия, вместо инлайн-формы.
Scenario: Создание через модалку
- WHEN пользователь нажимает кнопку создания, вводит название и подтверждает
- THEN система создаёт альбом и делает его активным
Scenario: Пустое название
- WHEN пользователь подтверждает пустое название
- THEN создание не выполняется
Requirement: Контекстная панель альбома
Правая панель SHALL показывать превью обложки, название, счётчики фото и участников, управление обложкой и переходы к участникам, настройкам и ссылкам. Элементы управления обложкой SHALL быть доступны только owner и admin.
Scenario: Владелец альбома
- WHEN owner открывает альбом
- THEN панель показывает управление обложкой и переходы к участникам и настройкам
Scenario: Наблюдатель
- WHEN viewer открывает альбом
- THEN панель не показывает управление обложкой
Requirement: Режим просмотра фотографии
Просмотр фото SHALL быть отдельным маршрутом внутри каркаса с крупным изображением, лентой миниатюр снизу и панелью метаданных и комментариев. Тот же режим SHALL использоваться на публичных share-страницах. Миниатюры нижней ленты SHALL давать обратную связь при наведении указателя: изображение SHALL плавно увеличиваться внутри плитки; тот же эффект SHALL применяться при фокусе с клавиатуры. Лента SHALL следовать за текущим фото: при переходе стрелками или выбором в ленте активная миниатюра SHALL оставаться в поле зрения, а лента — прокручиваться к ней.
Scenario: Открытие фото
- WHEN пользователь выбирает фото в сетке
- THEN открывается маршрут фото в каркасе, рейл и панель остаются доступны, адрес отражает фото
Scenario: Навигация в просмотре
- WHEN пользователь использует стрелки влево/вправо или ленту миниатюр
- THEN показывается соседнее фото, адрес обновляется
Scenario: Публичный просмотр
- WHEN посетитель share-ссылки открывает фото
- THEN используется тот же режим просмотра без рейла и панели
Scenario: Наведение на миниатюру
- WHEN пользователь наводит указатель на миниатюру в нижней ленте
- THEN изображение миниатюры плавно увеличивается внутри плитки
Scenario: Лента следует за фото
- WHEN пользователь листает фото стрелками влево/вправо
- THEN лента миниатюр прокручивается так, что активная миниатюра остаётся в поле зрения
Requirement: Плотность сетки
Сетка SHALL давать пользователю управлять размером миниатюр: сдвиг регулятора вправо SHALL
увеличивать миниатюры, влево — уменьшать. Регулятор SHALL сопровождаться кнопками шага в
сторону уменьшения и увеличения. Выбранный размер SHALL сохраняться между сессиями и SHALL
быть общим для обзора альбомов и сетки фото альбома. Клавиши + и - SHALL менять размер
миниатюр на шаг так же, как кнопки регулятора; при вводе текста и в открытых диалогах либо
меню эти клавиши SHALL сохранять обычное действие. В режиме просмотра фото те же клавиши
SHALL изменять зум изображения, а не размер миниатюр.
Scenario: Изменение размера
- WHEN пользователь двигает регулятор плотности
- THEN размер миниатюр меняется, значение сохраняется и восстанавливается при следующем открытии
Scenario: Направление регулятора
- WHEN пользователь сдвигает регулятор вправо
- THEN миниатюры становятся крупнее; при сдвиге влево — мельче
Scenario: Кнопки шага
- WHEN пользователь нажимает кнопку увеличения или уменьшения рядом с регулятором
- THEN размер миниатюр меняется на один шаг и сохраняется
Scenario: Клавиши размера
- WHEN пользователь нажимает
+или- - THEN размер миниатюр меняется на один шаг, как при нажатии кнопок регулятора
Scenario: Клавиши в поле ввода
- WHEN фокус находится в поле ввода или открыт диалог либо меню
- THEN
+и-не меняют размер миниатюр
Scenario: Единый размер в обоих местах
- WHEN пользователь меняет размер на обзоре альбомов или в сетке фото альбома
- THEN тот же размер применяется в обоих местах
Requirement: Загрузка фотографий из рабочего стола
Загрузка SHALL запускаться действием в тулбаре; перетаскивание файлов над альбомом SHALL показывать оверлей и загружать их; пустой альбом SHALL показывать подсказку-зону загрузки. Загрузка SHALL быть доступна только ролям с правом загрузки; принимать SHALL изображения и видео.
Scenario: Перетаскивание
- WHEN пользователь с правом загрузки перетаскивает файлы над альбомом
- THEN показывается оверлей загрузки, и файлы загружаются
Scenario: Пустой альбом
- WHEN пользователь открывает альбом без фото и имеет право загрузки
- THEN в центре показана зона-подсказка загрузки
Scenario: Загрузка видео
- WHEN пользователь с правом загрузки выбирает или перетаскивает видеофайл
- THEN видео загружается в альбом наравне с изображениями
Requirement: Темы и графитовый хром
Тёмная тема SHALL использовать графитовый хром Lightroom и SHALL применяться по умолчанию; нейтральная тема SHALL быть тёплым графитом; остальные темы SHALL оставаться доступными, а скроллбары SHALL быть стилизованы в цвет темы.
Scenario: Первый вход
- WHEN пользователь впервые открывает приложение без сохранённой темы
- THEN применяется тёмная графитовая тема
Scenario: Смена темы
- WHEN пользователь выбирает другую тему
- THEN интерфейс, иконки и скроллбары меняются согласованно, и выбор сохраняется
Scenario: Нейтральная тема
- WHEN пользователь выбирает нейтральную тему
- THEN применяется тёплый графитовый хром, отличный от холодной тёмной темы
Requirement: Адаптивность рабочего стола
На узких экранах рейл SHALL становиться выдвижной панелью слева, контекстная панель — выдвижной панелью справа, а лента миниатюр — горизонтально прокручиваемой. Кнопка «Панель» на узком экране SHALL выдвигать правую панель поверх содержимого; затемняющая область SHALL закрывать панель.
Scenario: Узкий экран
- WHEN ширина окна меньше порога рабочего стола
- THEN рейл скрыт за кнопкой открытия, панель сворачиваема, лента прокручивается горизонтально
Scenario: Открытие панели
- WHEN пользователь нажимает кнопку «Панель» на узком экране
- THEN правая панель выдвигается и становится видимой поверх содержимого
Requirement: Доступность рабочего стола
Иконочные органы управления каркаса SHALL иметь доступные имена, панели SHALL быть достижимы с клавиатуры с видимым фокусом, анимации SHALL учитывать предпочтение уменьшенного движения.
Scenario: Клавиатурная навигация
- WHEN пользователь работает только с клавиатуры
- THEN органы управления каркаса достижимы, доступные имена присутствуют, фокус виден
Requirement: Изменение ширины панелей
Пользователь SHALL иметь возможность менять ширину левой и правой панелей перетаскиванием, а выбранная ширина SHALL сохраняться между сессиями.
Scenario: Перетаскивание разделителя
- WHEN пользователь тянет разделитель у панели на рабочем столе
- THEN панель меняет ширину в допустимых пределах
Scenario: Сохранение ширины
- WHEN пользователь перезагружает приложение
- THEN восстановленные панели имеют сохранённую ширину
Requirement: Скрытие боковых панелей
В режиме просмотра альбома SHALL быть доступна кнопка скрытия левой и правой боковых панелей
в верхней панели сетки рядом с регулятором размера миниатюр и хоткей Tab. В скрытом режиме
панели SHALL не занимать место в раскладке, содержимое альбома SHALL занимать всю ширину,
а панели SHALL показываться при наведении указателя на левую или правую кромку окна и
скрываться после ухода указателя. Хоткей SHALL не срабатывать при вводе текста в поля и при
открытых модальных диалогах или меню. Размер миниатюр в скрытом режиме SHALL сохраняться:
вместо растянутых плиток сетка SHALL показывать больше фото в строке.
Scenario: Переключение кнопкой
- WHEN пользователь нажимает кнопку скрытия боковых панелей в альбоме
- THEN обе панели уезжают за края, а сетка фото занимает всю ширину окна
Scenario: Размер миниатюр при скрытых панелях
- WHEN боковые панели скрыты
- THEN плитки остаются того же размера, что и с видимыми панелями, а в строке помещается больше фото
Scenario: Переключение хоткеем
- WHEN пользователь нажимает
Tabв режиме просмотра альбома - THEN боковые панели скрываются или показываются
Scenario: Появление у кромки
- WHEN панели скрыты, и указатель подходит к левой или правой кромке окна
- THEN соответствующая панель выезжает поверх содержимого
Scenario: Скрытие после ухода указателя
- WHEN указатель покидает показанную у кромки панель
- THEN панель снова уезжает за край
Scenario: Поля ввода и диалоги
- WHEN фокус находится в поле ввода или открыт модальный диалог либо меню
- THEN
Tabвыполняет обычную навигацию и не переключает панели
Requirement: Сворачиваемые разделы панели
Контекстная панель SHALL группировать содержимое в сворачиваемые разделы, а состояние сворачивания SHALL сохраняться; раздел «Информация» SHALL показывать метаданные фото, включая извлечённые EXIF-атрибуты.
Scenario: Сворачивание раздела
- WHEN пользователь нажимает заголовок раздела
- THEN содержимое раздела скрывается или показывается, состояние сохраняется
Scenario: Метаданные фото
- WHEN открыт просмотр фото
- THEN раздел «Информация» показывает размер, тип, разрешение, дату съёмки, координаты и извлечённые EXIF-атрибуты
Requirement: Полноэкранный просмотр фотографии
В режиме просмотра SHALL быть доступен переход в полноэкранный режим кнопкой и хоткеем f.
В полноэкранном режиме верхняя и нижняя панели SHALL скрываться и показываться у
соответствующей кромки экрана.
Scenario: Вход в полный экран
- WHEN пользователь нажимает кнопку полного экрана или клавишу
f - THEN просмотр разворачивается на весь экран, панели скрыты
Scenario: Панель по кромке
- WHEN курсор касается верхней или нижней кромки экрана
- THEN соответствующая панель показывается, а при уходе курсора скрывается сразу
Scenario: Выход из полного экрана
- WHEN пользователь повторно нажимает кнопку или клавишу
f - THEN полноэкранный режим закрывается
Scenario: Полноэкранный режим приложения
- WHEN включён полноэкранный режим приложения, и пользователь нажимает
f - THEN открывается полноэкранный просмотр фото (вложенно), а полноэкранный режим приложения не выключается
Requirement: Зум в просмотре
Просмотр SHALL поддерживать зум колесом мыши без прокрутки, с привязкой к указателю и
верхним пределом 200% размеров фото. Клавиши + и - SHALL менять масштаб на шаг, сохраняя
точку в центре просмотра. Space SHALL возвращать изображение из любого зума во
вписанный вид, а из вписанного — в 100%. При зуме SHALL догружаться оригинал изображения.
Стрелки соседних фото SHALL появляться у левой/правой кромки и прятаться в зуме. Зум SHALL
работать и в полноэкранном режиме.
Scenario: Зум колесом к указателю
- WHEN пользователь вращает колесо мыши над фото
- THEN масштаб меняется относительно курсора, страница не прокручивается, предел — 200%
Scenario: Зум клавишами
- WHEN пользователь нажимает
+или-в режиме просмотра фото - THEN масштаб меняется на шаг, точка в центре просмотра остаётся на месте, а в полноэкранном режиме зум работает так же
Scenario: Пробел
- WHEN пользователь нажимает
Space - THEN из вписанного вида изображение переходит в 100%, а из любого зума возвращается во вписанный
Scenario: Оригинал в зуме
- WHEN пользователь входит в зум
- THEN догружается оригинал изображения и подменяется по факту загрузки
Scenario: Стрелки соседних фото
- WHEN курсор находится у левой или правой кромки области просмотра
- THEN показывается соответствующая стрелка, скрываясь сразу после ухода; в зуме стрелки скрыты
Scenario: Панорамирование
- WHEN фото увеличено
- THEN пользователь может перемещать изображение перетаскиванием и прокруткой
Requirement: Удаление фото из просмотра
Автор фото, владелец и админ альбома SHALL иметь возможность удалить текущее фото из режима просмотра; удаление SHALL подтверждаться диалогом.
Scenario: Подтверждение удаления
- WHEN пользователь с правом удаления нажимает «Удалить»
- THEN открывается диалог подтверждения, и после подтверждения фото удаляется
Scenario: Лента после удаления
- WHEN фото удалено
- THEN просмотр переходит к соседнему фото, а лента миниатюр обновляется без удалённого
Requirement: Возврат к обзору
Из альбома и из режима просмотра SHALL быть доступен возврат к обзору альбомов.
Scenario: Возврат из альбома
- WHEN пользователь нажимает кнопку «назад» слева от названия альбома
- THEN открывается обзор альбомов
Scenario: Возврат из просмотра
- WHEN пользователь нажимает кнопку «назад» в режиме просмотра
- THEN открывается альбом с сеткой фото
Requirement: Настройки списка атрибутов информации
Раздел «Информация» SHALL предоставлять доступ к настройкам (иконка-шестерёнка), открывающим модальное окно со списком EXIF-атрибутов, где пользователь SHALL отмечать отображаемые атрибуты и задавать их подписи. В окне SHALL быть доступны действия «отметить все» и «снять все». Настройки SHALL применяться ко всем фото и SHALL сохраняться в профиле пользователя. При отсутствии сохранённых настроек SHALL отображаться базовый набор атрибутов (камера, объектив, выдержка, диафрагма, ISO, фокусное расстояние).
Scenario: Открытие настроек
- WHEN пользователь нажимает иконку настроек в разделе «Информация»
- THEN открывается модальное окно со списком EXIF-атрибутов: галочки видимости и поля подписей, а также действия «отметить все»/«снять все»
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 открывать режим просмотра фото. На сенсорных экранах одиночное касание по фото SHALL открывать режим просмотра этого фото и не SHALL выделять его; выделение на сенсорных экранах SHALL выполняться долгим нажатием.
Scenario: Одиночный выбор
- WHEN пользователь кликает по фото в сетке указателем
- THEN фото отмечено рамкой, правая панель показывает сведения об этом фото, просмотр не открывается
Scenario: Двойной выбор
- WHEN пользователь дважды кликает по фото в сетке указателем
- THEN открывается режим просмотра этого фото
Scenario: Касание на сенсорном экране
- WHEN на сенсорном устройстве пользователь касается фото в сетке
- THEN открывается режим просмотра этого фото, а отметка выделения не появляется
Scenario: Смена альбома
- WHEN пользователь переходит в другой альбом
- THEN отметка выбора сбрасывается
Requirement: Мультивыбор фото в сетке
В режиме альбома пользователь SHALL выбирать несколько фото: обычный клик — одно фото, Ctrl/Cmd-клик — добавить или убрать, Shift-клик — диапазон. Выбранные фото SHALL быть отмечены.
Scenario: Мультивыбор
- WHEN пользователь кликает по фото с зажатым Ctrl/Cmd или Shift
- THEN соответствующие фото отмечены, а панель показывает количество выбранных
Requirement: Действия с выбранными фото
Когда выбрано одно или несколько фото, правая панель SHALL показывать раздел «Действия» со строками-пунктами (иконка + подпись) «Открыть (просмотр)», «Переместить», «Копировать» и «Удалить»; удаление SHALL подтверждаться диалогом.
Scenario: Действия
- WHEN выбрано одно или несколько фото
- THEN панель показывает раздел «Действия» с пунктами «Открыть (просмотр)», «Переместить», «Копировать» и «Удалить»
Requirement: Перетаскивание выбранных фото на альбом
Выбранные фото SHALL перетаскиваться на альбом в рейле; при отпускании SHALL открываться диалог выбора переноса или копирования с указанием целевого альбома. Во время такого перетаскивания зона загрузки файлов SHALL не показываться.
Scenario: Перетаскивание на альбом
- WHEN пользователь перетаскивает выделенные фото на альбом в рейле и отпускает
- THEN открывается диалог с выбором переноса/копирования и целевым альбомом
Requirement: Корзина в списке альбомов
Рейл альбомов SHALL содержать вход в корзину последним элементом списка (с иконкой корзины). Экран обзора альбомов SHALL не показывать пустую правую панель.
Scenario: Вход в корзину
- WHEN пользователь открывает приложение
- THEN в левом рейле последним элементом списка показана «Корзина» с иконкой корзины
Scenario: Обзор без панели
- WHEN пользователь открывает обзор альбомов
- THEN правая панель не отображается
Requirement: Действия в режиме просмотра
В режиме просмотра правая панель SHALL содержать раздел «Действия» со строками-пунктами (иконка + подпись) «Скачать», «Переместить», «Копировать» и «Удалить» (удаление — по правам). Верхняя панель просмотра SHALL не дублировать скачивание и удаление.
Scenario: Действия в просмотре
- WHEN открыт режим просмотра фото
- THEN раздел «Действия» содержит «Скачать», «Переместить», «Копировать» и «Удалить», а в верхней панели этих кнопок нет
Scenario: Удаление из панели
- WHEN пользователь удаляет фото из раздела «Действия»
- THEN открывается подтверждение, и после удаления просмотр переходит к соседнему фото
Requirement: Контекстное меню фото
Правый клик по фото в сетке и по изображению в режиме просмотра SHALL открывать контекстное меню с пунктами раздела «Действия» («Скачать», «Переместить», «Копировать», «Удалить»; в сетке также «Открыть (просмотр)»), выполняющими те же действия.
Scenario: Меню в сетке
- WHEN пользователь кликает правой кнопкой по фото в сетке
- THEN открывается меню с пунктами «Открыть (просмотр)», «Скачать», «Переместить», «Копировать», «Удалить»
Scenario: Меню в просмотре
- WHEN пользователь кликает правой кнопкой по изображению в режиме просмотра
- THEN открывается меню с пунктами «Скачать», «Переместить», «Копировать», «Удалить»
Requirement: Контекстное меню альбома
Правый клик по альбому в рейле и по карточке на обзоре SHALL открывать меню с пунктами «Переименовать» и «Удалить»; переименование SHALL открывать диалог, удаление — подтверждение.
Scenario: Меню альбома
- WHEN пользователь кликает правой кнопкой по альбому
- THEN открывается меню с «Переименовать» и «Удалить»
Scenario: Удаление альбома
- WHEN пользователь подтверждает удаление альбома
- THEN альбом удаляется, а его фото попадают в корзину
Requirement: Счётчики в рейле
Рейл SHALL показывать число фото у каждого альбома и число элементов в корзине.
Scenario: Счётчики
- WHEN пользователь смотрит на рейл альбомов
- THEN у каждого альбома показано число фото, а у корзины — число удалённых фото
Requirement: Метки в панели фото
Панель фото SHALL содержать раздел «Метки» с чипами меток фото и полем добавления, дающим поиск по меткам пользователя и создание новых. Изменение меток в панели SHALL сразу отражаться в сетке и в режиме просмотра без перезагрузки страницы.
Scenario: Раздел «Метки»
- WHEN открыт просмотр или выбран один фото в сетке
- THEN в панели есть раздел «Метки» с текущими метками и полем добавления с поиском
Scenario: Обновление без перезагрузки
- WHEN пользователь добавляет или снимает метку в панели фото
- THEN сетка и режим просмотра сразу отражают изменение (например, бейдж и режим 360-панорамы), без перезагрузки страницы
Requirement: Компактный верхний хром
Верхняя панель приложения и верхняя панель режима просмотра SHALL иметь минимальную высоту
(узкие отступы, иконочные органы управления), сохраняя все действия доступными. Верхняя панель
режима просмотра SHALL быть прозрачным оверлеем и SHALL не занимать место в раскладке,
увеличивая область фото. Кнопка «На весь экран» верхней панели приложения SHALL иметь хоткей
Ctrl+F; хоткей f полноэкранного просмотра SHALL срабатывать только без модификаторов, чтобы
Ctrl+F не перехватывался.
Scenario: Компактные верхние панели
- WHEN открыт список альбомов или режим просмотра фото
- THEN верхняя панель занимает минимум высоты, а её действия (навигация, полный экран, скачивание, удаление) доступны
Scenario: Полный экран по хоткею
- WHEN пользователь нажимает
Ctrl+F - THEN приложение переходит в полноэкранный режим или выходит из него, а в режиме просмотра фото полноэкранным остаётся сам просмотр
Scenario: Прозрачная панель просмотра
- WHEN открыт режим просмотра фото
- THEN верхняя панель нарисована поверх фото без фона и не уменьшает область изображения
Requirement: Курсор в сетке фото
Сетка SHALL хранить курсор — текущую ячейку, отдельную от выделения. Перемещение курсора SHALL не менять выделение и не обновлять правую панель. Курсор SHALL отображаться контуром, отличимым от рамки выделения, SHALL сохраняться при снятии выделения и SHALL устанавливаться на фото при клике по нему мышью.
Scenario: Синхронизация с мышью
- WHEN пользователь кликает по фото в сетке
- THEN фото выделено, а курсор установлен на нём
Scenario: Перемещение не меняет выделение
- WHEN фокус в сетке и пользователь нажимает стрелку
- THEN курсор перемещается, а выделение и правая панель не меняются
Scenario: Курсор переживает снятие выделения
- WHEN выделение снято кликом по пустому месту или клавишей Esc
- THEN курсор остаётся на прежней ячейке
Scenario: Отображение курсора
- WHEN сетка в фокусе и у курсора есть ячейка
- THEN ячейка показывает контур, отличимый от рамки выделения, и он скрывается при уходе фокуса из сетки
Requirement: Клавиши сетки фото
При фокусе в сетке клавиши SHALL управлять курсором и выделением: ←/→ — на соседнее фото с
переходом на соседнюю строку, ↑/↓ — на строку (density.cols) с упором в границы.
Space SHALL переключать выделение ячейки под курсором, Shift+стрелки — двигать курсор и
расширять выделение от якоря, Enter — открывать просмотр, Esc — снимать выделение. Вне
фокуса сетки клавиши SHALL не действовать.
Scenario: Горизонтальное перемещение
- WHEN курсор не в конце строки и нажато
→ - THEN курсор переходит на следующее фото
Scenario: Перенос на соседнюю строку
- WHEN курсор в конце строки и нажато
→ - THEN курсор переходит на первое фото следующей строки
Scenario: Вертикальные границы
- WHEN курсор в верхней или нижней строке и нажато
↑или↓ - THEN курсор остаётся на месте, без переноса
Scenario: Переключение выделения
- WHEN нажато
Space - THEN ячейка под курсором добавляется или убирается из выделения, а правая панель обновляется
Scenario: Расширение выделения
- WHEN нажато
Shiftвместе со стрелкой - THEN курсор двигается, а выделение расширяется от якоря до курсора
Scenario: Открытие просмотра
- WHEN нажато
Enter - THEN открывается просмотр фото под курсором
Scenario: Снятие выделения клавишей
- WHEN нажато
Esc - THEN выделение снимается, а курсор остаётся
Scenario: Прокрутка к курсору
- WHEN курсор перемещается за пределы видимой области сетки
- THEN сетка прокручивается так, чтобы ячейка под курсором стала видимой
Scenario: Догрузка при выходе за загруженные фото
- WHEN курсор на последнем загруженном фото и нажата стрелка в сторону следующих фото
- THEN следующая порция фото догружается, а курсор переходит на следующее фото
Scenario: Вне фокуса сетки
- WHEN фокус находится в поле ввода или диалоге и нажаты стрелки или
Space - THEN курсор и выделение сетки не меняются
Requirement: Снятие выделения кликом по пустому месту
Клик по области сетки и её отступам, не по фото и не по органам управления тулбара, рейла и панелей, SHALL снимать выделение и возвращать в правой панели свойства альбома. Фокус SHALL оставаться в сетке, чтобы клавиши продолжали действовать.
Scenario: Клик по пустому месту
- WHEN есть выделение и пользователь кликает по пустому месту сетки или её отступам
- THEN выделение снято, а правая панель показывает свойства альбома
Scenario: Мультивыделение
- WHEN выделено несколько фото и пользователь кликает по пустому месту
- THEN выделение снимается полностью
Scenario: Клик по фото
- WHEN пользователь кликает по фото
- THEN выделение устанавливается на фото, а не снимается
Scenario: Клик по органам управления
- WHEN пользователь кликает по кнопке тулбара, элементу рейла или панели
- THEN выделение не снимается
Scenario: Фокус остаётся в сетке
- WHEN выделение снято кликом по пустому месту
- THEN стрелки продолжают двигать курсор без дополнительного Tab
Requirement: Возврат фокуса из просмотра
При возврате в альбом из просмотра сетка SHALL выделить последнее просмотренное фото (одиночное), поставить на него курсор, вернуть фокус в сетку и прокрутить к ячейке. Если фото нет среди загруженных, сетка SHALL догружать страницы, пока оно не появится. Восстановление SHALL применяться только к тому альбому, из которого был открыт просмотр.
Scenario: Возврат из просмотра
- WHEN пользователь открыл фото и закрыл просмотр
- THEN в сетке это фото выделено, курсор на нём, фокус в сетке, а ячейка прокручена в видимую область
Scenario: Последнее просмотренное фото
- WHEN пользователь пролистал несколько фото в просмотре и закрыл его
- THEN восстанавливается последнее просмотренное фото
Scenario: Фото вне первой страницы
- WHEN просмотренное фото отсутствует в первой загруженной странице
- THEN сетка догружает страницы, пока фото не появится, и только затем ставит на него курсор
Scenario: Возврат в другой альбом
- WHEN после просмотра пользователь открывает другой альбом
- THEN восстановление не применяется, выборка пуста
Scenario: Клавиатура после возврата
- WHEN фокус восстановлен после просмотра
- THEN стрелки сразу двигают курсор без дополнительного Tab
Requirement: Просмотр 360-панорамы
Фото с тегом pano360 SHALL открываться в режиме сферического обзора (equirect) вместо плоского
изображения. Перетаскивание SHALL менять направление взгляда и SHALL перемещать сцену вслед за
указателем по обеим осям согласованно, колесо — угол обзора, Space — сбрасывать вид. Остальные
фото SHALL просматриваться как прежде. При недоступности WebGL или сбое загрузки/декодирования
SHALL показываться плоское изображение. На публичных share-страницах панорамный режим SHALL не
активироваться.
Scenario: Обзор панорамы
- WHEN пользователь открывает фото с тегом
pano360 - THEN показывается сферический обзор, и перетаскивание меняет направление взгляда
Scenario: Направление перетаскивания
- WHEN в режиме панорамы пользователь перетаскивает вправо
- THEN сцена перемещается вправо (направление взгляда поворачивается влево), согласованно с вертикальным перетаскиванием
Scenario: Обычное фото
- WHEN пользователь открывает фото без тега
pano360 - THEN показывается обычное плоское изображение
Scenario: Управление обзором
- WHEN в режиме панорамы пользователь вращает колесо или нажимает
Space - THEN колесо меняет угол обзора, а
Spaceсбрасывает направление взгляда и угол обзора
Scenario: Навигация между фото
- WHEN в режиме панорамы пользователь нажимает стрелки влево/вправо или выбирает миниатюру
- THEN открывается соседнее фото, панорамный режим включается или выключается по его тегу
Scenario: Нет WebGL
- WHEN WebGL недоступен или изображение не загружается
- THEN показывается плоское изображение
Scenario: Публичная share-страница
- WHEN фото открыто по публичной share-ссылке
- THEN панорамный режим не активируется, показывается плоское изображение
Requirement: Резкость миниатюр
Сетка фото и обзор альбомов SHALL подбирать вариант изображения под фактический размер плитки, чтобы миниатюры не отображались крупнее доступного разрешения.
Scenario: Крупные плитки
- WHEN пользователь увеличивает миниатюры до крупных плиток
- THEN показывается вариант изображения, достаточный для их размера, без расплывания
Scenario: Мелкие плитки
- WHEN миниатюры показаны мелкими плитками
- THEN показывается компактный вариант, чтобы не загружать крупное изображение
Scenario: Вариант ещё не готов
- WHEN для фото доступен только оригинал или только миниатюра
- THEN показывается доступный вариант, и сетка отображается корректно
Requirement: Воспроизведение видео в просмотре
Видео в режиме просмотра SHALL воспроизводиться встроенным плеером с нативными контролами и постером до готовности варианта; зум, панорамирование и режим панорамы SHALL к видео не применяться, а действия скачивания, переноса, копирования и удаления SHALL оставаться доступными.
Scenario: Плеер
- WHEN пользователь открывает видео в режиме просмотра
- THEN показывается встроенный плеер, а до готовности варианта — постер
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 отражать результат.
Scenario: Наличие действия
- WHEN пользователь открывает корзину с элементами
- THEN на экране показано действие «Очистить корзину»
Scenario: Подтверждение очистки
- WHEN пользователь выбирает «Очистить корзину»
- THEN открывается диалог подтверждения, и только после подтверждения корзина очищается
Scenario: Обновление после очистки
- WHEN корзина очищена
- THEN на экране показывается пустая корзина, а счётчик корзины в рейле обновляется
Requirement: Синхронизация счётчика корзины
Рейл SHALL обновлять число элементов корзины сразу после любого действия, меняющего содержимое корзины, без перезагрузки страницы: помещения фото в корзину, восстановления, окончательного удаления и удаления альбома с фото.
Scenario: Помещение фото в корзину
- WHEN пользователь удаляет одно или несколько фото
- THEN фото покидают сетку, а число элементов корзины в рейле увеличивается на их количество
Scenario: Восстановление из корзины
- WHEN пользователь восстанавливает фото из корзины
- THEN фото покидает список корзины, а число элементов корзины в рейле уменьшается на единицу
Scenario: Окончательное удаление
- WHEN пользователь окончательно удаляет фото из корзины
- THEN число элементов корзины в рейле уменьшается на единицу
Scenario: Удаление альбома
- WHEN пользователь удаляет альбом с фото
- THEN его фото попадают в корзину, а число элементов корзины в рейле увеличивается на их количество
Requirement: Офлайн-режим веб-приложения
Веб-приложение SHALL хранить в офлайн-кеше браузера оболочку приложения, ответы API (списки альбомов и фото) и варианты изображений: эскиз, превью и средний вариант. Ключ кеша картинки SHALL определяться путём объекта без подписи, чтобы обновление подписи не создавало новые записи. Оригиналы и видео SHALL не кешироваться. Размер кеша SHALL быть ограничен, а более старые записи SHALL вытесняться первыми. При выходе из аккаунта кеш SHALL очищаться. При отсутствии сети приложение SHALL открываться и показывать сохранённые данные, а верхняя панель SHALL показывать индикатор «Офлайн».
Scenario: Просмотр без сети
- WHEN пользователь открывает ранее просмотренный альбом без подключения к сети
- THEN приложение открывается, показывает сохранённый список фото и их эскизы
Scenario: Смена подписи объекта
- WHEN подпись объекта обновляется, а содержимое остаётся прежним
- THEN используется та же запись кеша, а не новая
Scenario: Ограничение кеша
- WHEN кеш картинок превышает предел
- THEN самые старые записи вытесняются, а свежие остаются
Scenario: Выход из аккаунта
- WHEN пользователь выходит из аккаунта
- THEN офлайн-кеш картинок и данных очищается
Scenario: Индикатор офлайна
- WHEN браузер сообщает об отсутствии сети
- THEN верхняя панель показывает индикатор «Офлайн»
Requirement: Вкладка «Редактирование»
Правая панель фото SHALL содержать вкладку «Редактирование» с инструментом «Свет»: ползунки экспозиции, контраста, светов, теней, белых и чёрных, а также точечную кривую с выбором канала. Правки SHALL применяться к фото сразу и отправляться на сервер после десяти секунд без изменений либо при переключении фото. Переключатель «Сброс» SHALL удалять параметры правок и отредактированную версию фото. Сетка и просмотр SHALL показывать отредактированную версию фото, когда она отрендерена.
Scenario: Отложенная отправка
- WHEN пользователь меняет ползунок и не трогает панель десять секунд
- THEN правки отправляются на сервер, а сервер рендерит фото в фоне
Scenario: Вкладки панели
- WHEN открыта правая панель фото
- THEN видны вкладки «Сведения» и «Редактирование»
Scenario: Сохранение правок
- WHEN пользователь отпускает ползунок
- THEN правки сохраняются, а после рендера фото в сетке и просмотре показывается отредактированная версия
Scenario: Правки видны сразу
- WHEN пользователь меняет ползунок, а сервер ещё не отрендерил фото
- THEN видимая область фото в просмотре показывает правки без ожидания рендера
Scenario: Фильтры считаются в браузере
- WHEN браузер рисует предпросмотр правок
- THEN фильтры применяются WASM-модулем, а при его недоступности — JavaScript-реализацией с тем же результатом
Scenario: Сброс правок
- WHEN пользователь нажимает «Сброс» на вкладке «Редактирование»
- THEN параметры правок и отредактированная версия фото удаляются, а фото возвращается к исходному виду
Scenario: Зум при правках
- WHEN фото отредактировано, и пользователь увеличивает его
- THEN браузер рисует видимую область с правками, пока сервер не отрендерил фото, а зум не сбрасывается при обновлении фото