chore(openspec): sync lightroom-workspace-ui with implemented changes
Reflect post-planning work in proposal, design, specs (albums/photos/ ui-workspace) and tasks: photo rename, viewer delete/zoom/fullscreen, resizable and collapsible panels with EXIF, upload size limit, album counts, themes/scrollbars, back navigation.
This commit is contained in:
@@ -93,6 +93,37 @@ filmstrip, панель метаданных/комментариев, тулб
|
||||
Кнопка `+` с хинтом (в рейле и/или топбаре) открывает модалку с полем названия; `POST
|
||||
/albums`, созданный альбом становится активным. Инлайн-форма удаляется.
|
||||
|
||||
### 12. Переименование фото: `PATCH /photos/:id`
|
||||
Имя файла меняется через новый эндпоинт; права — как у удаления (`CanDeleteAnyPhoto` или
|
||||
автор своего фото). Пустое имя отклоняется, длина ограничивается 255 символами, изменение
|
||||
транслируется событием `photo.updated`. UI — инлайн-редактор в правой панели. Альтернатива —
|
||||
редактировать `caption` вместо имени — отклонена: пользователь просил имя файла.
|
||||
|
||||
### 13. Просмотр: полный экран и зум
|
||||
Полный экран — `requestFullscreen` на корне `Viewer` (кнопка + `f`), состояние — по
|
||||
`fullscreenchange`. Зум — локальные `zoomed`/`scale`: колесо меняет масштаб с привязкой к
|
||||
курсору; `Space` переключает 100% (1:1); при увеличении изображение панорамируется
|
||||
перетаскиванием и скроллбарами. Хоткеи игнорируются при фокусе в поле ввода. Зум
|
||||
сбрасывается при смене фото. Альтернатива — только `object-fit` без ручного масштаба —
|
||||
не даёт 1:1 и панорамирования.
|
||||
|
||||
### 14. Панели: ресайз и сворачиваемые разделы
|
||||
Ширина рейла и панели хранится в `localStorage`, меняется перетаскиванием разделителя
|
||||
(и стрелками с клавиатуры), ширина подаётся CSS-переменной на десктопных брейкпоинтах.
|
||||
Разделы панели — компонент `PanelSection` со сворачиванием и сохранением состояния.
|
||||
Альтернатива — фиксированные ширины и всегда развёрнутые секции — отклонена: не отвечает
|
||||
запросу.
|
||||
|
||||
### 15. Лимит загрузки
|
||||
Общий лимит тела запроса поднят до 100 МБ; при `http.MaxBytesError` возвращается
|
||||
`413 file_too_large` с понятным сообщением вместо маскировки под ошибку разбора multipart.
|
||||
Клиент заранее проверяет размер каждого файла и суммарный.
|
||||
|
||||
### 16. Счётчики альбома и событие обновления фото
|
||||
`photos_count`/`members_count` считаются подзапросами в тех же `ListForUser`/`GetByID`, чтобы
|
||||
панель обходилась одним запросом. Переименование публикует `photo.updated`, страница альбома
|
||||
обновляет запись в списке.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Много альбомов → много presign в списке] → presign это локальная крипто-операция без сети;
|
||||
@@ -109,15 +140,24 @@ filmstrip, панель метаданных/комментариев, тулб
|
||||
загрузки обложку не трогают, пользователь может задать явно.
|
||||
- [Дополнительное состояние в маршруте просмотра (filmstrip, клавиатура)] → единый компонент
|
||||
`Viewer` с props и общим источником фото.
|
||||
- [Зум/панорамирование конфликтует с навигацией и прокруткой] → при увеличении отключаем
|
||||
стрелки и свайп; колесо над областью фото перехватывается с `preventDefault`.
|
||||
- [Глобальные хоткеи при вводе текста] → игнорируем `f`/`Space`/стрелки, когда фокус в
|
||||
`input`/`textarea`/`contenteditable`.
|
||||
- [Крупные файлы утяжеляют память] → лимит 100 МБ и чтение через `LimitReader`; превышение
|
||||
даёт понятную 413.
|
||||
- [Сохранённые ширина/состояние панелей] → значения ограничиваются допустимым диапазоном.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Миграция `000002_album_cover_fk`: `UPDATE albums SET cover_photo_id = NULL WHERE
|
||||
cover_photo_id IS NOT NULL AND cover_photo_id NOT IN (SELECT id FROM photos);` затем
|
||||
`ADD CONSTRAINT ... ON DELETE SET NULL`. Down — `DROP CONSTRAINT`.
|
||||
2. Бэкенд: `models.Album` (+`CoverThumbURL`), `albums/store.go` (LEFT JOIN), `albums/service.go`
|
||||
(enrich + presign), `photos/service.go` (SetCover только если NULL).
|
||||
3. Фронтенд: каркас и маршруты, компоненты, токены, `types.ts`.
|
||||
2. Бэкенд: `models.Album` (+`CoverThumbURL`, `PhotosCount`, `MembersCount`), `albums/store.go`
|
||||
(LEFT JOIN + счётчики), `albums/service.go` (enrich + presign), `photos` (автообложка только
|
||||
если NULL; `PATCH /photos/:id` для переименования; лимит 100 МБ и `413`).
|
||||
3. Фронтенд: каркас и маршруты, компоненты (шелл с ресайзом/фуллскрином, рейл, панель с
|
||||
разделами и EXIF, модалки, `Viewer` с зумом), стор альбомов, токены и скроллбары, `types.ts`.
|
||||
4. Откат: снять constraint (down-миграция); фронтенд откатывается независимо, поле ответа
|
||||
игнорируется старым клиентом.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user