# Улучшение графики локальной нейросетью (дизайн) Клиент у себя поднимает генератор изображений (SwarmUI; также ComfyUI и A1111), браузер ходит к нему напрямую — как к `local-translator` ([LOCALIZATION.md](LOCALIZATION.md)). Сервер библиотеки о генераторе не знает; он получает только готовые файлы через уже существующую замену ассетов в моде (`PUT /api/versions/{id}/asset`, [MODDING.md](MODDING.md)). Два режима поверх одного движка задач: | Режим | Что делает | Где хранится результат | Видят другие | |---|---|---|---| | **На лету** | плеер подменяет картинку при показе на улучшенную | IndexedDB браузера, на сервер ничего не уходит | нет | | **В исходниках** | редактор «Улучшение графики» массово обрабатывает файлы мод-форка | блобы мода (через замену ассета) | да, как обычный мод | ## Схема ``` Браузер ──► SwarmUI / ComfyUI / A1111 (127.0.0.1, разрешён CORS для сайта) │ ├─ IndexedDB: кэш результатов key = версия|путь|хэш оригинала|хэш пресета ├─ плеер: engine.fileUrl() → кэш, если есть улучшение └─ редактор: результат → PUT /asset (мод-форк), по одному файлу ``` ## Модули клиента (`frontend/src/imager/`) * `backends/` — интерфейс `ImageBackend { health(), models(), process(png, preset, signal) → Blob }` и адаптеры `swarm.ts` (основной: `GetNewSession`, `GenerateText2Image` с `initimage`, `refinerupscale`/`refinerupscalemethod`, `ListT2IParams`), `comfy.ts`, `a1111.ts`. * `presets.ts` — пресет = бэкенд + режим (`upscale` | `img2img`) + параметры (модель, апскейлер, множитель, creativity, prompt/negative, steps, cfg). Хранится в localStorage, экспорт в JSON. `hash(preset)` входит в ключ кэша. * `queue.ts` — очередь задач: параллелизм 1 (GPU), отмена, повтор, возобновление после перезагрузки (журнал в IndexedDB), прогресс. * `cache.ts` — IndexedDB: `{key, blob, scale}`; очистка по версии. * `ImagerSetup.tsx` — страница настройки, как `TranslatorSetup` (проверка связи, CORS-подсказка). * `ImageEnhance.tsx` — редактор массовой обработки (ниже). ## Масштаб изображения (важно) Плеер рисует `` в натуральном размере (`renderer.ts`), поэтому ×2 апскейл без поправки покажется вдвое больше. Решение: у каждой улучшенной картинки хранится `scale` (отношение новой ширины к исходной). * на лету — `scale` лежит в записи кэша; * в исходниках — `game.json → assetScale: { "images/bg room.webp": 2 }` (проверяется `mods.validate`); * рендерер: после загрузки `width = naturalWidth / scale` для `` без явного размера; размеры из `def.size`, ATL и масок масштабом не затрагиваются. Это единственное изменение плеера и схемы PGVN; без него замена файла на файл другого размера ломает вёрстку. ## Режим «на лету» Хук в `Engine.fileUrl()`: если для `(версия, логический путь)` в кэше есть результат — возвращается `blob:` URL (создаётся один раз, освобождается в `destroy`). Предзагрузка ближайших картинок уже использует `fileUrl`, поэтому работает без изменений. Фоновая обработка: при показе картинки без улучшения в очередь ставится её задача (опция «улучшать по ходу игры»), результат подхватывается при следующем показе. Переключатель «оригинал / улучшенное» в меню плеера для сравнения. ## Режим «в исходниках» (редактор) Страница доступна для `kind=mod` с `versions.fork`: 1. Список изображений версии (фильтр по папке/размеру/уже обработанные), миниатюры. 2. Выбор пресета, предпросмотр «до/после» на одном файле (слайдер). 3. Запуск пачки: для каждого файла — скачать оригинал → бэкенд → превью в списке → при «Применить» загрузка в мод. Загрузка последовательная (каждая замена меняет `revision`; клиент берёт актуальную из ответа). Для скорости позже — один пакетный эндпоинт `POST …/assets` с одной ревизией. 4. Откат — обычная история ревизий; «вернуть оригинал» = восстановление блоба из родителя. ## Что нужно поменять на сервере * `converter/assets._image`: не пересжимать результат повторно в WebP q88 (потеря качества после апскейла). Для замены из этого редактора принимать PNG/WebP без перекодирования лосслесс-веткой (параметр `lossless=1` в `/asset`). * лимит `WEBP_MAX = 16383`: при апскейле ×4 больших фонов результат уйдёт в PNG — предупреждать в UI. * `assetScale` в `game.json` и его проверка в `mods.validate`. * пакетный эндпоинт замены ассетов (вторая очередь работ). ## Риски и открытые вопросы * **CORS**: проверить, какие заголовки отдают SwarmUI/ComfyUI/A1111 при запросах с чужого origin; страница настройки должна объяснять флаг (`--enable-cors-header`, `--cors-allow-origins`, настройка Swarm). Mixed content: сайт по https → локальный http; Chrome разрешает для `127.0.0.1`, Safari нет. * **Качество**: img2img на персонажах с альфой — нейросеть не сохраняет прозрачность. Для спрайтов режим «только апскейлер» (модель ESRGAN-типа) с отдельной обработкой альфа-канала (апскейл маски обычным ресайзом, цвета — моделью). Это должно быть в первой версии, иначе спрайты получат ореол. * **Анимации** (animated WebP) и видео — вне первой версии. * **Права на результаты**: производные изображения остаются в моде пользователя, в публичные версии не попадают без `versions.publish`. ## Этапы 1. Каркас: `imager/` (клиент SwarmUI, пресеты, очередь, кэш), `ImagerSetup`. 2. `assetScale` в схеме, рендерере и валидаторе; режим «на лету» в плеере. 3. Редактор массовой обработки + загрузка в мод + лосслесс-замена. 4. Адаптеры ComfyUI и A1111, обработка альфы, пакетный эндпоинт. ## Реализация (25 октября 2026) Добавлены все четыре группы функций: * `/imager` — настройка локального генератора, проверка API из браузера, список моделей и апскейлеров, пресеты с импортом/экспортом JSON. Последний сохранённый пресет используется плеером. * `frontend/src/imager/` — адаптеры SwarmUI, ComfyUI и A1111, общая очередь, журнал и кэш IndexedDB. Ключ включает ID версии, логический путь, хэш исходного блоба и SHA-256 параметров пресета (включая адрес генератора и фиксированный seed). Переименование пресета кэш не меняет. * Плеер PGVN: Настройки → Графика → «Показывать улучшенные изображения» и отдельно «Улучшать по ходу игры». Переключатели сохраняются по версии. По умолчанию оба выключены. Завершённые задания подхватываются при следующем показе изображения; URL освобождаются при закрытии. * `/versions/:versionId/enhance` — выбор картинок, фильтры пути/размера файла/готовности, выбор пресета, сравнение слайдером, обработка выбранных файлов и отдельное применение. Открывается из редактора мода или его карточки. Применять можно только в доступный для редактирования черновик. Ошибки ревизий не удаляют локальные результаты. * `PUT /api/versions/{id}/asset?lossless=1&scale=2` сохраняет проверенные статичные PNG/WebP без повторного сжатия. `scale` — полный масштаб относительно исходных игровых координат, поэтому повторный апскейл умножает прежний `assetScale`. Файл и метаданные пишутся одной ревизией. Обычная ручная замена без `scale` сбрасывает прежний масштаб этого файла. * `POST /api/versions/{id}/assets`: JSON `{revision, assets: [{path, filename, data, scale}]}`, где `data` — base64 без префикса data URL. Максимум 32 картинки / 128 МБ запроса; редактор дополнительно ограничивает группу 80 МБ исходных блобов. Каждый пакет атомарен и создаёт одну ревизию. При ошибке файлы версии и её ревизия не меняются. * `POST /api/versions/{id}/asset/restore`: `{revision, path}` возвращает файл **из замороженной базовой ревизии форка**, вместе с прежним масштабом. Для отката пачки также доступна история мода. * Альфа-канал масштабируется отдельно в браузере и возвращается в итоговый PNG. Img2img прозрачных изображений отклоняется с предложением выбрать апскейлер. Анимации отклоняются, а не превращаются незаметно в первый кадр. Изменение пропорций генератором также отклоняется. Отмена активного задания отбрасывает его результат, но ждёт завершения запроса перед стартом следующего: глобальный interrupt генератора мог бы остановить чужую работу. После перезагрузки незавершённые задания видны в журнале и запускаются кнопкой «Повторить». При наличии Web Locks очередь сериализует GPU-запросы также между вкладками этого сайта. При очистке кэша нужно закрыть плеер, чтобы освободить уже загруженные в память blob-URL. Текущие адаптеры рассчитаны на локальные API без обязательной авторизации. Прямые обращения генератора не проксируются через сервер библиотеки. Для SwarmUI настройка CORS называется `Network.AccessControlAllowOrigin`; для ComfyUI — `--enable-cors-header ORIGIN`, для A1111 — `--api --cors-allow-origins ORIGIN`. Страница настройки проверяет доступ к API, а скачивание результата проверяется первым пробным заданием. Реальный SwarmUI на `127.0.0.1:7801` при разработке не отвечал: GPU-генерация и CORS установленного сервиса пока не подтверждены. Протоколы адаптеров покрыты тестовыми HTTP-ответами. Проверки: `frontend/tests/imager.test.mjs`, `frontend/tests/imager-browser.test.mjs` (опциональный Playwright/Chromium, как существующие браузерные тесты), `backend/tests/test_image_enhance.py`. Браузерный тест проверяет реальные пиксели альфы, геометрию ``, сохранность IndexedDB после перезагрузки, освобождение URL и отсутствие загрузок на сервер в локальном режиме. Результат проверки реализации: 9 клиентских тестов imager, 13 серверных тестов imager/mods и отдельный тест Chromium проходят; production-сборка и Ruff проходят. Интеграционный тест с PostgreSQL пропущен без `TEST_POSTGRES`. В полном клиентском наборе отдельно воспроизводится сбой изменённого теста интерпретатора Ren'Py `interpreted instances can own arguments without exposing host function properties` (`frontend/tests/renpy-runtime.test.mjs`, неизвестное имя `list`); этот тест не импортирует imager. Первичная документация протоколов: [SwarmUI API](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/API.md), [параметры SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/APIRoutes/T2IAPI.md), [ComfyUI API](https://github.com/comfyanonymous/ComfyUI/blob/master/script_examples/basic_api_example.py), [A1111 API](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/api/models.py).