# 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 быть доступна только ролям с правом загрузки. #### 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** показывается доступный вариант, и сетка отображается корректно