Настольное приложение на Tauri 2 + Vue 3 + Naive UI, которое решает две смежные задачи поверх локальной vision-модели в LMStudio:
- Подпись к фотографиям альбома ВКонтакте — модель смотрит на каждое фото и предлагает текстовое описание, которое затем сохраняется в поле подписи через VK API. Доступны автоматический и ручной режимы.
- Локальный «распределитель» фото — модель раскладывает кучу файлов из указанной папки по подкаталогам (например, «Документы», «Скриншоты», «Природа», «_unsorted»), руководствуясь набором правил, который формирует пользователь.
Вся тяжёлая работа (HTTP к LMStudio и VK, чтение/перемещение файлов, декодирование изображений и генерация миниатюр) выполняется на стороне Rust через Tauri-команды, поэтому интерфейс остаётся отзывчивым даже при работе с большими альбомами и папками на сотни файлов.
- Возможности
- Системные требования
- Установка и запуск
- Первичная настройка
- Режим «Подписи к фото ВК»
- Режим «Распределитель»
- Структура проекта
- Скрипты
- Сборка релиза
- Работа с локальной vision-моделью через OpenAI-совместимый API LMStudio (адрес и имя модели задаются в настройках).
- Подписи к фото ВК:
- выбор альбома и владельца, фильтрация по «без подписи»;
- автоматический режим — пакетная обработка с прогрессом, текущим фото и кнопкой отката последней правки;
- ручной режим — пошаговый просмотр с предложением модели, которое можно принять, исправить или пропустить.
- Локальный распределитель файлов:
- очередь задач — несколько папок ставятся в ряд и обрабатываются последовательно (следующая idle-задача стартует автоматически);
- шаблоны настроек — наборы правил/папок/моделей сохраняются под именем и переиспользуются как пресет для новых задач;
- построение списка целевых папок прямо моделью (по существующей структуре каталога-источника или с нуля по описанию);
- режим «оставить нераспознанные на месте» либо сложить их в папку
_unsorted; - индивидуальные переопределения модели и температуры на каждую задачу;
- превью-миниатюры (генерируются на стороне Rust в JPEG ≈10 КБ, чтобы WebView2 не декодировал тяжёлые HEIC/PNG в основном потоке).
- Открытие файла по клику и «Показать в проводнике» через
tauri-plugin-opener. - Тёмная и светлая темы Naive UI, выбор сохраняется между сессиями.
| Компонент | Минимум |
|---|---|
| ОС | Windows 10/11 (приложение собирается под NSIS-installer; теоретически портируется на Linux/macOS, но не тестировалось) |
| Node.js | 24.14+ (см. engines в package.json) |
| Rust | стабильный канал, 1.77.2+ (требование tauri-plugin-opener) |
| Tauri CLI | ставится через @tauri-apps/cli (зафиксирован в devDependencies) |
| LMStudio | любая текущая версия с включённым OpenAI-совместимым API и загруженной vision-моделью (например, Qwen2-VL, Llava, MiniCPM-V) |
| Видеокарта | желательно дискретная — vision-модель и компоновщик WebView2 делят GPU; на интегрированной графике возможны видимые подтормаживания UI во время массовой классификации |
git clone https://github.com/<your-fork>/vk-caption-ai.git
cd vk-caption-ai
npm install
npm run tauri:devКоманда tauri:dev сначала поднимает Vite-сервер на http://localhost:1420,
а затем стартует Tauri-окно, которое к нему подключается.
- Откройте вкладку Настройки в приложении.
- Заполните блок VK (нужно только для режима подписей):
- VK Token — получается на vkhost.github.io (приложение VK Admin или собственное); хранится локально, на серверы не уходит.
- VK Owner ID —
idпользователя или сообщества (для группы — со знаком минус, как принято в VK API). - VK Album ID — выбирается из выпадающего списка после ввода токена,
либо вводится вручную (
100= «Сохранённые»,wall= «Со стены» и т.д.).
- В блоке LMStudio укажите:
- Хост LMStudio — обычно
http://localhost:1234. - Имя модели — точное имя из LMStudio (например,
qwen2-vl-7b-instruct). - Температуру — творческий разброс при генерации (0 → детерминированно, 1 → сильно «фантазирует»).
- Хост LMStudio — обычно
- Выберите режим обработки: Авто или Ручной.
Все настройки сохраняются через tauri-plugin-store (на диск в JSON) и
доступны при следующем запуске.
Открывается из вкладки Главная.
- Кнопка «Запустить» загружает фотографии указанного альбома, у которых
ещё нет подписи, и обрабатывает их по очереди:
- в авторежиме — без вмешательства пользователя; кнопка «Отменить» возвращает последнюю сохранённую подпись;
- в ручном режиме — открывается модальное окно
ManualReviewModal, где можно принять, отредактировать или пропустить предложение модели.
- Сводная статистика «Всего / Обработано / Пропущено / Ошибки» обновляется по ходу работы, ниже виден прогресс-бар и текущее фото.
- Список всех фото рендерится через
n-virtual-list— окно остаётся отзывчивым даже на альбомах в тысячи снимков.
Открывается из вкладки Распределение. Левая колонка — форма задачи, правая колонка — очередь.
Полный набор полей формы (папка-источник, папка-назначение, правила, список целевых папок, оверрайды модели и температуры, поведение для пропущенных файлов) можно сохранить как шаблон под произвольным именем:
- «Создать» — добавляет новый шаблон с дефолтными значениями.
- «Создать как копию» — клонирует текущий активный шаблон под другим именем (удобно для разводки «Фото / Документы / Скриншоты»).
- «Удалить шаблон» — убирает пресет; задачи в очереди, созданные по этому шаблону, остаются нетронутыми (config задачи — это её собственный снимок настроек, ссылка на шаблон хранится только как метка).
Активный шаблон используется как пресет для следующей создаваемой задачи. Если форма привязана к уже существующей задаче, выбор шаблона его не меняет.
- При нажатии «Запустить» на пустой очереди создаётся новая задача из текущей формы и стартует немедленно.
- Если очередь не пуста, можно «Добавить в очередь» — задача встанет в ряд со статусом idle и стартует автоматически, как только освободится обработчик.
- Состояния задачи: idle → processing → done / cancelled / error.
- Активная задача показывает «текущий файл», прогресс и сводную статистику
(
moved / skipped / failed). По завершении автоматически активируется следующая idle-задача. - Кнопка «Остановить» прерывает текущую задачу через
AbortController: Rust-командыorganizer_classify_imageиorganizer_move_fileподдерживают отмену, текущий файл откатывается вpending, задача переходит в cancelled, очередь продолжает работать со следующей. - Удаление задачи из очереди — крестиком; активную processing-задачу предварительно нужно остановить.
- Список целевых папок заполняется тегами через Enter, при необходимости
можно сгенерировать его моделью:
- «Сгенерировать из существующих» — модель смотрит на содержимое каталога-источника (имена подпапок и нескольких файлов в каждой) и предлагает рабочий список;
- «Сгенерировать с нуля» — модель опирается только на правила пользователя.
- Свободные правила — текстовое поле, куда пишутся подсказки моделью на естественном языке: «Скриншоты UI и кода → «Скриншоты»; селфи и портреты → «Люди»; всё, что не подходит, отметь как SKIP» и т.п.
- Поведение для нераспознанных файлов:
- либо переместить в папку с настраиваемым именем (по умолчанию
_unsorted); - либо оставить на месте.
- либо переместить в папку с настраиваемым именем (по умолчанию
- Оверрайды модели и температуры на конкретную задачу — пусто = брать значения из общих настроек.
- Список файлов виртуализирован (
n-virtual-list) и при необходимости отключается полностью (если очередь огромная — UI становится легче). - Миниатюры генерируются Rust-командой
organizer_get_thumbnail: декодирование, ресайз и перекодирование в JPEG ≈10 КБ выполняются вне главного потока; в WebView2 уезжает уже готовыйdata:-URL. Параллелизм ограничен (MAX_CONCURRENT = 2вuseThumbnail), кеш общий на всё приложение. - Превью можно отключить чекбоксом «Показывать миниатюры», если интегрированная графика не справляется одновременно с vision-моделью и предпросмотром.
Состояние распределения (шаблоны, очередь задач, выбор активной задачи,
переключатели UI) хранится в localStorage под ключом
vk-caption-ai-organizer. Запись организована вручную, без
pinia-plugin-persistedstate, и оптимизирована под сценарий «горячая
обработка сотен файлов»:
- запись делается по дебаунсу в 600 мс — пачка реактивных мутаций сериализуется один раз;
- watcher слушает не сами задачи, а проекцию-геттер, в которую массив
imagesнамеренно не входит. Поэтому смена статуса очередного файла в горячем цикле не дёргает сейв вообще — мутации видит только Vue, доJSON.stringifyони не доходят; - при записи
imagesу каждой задачи сбрасывается в пустой массив: список файлов всё равно перестраивается Rust'ом при следующем запуске задачи (organizer_list_images), а сводная статистика иcurrentIndexпереживают перезагрузку и видны в очереди; - хвост сохраняется на
beforeunload, чтобы последняя правка формы не пропала при закрытии окна; - при восстановлении задача со статусом
processingприводится кcancelled(с пометкой «Обработка прервана перезапуском приложения»), «зависшие» файлы со статусомprocessingвозвращаются вpending.
Дополнительно, во время активной обработки, на корневом контейнере
страницы поднимается класс organizer-view--running, который через
:deep(*) обнуляет CSS-transition и animation Naive UI, а тяжёлые
секции (форма, правая колонка, виртуальный список, текущее фото) изолированы
через contain: layout paint style. Это снижает нагрузку на компоновщик
WebView2 в условиях, когда GPU занят инференсом vision-модели.
src/
main.ts — точка входа Vue + Pinia + Naive UI
router/index.ts — три маршрута: /, /organizer, /settings
views/
HomeView.vue — режим подписей к фото ВК
OrganizerView.vue — распределение: форма, очередь, список файлов
OrganizerImageItem.vue — элемент виртуального списка с превью и статусом
SettingsView.vue — общие настройки: VK + LMStudio
stores/
app.ts — режим подписей (фото, прогресс, отмена)
organizer.ts — распределение: шаблоны, очередь, ручная персистентность
theme.ts — выбор темы Naive UI
composables/
useThumbnail.ts — ленивый загрузчик миниатюр через Rust-команду
components/
ManualReviewModal.vue — модалка ручного режима (по одному фото)
src-tauri/
src/
main.rs — bootstrap Tauri, регистрация плагинов и команд
commands.rs — команды режима подписей (VK API, LMStudio, store)
organizer.rs — команды распределения: list / classify / move /
generate folders / thumbnail
capabilities/default.json — разрешения плагинов (opener, store, dialog)
tauri.conf.json — конфигурация окна и сборки
| Команда | Что делает |
|---|---|
npm run dev |
запускает только Vite-сервер (без Tauri-окна) — удобно для отладки UI в браузере |
npm run build |
проверяет типы (vue-tsc --noEmit) и собирает фронт через Vite |
npm run typecheck |
только проверка типов, без сборки |
npm run tauri:dev |
боевой dev-режим: Vite + Tauri-окно с DevTools |
npm run tauri:build |
продакшен-сборка → NSIS-installer в src-tauri/target/release/bundle/nsis/ |
npm run tauri:buildНа выходе получается NSIS-инсталлятор VK Caption AI_<version>_x64-setup.exe
в каталоге src-tauri/target/release/bundle/nsis/. Конфигурация бандла,
иконки и идентификатор приложения задаются в src-tauri/tauri.conf.json и
src-tauri/Cargo.toml.