- single click selects a photo (ring + aria-pressed) and shows its info in the right panel without navigating; double click opens the viewer - shared 'selection' store; panel uses the route photo in viewer mode and the selected photo in album mode; selection resets on album change - openspec: sync lightroom-workspace-ui (grid-selection requirement, viewer chrome wording without timers, nested f fullscreen, tasks 9.9/9.10)
15 KiB
15 KiB
Tasks
1. Backend: обложка и целостность
- 1.1 Добавить миграцию
000002_album_cover_fk(up/down): обнулить висячиеcover_photo_id, затемADD CONSTRAINT ... FOREIGN KEY (cover_photo_id) REFERENCES photos(id) ON DELETE SET NULL. Проверка:make migrateприменяется,\d albumsпоказывает constraint, down-миграция снимает его. - 1.2 Добавить
CoverThumbURLвmodels.Album(jsoncover_thumb_url,omitempty). Проверка:cd backend && go build ./...проходит. - 1.3 В
albums/store.goдобавитьLEFT JOIN photos p ON p.id = a.cover_photo_idи выбиратьp.thumb_key AS cover_thumb_keyвListForUserиGetByID. Проверка: запрос возвращаетcover_thumb_keyдля альбома с обложкой иNULLбез неё. - 1.4 В
albums/service.goпресигнитьcover_thumb_keyвCoverThumbURLвListиGet. Проверка:GET /api/albumsв ответе содержитcover_thumb_urlу альбома с обложкой. - 1.5 В
photos/service.goвызыватьSetCoverтолько приcover_photo_id IS NULL. Проверка: загрузка двух фото в новый альбом оставляет обложкой первое; после удаления фото-обложки cover становитсяNULL, следующая загрузка ставит новый. - 1.6 Покрыть тестами store-join обложки и автообложку; проверить
cd backend && go test ./....
2. Frontend: темы и каркас
- 2.1 Добавить токены
--chrome/--canvas, перенастроитьdarkв графит и сделать его дефолтом (при отсутствии сохранённого выбора). Проверка:cd frontend && npm run checkпроходит, все три темы визуально согласованы. - 2.2 Добавить
cover_thumb_urlвlib/types.tsи недостающие иконки (обложка, плотность/меню) вlib/icons/registry.ts. Проверка:npm run checkбез ошибок типов. - 2.3 Разбить
+layout.svelteна группы(app)/(public)и ввестиAppShell(топбар, слот рейла, слот панели, центр = маршрут). Проверка: навигация между альбомами не перезагружает рейл;/login,/invites/[token],/share/[token]рендерятся без каркаса. - 2.4 Реализовать адаптивный каркас: рейл → drawer, панель → сворачиваемая секция, лента → горизонтальный скролл на узкой ширине. Проверка: при ширине меньше порога каркас управляем без горизонтального overflow.
3. Рейл и создание альбома
- 3.1 Компонент
AlbumRail: список альбомов с обложками, плейсхолдер, подсветка активного. Проверка: активный альбом выделен, остальные показывают обложку/плейсхолдер. - 3.2 Кнопка создания
+с хинтом и модалка ввода названия (замена инлайн-формы). Проверка: создание альбома через модалку, новый альбом становится активным; пустое название не создаёт альбом.
4. Контекстная панель и обложки
- 4.1 Компонент
AlbumPanel: превью обложки, название, счётчики, переходы к участникам/настройкам/ссылкам; управление обложкой только для owner/admin. Проверка: viewer не видит управление обложкой. - 4.2 Модальный
CoverPicker(сетка миниатюр) +PATCH /albums/:idсcover_photo_id/clear_cover. Проверка: назначение и сброс обложки отражаются в рейле, карточке и панели. - 4.3 Выбор обложки выполняется только из правой панели (модальный пикер); действие «Сделать обложкой» в тулбаре просмотра не используется. Проверка: обложка задаётся и сбрасывается из панели, в тулбаре просмотра кнопки нет.
5. Центр: сетка, плотность, загрузка
- 5.1 Сетка альбомов edge-to-edge с реальными обложками вместо карточек-заглушек. Проверка: карточки показывают
cover_thumb_url, при отсутствии — плейсхолдер. - 5.2 Регулятор плотности миниатюр в тулбаре сетки с сохранением в
localStorage. Проверка: размер меняется и восстанавливается после перезагрузки. - 5.3 Загрузка: иконочная кнопка в тулбаре, полноэкранный drag-overlay, зона-подсказка в пустом альбоме; прогресс компактной полосой. Проверка: перетаскивание и выбор файлов загружают фото, пустой альбом показывает подсказку.
6. Просмотр как маршрут
- 6.1 Маршрут
albums/[id]/photos/[photoId]и компонентViewer(крупное фото, filmstrip, панель метаданных/комментариев, тулбар, клавиши ←/→/Esc). Проверка: переход из сетки открывает маршрут, адрес отражает фото, навигация синхронна с filmstrip. - 6.2 Переиспользовать
Viewerнаshare/[token]вместо инлайн-лайтбокса. Проверка: публичный просмотр использует тот же компонент без рейла/панели.
7. Интеграция
- 7.1 Пройти сценарий: создать альбом → загрузить фото → задать обложку → открыть просмотр → открыть share-ссылку; проверить поведение на десктопе и узком экране.
- 7.2 Проверить
make build(сборка фронтенда вbackend/static, статический Go-бинарник) иopenspec validate "lightroom-workspace-ui".
8. Дополнительно реализовано (после планирования)
- 8.1 Переименование фото:
PATCH /photos/:id(права автора/owner/admin, пустое имя 400) и инлайн-редактор в правой панели; событиеphoto.updated. Проверка: переименование сохраняется, пустое имя отклоняется, чужое фото — отказ, событие приходит. - 8.2 Удаление фото из просмотра с модальным подтверждением; переход к соседнему и обновление ленты. Проверка: после удаления лента не содержит удалённое фото.
- 8.3 Полноэкранный просмотр (кнопка и
f) с автоскрытием верхней/нижней панелей у кромок и стрелок (1/6 края, в зуме скрыты); поведение без таймеров — панели и стрелки показываются/скрываются сразу, стрелки только в границах вьюера; зум колесом к указателю без прокрутки (макс 200%),Spaceвозвращает из любого зума во вписанный вид, в зуме подгружается оригинал. Проверка: поведение подтверждено в браузере (автоскрытие, зум к указателю, предел 200%, подмена превью→оригинал). - 8.4 Сворачиваемые разделы правой панели с сохранением состояния и раздел «Информация» (EXIF). Проверка: состояние сохраняется между сессиями, метаданные отображаются.
- 8.5 Ресайз левой и правой панелей перетаскиванием с сохранением ширины. Проверка: ширина восстанавливается после перезагрузки.
- 8.6 Лимит загрузки 100 МБ и понятные ошибки (клиентская проверка и
413 file_too_large). Проверка: 60 МБ проходит разбор, превышение лимита даёт явную ошибку размера. - 8.7 Счётчики
photos_count/members_countв ответе альбома. Проверка:GET /api/albumsиGET /api/albums/:idсодержат счётчики. - 8.8 Тема и хром: нейтральная тема — тёплый графит, порядок и значок переключателя, стилизация скроллбаров. Проверка: темы переключаются согласованно, скроллбары в тон темы.
- 8.9 Извлечение EXIF: парсер заменён на
github.com/dsoprea/go-exif/v3(все распознаваемые теги, нормализация чисел), хранение вphotos.exif(миграция000003_photo_exif, jsonb'{}'по умолчанию), отдача в API фото; дата съёмки и GPS дублируются вtaken_at/координаты. Проверка: реальное фото HUAWEI nova 15 Pro даёт 67 атрибутов;go test ./.... - 8.10 Бэкфилл метаданных существующих фото: команда
cmd/backfill-exif+storage.Get+photos.Store.BackfillEXIF(тянет оригиналы из S3, заполняетexif/taken_at/координаты). Проверка: прогон обновил 25 фото,taken_atпроставлен. - 8.11 Раздел «Информация» показывает все EXIF-атрибуты с русскими подписями, переводом enum и единицами (выдержка
1/N,f/N,N мм, EV); служебные/дублирующие теги скрыты. Проверка: Playwright на реальном фото — 9/9.
9. Настройки информации пользователя
- 9.1 Миграция
000004_user_preferences(up/down):ADD COLUMN preferences jsonb NOT NULL DEFAULT '{}'. Проверка:make migrateприменяется,\d usersпоказывает колонку, down её снимает. - 9.2
models.User(+Preferences),auth/store.go(выборкаpreferencesвGetUserByID, методUpdatePreferences),auth/handlers.go/service.go(Meвозвращаетpreferences;PATCH /auth/me/preferencesс валидацией структуры, длины подписей/ключей и числа скрытых). Проверка: невалидный payload → 400, валидный сохраняется. - 9.3 Маршрут
PATCH /auth/me/preferencesвapp.goпод middleware. Проверка: без токена 401, с токеном сохранение и чтение согласованы. - 9.4 Frontend:
types.ts(+preferences), auth-стор (+preferences, сохранение через API). Проверка:cd frontend && npm run check. - 9.5
PanelSection— слотactionsдля шестерёнки; новыйPhotoInfoSettingsDialog(список атрибутов, видимость, подписи, сброс к дефолту). Проверка: изменения применяются к разделу и сохраняются в профиле. - 9.6
AlbumPanel: фильтрация атрибутов по оверрайдам, элемент «…» для показа скрытых (при отсутствии скрытых — не показывается). Проверка: скрытые сворачиваются, «…» раскрывает все, дефолт — все видимы. - 9.7 Проверка:
cd frontend && npm run check,cd backend && go test ./...,make build,openspec validate "lightroom-workspace-ui". - 9.8 Базовый набор атрибутов по умолчанию при пустых настройках (модель
visibleвместоhidden); в диалоге — иконки «отметить все»/«снять все» вместо «Показать всё»; «Сбросить» удаляет настройки из профиля (PATCH {}). Проверка: Playwright — дефолт даёт базовый набор и «…», снятие/отметка всех, сброс очищает профиль (12/12). - 9.9 Выбор фото в сетке: одиночный клик выделяет фото рамкой и показывает его сведения в правой панели, двойной — открывает просмотр; стор
selection, сброс при смене альбома. Проверка: Playwright — выделение без навигации, панель показывает инфо, двойной клик открывает просмотр (5/5). - 9.10 Вьюер и модалки: убраны таймеры автоскрытия (панели/стрелки мгновенно), стрелки появляются только в границах вьюера,
f/кнопка переключают вложенный fullscreen вьюера (не выключают режим приложения); модалки рендерятся порталом вdocument.body(центрирование во вьюпорте). Проверка: Playwright — стрелки 4/4, fullscreen+f 6/6, центрирование 2/2.