- synced delta specs into main specs: albums (+3 reqs, MODIFIED deleting album -> photos to trash), auth (+1), photos (+8), and created new ui-workspace spec (29 reqs) - moved the change to openspec/changes/archive/2026-10-10-lightroom-workspace-ui/
122 lines
13 KiB
Markdown
122 lines
13 KiB
Markdown
# Proposal
|
|
|
|
## Why
|
|
|
|
Сейчас интерфейс — это центрированный «маркетинговый» лейаут: список альбомов с
|
|
инлайн-формой создания, серая заглушка вместо обложек, отдельные страницы «Участники» и
|
|
«Настройки», лайтбокс-оверлей. Обложки в бэкенде уже есть (`albums.cover_photo_id`,
|
|
`PATCH /albums/:id`), но до UI не доходят. Пользователь хочет полноценный «рабочий стол»
|
|
в духе Adobe Lightroom: постоянный рейл альбомов, контекстная панель, плотная сетка,
|
|
режим просмотра как часть рабочего стола, обложки с явным выбором и единый тёмный
|
|
графитовый хром.
|
|
|
|
## What Changes
|
|
|
|
- **Рабочий стол (шелл).** Группа `(app)` с постоянным левым рейлом альбомов, топбаром и
|
|
контекстной правой панелью. Публичные маршруты (`/login`, `/invites/[token]`,
|
|
`/share/[token]`) остаются вне шелла, но в тех же токенах.
|
|
- **Создание альбома.** Инлайн-форма заменяется иконочной кнопкой с хинтом, открывающей
|
|
модалку ввода названия.
|
|
- **Обложки.** `Album` получает `cover_thumb_url`; карточки и рейл показывают реальную
|
|
миниатюру. Выбор обложки: пикер в правой панели (действие «сделать обложкой» в
|
|
просмотре не используется). Автообложка пишется только когда обложка ещё не задана; FK
|
|
`albums.cover_photo_id -> photos(id) ON DELETE SET NULL` чинит висячие ссылки.
|
|
- **Просмотр как маршрут.** `Lightbox`-оверлей заменяется маршрутом
|
|
`/albums/:id/photos/:photoId` внутри шелла: крупное фото, filmstrip снизу, панель
|
|
метаданных/комментариев. `share/[token]` переиспользует тот же компонент.
|
|
- **Плотность сетки.** Слайдер размера миниатюр в тулбаре (состояние в localStorage).
|
|
Мультивыбор и массовые действия — вне объёма.
|
|
- **Загрузка.** Крупный dropzone заменяется кнопкой в тулбаре, полноэкранным
|
|
drag-overlay и dropzone-намёком только в пустом альбоме. Лимит — 100 МБ с понятной
|
|
ошибкой при превышении.
|
|
- **Переименование фото.** `PATCH /photos/:id` меняет `original_filename`; инлайн-редактор
|
|
в правой панели, доступный автору фото и owner/admin.
|
|
- **Метаданные фото (EXIF).** При загрузке система извлекает все распознаваемые EXIF-атрибуты
|
|
(камера, объектив, экспозиция, ISO, дата съёмки, GPS и др.) и хранит их в `photos.exif`;
|
|
они возвращаются вместе с фото и показываются в разделе «Информация». Для ранее загруженных
|
|
фото есть команда бэкфилла метаданных из оригиналов.
|
|
- **Метки фото.** Пользователь присваивает фото свои метки: создаёт новые и выбирает из списка
|
|
с поиском; метки показываются и снимаются в разделе «Метки» панели фото.
|
|
- **Контекстные меню.** Правый клик по фото (в сетке и в просмотре) открывает меню с пунктами
|
|
раздела «Действия»; правый клик по альбому (в рейле и на обзоре) — меню «Переименовать»/
|
|
«Удалить».
|
|
- **Удаление альбома.** Удаление альбома помещает его фото в корзину, не удаляя объекты
|
|
хранилища; при восстановлении фото без альбома предлагается выбрать существующий альбом или
|
|
создать новый.
|
|
- **Просмотр.** Полноэкранный режим (кнопка и `f`) с автоскрытием верхней/нижней панелей и
|
|
стрелок у кромок; зум колесом к указателю (без прокрутки, максимум 200%), `Space`
|
|
возвращает из любого зума во вписанный вид, в зуме догружается оригинал изображения;
|
|
действия просмотра (скачать, удалить, перенести, копировать) — в правой панели, в разделе
|
|
«Действия»; удаление с модальным подтверждением; возврат к обзору.
|
|
- **Разделы и панели.** Правая панель — сворачиваемые разделы (состояние сохраняется), с
|
|
разделом «Информация» (EXIF/метаданные); левая и правая панели ресайзятся перетаскиванием.
|
|
Выбор фото в сетке показывается в панели (одиночный клик), двойной клик открывает просмотр.
|
|
- **Настройки информации.** Раздел «Информация» получает кнопку-шестерёнку, открывающую
|
|
модалку со списком EXIF-атрибутов: галочки видимости (с действиями «отметить все»/«снять
|
|
все»), пользовательские подписи и сброс. Скрытые атрибуты сворачиваются под элементом «…»
|
|
(клик раскрывает все). Настройки хранятся в профиле пользователя (server-side), применяются
|
|
ко всем фото, а при отсутствии настроек показывается базовый набор; «Сбросить» удаляет
|
|
настройки из профиля.
|
|
- **Мультивыбор и операции.** Ctrl/Shift-выбор нескольких фото в сетке; в правой панели —
|
|
раздел «Действия» с пунктами «Открыть (просмотр)», «Переместить», «Копировать», «Удалить»;
|
|
перетаскивание выделенных фото на альбом в рейле открывает диалог перенос/копирование.
|
|
Перенос и копирование меняют только метаданные (объекты S3 не трогаются). Любое удаление
|
|
помещает фото в корзину; объекты S3 удаляются лишь когда на них нет ссылок в других
|
|
альбомах. Корзина — последний элемент списка альбомов в левом рейле (иконка корзины); на
|
|
обзоре убрана пустая правая панель.
|
|
- **Счётчики.** В ответе альбома — число фото и участников; в рейле у каждого альбома показано
|
|
число фото, у корзины — число удалённых фото.
|
|
- **Темы и хром.** Существующие 4 темы сохраняются; `dark` — графит Lightroom и дефолт,
|
|
`neutral` — тёплый графит; переключатель упорядочен тёмная → нейтральная → светлая →
|
|
системная; добавляются токены `--chrome`/`--canvas`; скроллбары стилизованы в тон темы.
|
|
Верхняя панель приложения и верхняя панель режима просмотра — компактные (минимум высоты).
|
|
- **Адаптив.** На узких экранах рейл становится off-canvas drawer, правая панель —
|
|
bottom-sheet, filmstrip — горизонтальным скроллом.
|
|
|
|
## Capabilities
|
|
|
|
### New Capabilities
|
|
|
|
- `ui-workspace`: рабочий стол приложения — шелл, рейл альбомов, контекстная панель,
|
|
режим просмотра как маршрут, адаптивное поведение и тёмный хром.
|
|
|
|
### Modified Capabilities
|
|
|
|
- `albums`: отображение обложки, счётчики фото/участников, явный выбор обложки и удаление в
|
|
корзину.
|
|
- `photos`: автообложка только при отсутствии выбора; переименование фото; лимит размера
|
|
загрузки; извлечение и хранение EXIF-метаданных; перенос/копирование, корзина и метки.
|
|
- `auth`: хранение пользовательских настроек отображения информации в профиле.
|
|
|
|
## Impact
|
|
|
|
- **Backend:** миграция FK `albums.cover_photo_id -> photos(id) ON DELETE SET NULL`;
|
|
`internal/albums/store.go` (LEFT JOIN `photos` → `cover_thumb_key` и счётчики
|
|
`photos_count`/`members_count` в `ListForUser`/`GetByID`); `internal/albums/service.go`
|
|
(presign → `CoverThumbURL`, обогащение списка); `internal/models` (`Album.CoverThumbURL`,
|
|
`PhotosCount`, `MembersCount`); `internal/photos` (автообложка только при
|
|
`cover_photo_id IS NULL`; `PATCH /photos/:id` для переименования; лимит загрузки 100 МБ с
|
|
`413 file_too_large`; событие `photo.updated`; извлечение EXIF через
|
|
`github.com/dsoprea/go-exif/v3` в `photos.exif`, миграция `000003_photo_exif`, команда
|
|
`cmd/backfill-exif` и `storage.Get`); миграция `users.preferences`,
|
|
`models.User.Preferences`, `internal/auth` (`Me` отдаёт настройки,
|
|
`PATCH /auth/me/preferences` с валидацией); миграции `000005_photo_trash` (мягкое удаление),
|
|
`000006_photo_album_nullable` (FK `photos.album_id` → `ON DELETE SET NULL`, удаление альбома
|
|
в корзину), `000007_photo_tags` (метки); `GET /trash`/`GET /trash/count`,
|
|
`POST /photos/:id/restore` (c `album_id`), `DELETE /photos/:id/permanent`,
|
|
`POST /photo-batch/{delete,move,copy}`, `GET /tags`, `POST/DELETE /photos/:id/tags`.
|
|
- **Frontend:** реструктуризация `+layout.svelte` в группы `(app)`/`(public)`, `AppShell`
|
|
с ресайзом панелей и кнопкой полного экрана; компоненты рейла, контекстной панели со
|
|
сворачиваемыми разделами и EXIF, модалки создания/пикера обложки/подтверждения,
|
|
`Viewer` с полноэкранным режимом и зумом, общий стор альбомов, контрол плотности; новый
|
|
маршрут `albums/[id]/photos/[photoId]`; слот действий в `PanelSection`, модалка настроек
|
|
атрибутов с пользовательскими подписями, стор настроек, фильтрация и «…» в панели;
|
|
новые значения токенов темы и стили скроллбаров; `types.ts` (+`cover_thumb_url`, счётчики,
|
|
`photos_count`, `preferences`, `tags`); общие компоненты `PhotoPanel`, `ContextMenu`,
|
|
`TransferDialog`, `AlbumActionsDialogs`; маршрут `/trash`; сторы `selection`, `transfer`,
|
|
`contextMenu`, `albumActions`, `trash`, `viewerActions`; новые иконки в реестре.
|
|
- **Спеки:** `ui-workspace` (новая), `albums`, `photos`, `auth` (дельты). `ui-iconography` не
|
|
затрагивается — иконки по-прежнему наследуют цвет темы.
|
|
- **Совместимость:** API-контракты сохраняются; добавляется поле ответа и один FK. Ломающих
|
|
изменений API нет.
|