Files
ds-galo/openspec/specs/ui-workspace/spec.md
T

635 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
применяться при фокусе с клавиатуры.
#### Scenario: Открытие фото
- **WHEN** пользователь выбирает фото в сетке
- **THEN** открывается маршрут фото в каркасе, рейл и панель остаются доступны, адрес отражает фото
#### Scenario: Навигация в просмотре
- **WHEN** пользователь использует стрелки влево/вправо или ленту миниатюр
- **THEN** показывается соседнее фото, адрес обновляется
#### Scenario: Публичный просмотр
- **WHEN** посетитель share-ссылки открывает фото
- **THEN** используется тот же режим просмотра без рейла и панели
#### Scenario: Наведение на миниатюру
- **WHEN** пользователь наводит указатель на миниатюру в нижней ленте
- **THEN** изображение миниатюры плавно увеличивается внутри плитки
### Requirement: Плотность сетки
Сетка SHALL давать пользователю управлять размером миниатюр: сдвиг регулятора вправо SHALL
увеличивать миниатюры, влево — уменьшать. Регулятор SHALL сопровождаться кнопками шага в
сторону уменьшения и увеличения. Выбранный размер SHALL сохраняться между сессиями и SHALL
быть общим для обзора альбомов и сетки фото альбома.
#### 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 группировать содержимое в сворачиваемые разделы, а состояние
сворачивания 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% размеров фото. `Space` SHALL возвращать изображение из любого зума во
вписанный вид, а из вписанного — в 100%. При зуме SHALL догружаться оригинал изображения.
Стрелки соседних фото SHALL появляться у левой/правой кромки и прятаться в зуме. Зум SHALL
работать и в полноэкранном режиме.
#### Scenario: Зум колесом к указателю
- **WHEN** пользователь вращает колесо мыши над фото
- **THEN** масштаб меняется относительно курсора, страница не прокручивается, предел — 200%
#### 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 содержать раздел «Метки» с чипами меток фото и полем добавления, дающим
поиск по меткам пользователя и создание новых.
#### Scenario: Раздел «Метки»
- **WHEN** открыт просмотр или выбран один фото в сетке
- **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** выделение снято кликом по пустому месту или клавишей 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 менять направление взгляда, колесо — угол обзора, `Space` —
сбрасывать вид. Остальные фото SHALL просматриваться как прежде. При недоступности WebGL или
сбое загрузки/декодирования SHALL показываться плоское изображение. На публичных share-страницах
панорамный режим SHALL не активироваться.
#### Scenario: Обзор панорамы
- **WHEN** пользователь открывает фото с тегом `pano360`
- **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** открывается режим просмотра видео