• 0.1-alpha Pre-Release

    iu released this 2026-10-21 18:29:36 +00:00 | 14 commits to develop since this release

    Описание релиза Galo — 21 октября 2026

    База релиза: main (84ce4d1, 10 октября 2026) → develop (afe85f7, 21 октября 2026)
    Объём: 138 коммитов, 4 спецификации обновлены, 1 изменение реализовано


    Коротко о главном

    1. Локальный режим без внешних сервисов. Слой метаданных переключается на
      встроенный SQLite, хранилище блобов — на локальную файловую систему. По умолчанию
      приложение работает полностью на одной машине: без Postgres и без S3.
    2. Редактирование фотографий в браузере. Вкладка «Редактирование» с инструментом
      «Свет»: экспозиция, контраст, света, тени, белые и чёрные, тональная кривая по
      каналам с живой гистограммой. Правки видны сразу, без ожидания сервера.
    3. Новый интерфейс рабочего пространства. Альбомы с обложками, тёмная, светлая и
      нейтральная темы, единая система векторных иконок, гало-эмблема и фавикон.
    4. Нативный Android-клиент. Отдельное приложение: просмотр альбомов, загрузка с
      устройства, двусторонняя синхронизация с сервером, безопасная разгрузка превью.
    5. Видео и офлайн. Видео в общих альбомах с фоновой подготовкой и перемоткой;
      офлайн-кеш оболочки, списков и превью.

    Новые возможности

    Локальный режим: SQLite и файловая система

    • Слой метаданных выбирается конфигурацией: внешний PostgreSQL или встроенный
      SQLite с тем же поведением для одного экземпляра приложения.
    • Хранилище блобов выбирается отдельно: S3-совместимое (Garage) или локальная
      файловая система. Клиент никогда не видит хранилище — блобы всегда отдаются через
      бэкенд по подписанному URL.
    • По умолчанию (DB_BACKEND=sqlite, STORAGE_BACKEND=fs) приложение поднимается без
      Postgres и S3: данные лежат в galo.db и каталоге data/.
    • Секрет подписи JWT и URL объектов генерируются при первом старте и хранятся в
      слое метаданных — переживают перезапуск и рестарт SQLite.
    • Реконсиляция хранилища с журналом объектов: make reconcile (отчёт),
      make reconcile-delete (удаление осиротевших блобов). Сборку мусора ведёт журнал,
      хранилище не сканируется.

    Слой объектов

    • Физическое объектное хранилище с непрозрачными ключами: идентификаторы альбомов и
      фото в ключах не участвуют, сопоставление живёт только в метаданных.
    • Журнал объектов: у каждого объекта ровно одна запись с ключом, MIME-типом,
      размером, габаритами и состоянием; фото ссылается на объекты по ролям
      (original, thumb, medium, preview, playback, edit, edit_thumb,
      edit_medium, edit_preview).
    • Один физический объект может разделяться несколькими фото: перенос и копирование
      меняют только ссылки, хранилище не трогается.
    • Сборка мусора по ссылкам: объект удаляется, когда на него не ссылается ни одно фото
      и ни один аватар.

    Редактирование фотографий

    • Вкладка «Редактирование» в правой панели фото, инструмент «Свет»: экспозиция,
      контраст, света, тени, белые, чёрные.
    • Тональная кривая: выбор канала (R/G/B/мастер), точки добавляются кликом по
      полю графика, перетаскиваются и двигаются стрелками с клавиатуры.
    • Гистограмма каналов пересчитывается на лету с учётом текущих правок.
    • Блок «Кривая» сворачивается (по умолчанию свёрнут), кнопки выбора канала остаются
      доступными.
    • Предпросмотр правок рисуется в браузере — WASM-модулем на Rust, с резервной
      JS-реализацией, если модуль недоступен или считает иначе.
    • Правки применяются сразу; на сервер уходят через 10 секунд без изменений или при
      переключении фото. Сервер рендерит отредактированную версию в фоне отдельной
      очередью и сообщает о готовности.
    • Переключатель «Сброс» удаляет и параметры правок, и отредактированную версию.
    • Редактирование неразрушающее: оригинал остаётся неизменным, скачивание отдаёт
      исходник. Рендер идёт по исходному объекту, а не по уменьшенной копии.

    Интерфейс рабочего пространства

    • Альбомы с обложками, «лайтрумовская» компоновка: рельс альбомов, сетка, единая
      панель фотографии.
    • Три темы — тёмная, светлая, нейтральная — с семантическими токенами; переключатель
      тем переехал в шапку, нейтральная тема затемнена.
    • Единая система иконок: встроенная векторная графика (Material Symbols) вместо
      эмодзи и символов системного шрифта — иконки наследуют цвет темы и не тянут
      внешние ресурсы.
    • Гало-эмблема (диск солнца в отделённом кольце) и фирменный фавикон: отдаются
      самим приложением, наследуют тему, читаются в размере 16–24 px.
    • Панель сведений о фото со сворачиваемыми секциями и настраиваемым набором
      атрибутов: состав EXIF хранится в профиле пользователя.
    • Полноэкранный просмотр: своя панель, кнопка «на весь экран», возврат по Esc.

    Просмотр и навигация

    • Зум колесом мыши (с привязкой к курсору, до 200%), клавишами + и -;
      Space возвращает во вписанный вид, а из вписанного — в 100% от оригинала по
      центру кадра.
    • В зуме стрелки клавиатуры панорамируют фото, переключение соседних снимков
      остаётся за мышью и кнопками у краёв.
    • Скрытие боковых панелей альбома: кнопка в панели сетки и хоткей Tab. Панели
      уезжают за края, содержимое занимает всю ширину, панели возвращаются при наведении
      на кромку окна. Размер миниатюр при этом сохраняется — в строке помещается больше
      фото.
    • Лента миниатюр в просмотре следует за текущим фото; на превью работает наведение-
      зум.
    • Полноэкранный режим приложения по Ctrl+F, навигация с клавиатуры в сетке с
      возвратом фокуса, открытие фото тапом на тач-экранах.
    • Просмотр 360-панорам по тегу pano360, корректное направление перетаскивания.
    • Пока грузится оригинал для зума — спиннер; на Android — щипок-зум.

    Работа с фотографиями

    • Мультивыбор в сетке, контекстные меню, перенос и копирование снимков, корзина
      на уровне альбома и общие счётчики.
    • Теги фото: правки расходятся по сетке и просмотру.
    • Переименование фото и удаление из просмотра с подтверждением.
    • EXIF извлекается полностью при загрузке; для старых снимков — дозаполнение
      (cmd/backfill-exif).

    Офлайн-режим

    • Service worker кеширует оболочку приложения, списки и эскизы/превью фотографий:
      открытые альбомы и ранее загруженные снимки доступны без сети.
    • Модуль фильтров в офлайне не кешируется — он всегда подтягивается свежим.

    Видео

    • Загрузка видео в те же альбомы, что и фото (kind=video).
    • Фоновая подготовка: постер (кадр кадра → обычные варианты сетки) и web-совместимый
      вариант для воспроизведения (ремукс без перекодирования либо транскод в
      h264/yuv420p/aac с +faststart), с поддержкой перемотки (Range).
    • Транскод идёт в отдельном малом пуле и не вытесняет генерацию вариантов
      изображений; готовность транслируется подписчикам альбома.
    • Без ffmpeg/ffprobe приём видео отклоняется, /api/health сообщает
      "video": "unavailable", изображения работают как прежде.

    Android-клиент

    • Отдельный Gradle-модуль, работает с существующим HTTP API, сервер и веб не
      трогает. Kotlin + Jetpack Compose, minSdk 28, targetSdk 35.
    • Двусторонняя синхронизация альбома устройства с серверным альбомом: загрузки,
      изменения состава, удаления.
    • Безопасная разгрузка: с устройства уходит превью, оригинал остаётся на сервере;
      потеря серверной копии распознаётся и показывается в интерфейсе.
    • Статус синхронизации на карточке альбома, ход восстановления оригиналов,
      сортировка списка, обновление списка альбомов в реальном времени.
    • Установка на устройство одной командой: make install-android-app (с выбором
      серийного номера при нескольких подключённых устройствах).

    Профиль и учётная запись

    • Профиль пользователя: круглый аватар с загрузкой и кадрированием, публичная отдача
      аватара по версионированной ссылке.
    • Вход и регистрация по телефону (E.164) в дополнение к email, управление
      идентификаторами, удаление аккаунта.
    • Предпочтения пользователя (в том числе состав панели сведений) хранятся на сервере.

    Альбомы и живое обновление

    • Обложки альбомов: отдельный жизненный цикл, обложка доводится до готовности и
      доставляется подписчикам.
    • Живое обновление: события жизненного цикла альбома и изменения состава фото через
      SSE, подписчики видят изменения без перезагрузки.
    • Квадратный вариант medium — резкие миниатюры в сетке; шаг переключения размера
      миниатюр и направление ползунка приведены в порядок.
    • Быстрая загрузка больших альбомов.

    Наблюдаемость и эксплуатация

    • Настраиваемое логирование: LOG_LEVEL (debug включает access-логи и ожидаемые
      4xx), LOG_FORMAT (json/text), request id в ответах и логах.
    • scripts/seed.py и make testdata — наполнение запущенного инстанса тестовыми
      данными через публичный API.
    • Release-сборка: make build-release — статичный бинарник с вшитой версией и
      UPX-сжатием, если он установлен.

    Исправления

    • Копия фото больше не наследует отредактированную версию. Раньше копирование
      переносило все привязки объектов, включая edit*: копия показывала
      отредактированную картинку, но не наследовала параметры правок, и первая же новая
      правка перезаписывала обработку с нуля. Теперь правка принадлежит своему фото, а
      копия показывает исходник. Разовая миграция убирает привязки у копий, созданных до
      фикса.
    • Предпросмотр правок работает и без зума, а не только в увеличенном виде.
    • Устранено искажение цветов в предпросмотре (WASM-фильтры считались по
      преобразованным каналам).
    • Гистограмма пересчитывается при правке, а не остаётся от исходного снимка.
    • Линия тональной кривой видима в тёмной теме.
    • Починен зум в просмотре: контейнер прокрутки и центрирование.
    • Android: разгруженное превью больше не считается новой версией элемента.
    • Сервер отдаёт контрольную сумму оригинала в ответе на загрузку — клиент может
      сверить файл.
    • Счётчик корзины и правки тегов синхронизируются с сеткой и просмотром.
    • Ошибка загрузки крупного файла показывается явно, лимит поднят.
    • make run пересобирает фронтенд, если он изменился.

    Технические изменения

    API

    Новые эндпоинты:

    • PATCH /api/photos/:id/edit — сохранить параметры правок (тело: {"light": {...}}).
    • DELETE /api/photos/:id/edit — сбросить правки вместе с отредактированной
      версией.
    • GET|PATCH|DELETE /api/auth/me, POST /api/auth/me/password,
      POST /api/auth/me/identifiers, PATCH /api/auth/me/preferences,
      POST|DELETE /api/auth/me/avatar, GET /api/avatars/:userID/:version?size=large.
    • POST /api/photo-batch/copy, POST /api/photo-batch/move — пакетные операции над
      снимками.

    Изменившиеся ответы: у фото появились поля edit_params, edit_url,
    edit_thumb_url, edit_medium_url, edit_preview_url; у видео — playback_url.

    Схема данных и миграции

    • Postgres: миграции до 000016; SQLite: до 000008 (нумерация диалектов
      независимая, миграции вшиты в бинарник и применяются автоматически при старте;
      make migrate применяет только Postgres).
    • Новые роли объектов edit, edit_thumb, edit_medium, edit_preview — рядом с
      обычными original, thumb, medium, preview, playback.
    • Новая колонка photos.edit_params — параметры неразрушающих правок.

    Сборка

    • Фильтры правок считает WASM-модуль из frontend/wasm/light (Rust, no_std).
      Пересборка нужна только при правке самого модуля; в репозитории лежит готовый
      light.wasm, поэтому фронтенд собирается без Rust-инструментов.
    • npm run wasm — отдельная пересборка модуля; обычная сборка фронтенда обновляет
      его сама.

    Как обновиться

    1. Собрать бинарник: make build (фронтенд → backend/static → go:embed → bin/galo)
      или make build-release — с вшитой версией.
    2. Остановить сервис, заменить бинарник. Фронтенк вшит в бинарник, отдельно
      выкладывать нечего.
    3. Запустить: миграции применятся автоматически при старте для выбранного бэкенда.
      Для Postgres можно отдельно прогнать make migrate.
    4. Для видео нужен ffmpeg/ffprobe на хосте; без них приём видео отключается.
    5. Android-клиент ставится отдельно: make install-android-app.

    Откат: бинарник возвращается предыдущей версии; миграции down не восстанавливают
    удалённые привязки копий — это безопасно, отредактированные версии исходных фото
    остаются на месте.


    Ограничения

    • Редактирование — только для изображений; видео не редактируется.
    • Правки не порождают отдельной записи в истории: параметры хранятся в метаданных
      фото, отредактированная версия — как отдельный объект.
    • Офлайн-кеш ограничен оболочкой, списками и эскизами/превью: оригиналы для зума и
      скачивания требуют сети.
    • Android-клиент требует JDK 17 и Android SDK (platforms;android-35,
      build-tools;35.0.0).
    • Номер версии берётся из git describe --tags; в репозитории пока нет тегов, поэтому
      версия в сборке — короткий SHA или dev. Для осмысленной нумерации нужен тег.

    Для разработчиков

    • Спецификации OpenSpec синхронизированы с поведением: обновлены ui-workspace
      (редактирование, кривая, зум и навигация), media-objects (роли edit*, раздельное
      владение правками, отдельная очередь рендера), photos (рендер по оригиналу),
      dev-make-workflow (сборка WASM-модуля).
    • Изменение fix-copy-edit-variant-sharing реализовано (9/9 задач) и ждёт ревью и
      архивации: openspec list показывает его как complete.
    • Контрактные тесты репозиториев (internal/repo/contract) гоняют один набор
      проверок на SQLite и Postgres — новый кейс Clone проверяет, что копия не
      наследует отредактированную версию, в обоих хранилищах.
    • Интеграционные тесты Postgres требуют TEST_DATABASE_URL; без них они
      пропускаются. Тесты e2e поднимают SQLite в t.TempDir() и внешних сервисов не
      требуют.
    • Сервис переименован в galo, бренд — «гало»; сборка make build-release пишет
      bin/galo-$(VERSION).
    Downloads