Kawai-Focus 2.2: Python-бинарник в Tauri — проблемы и альтернативы
- суббота, 31 января 2026 г. в 00:00:08
Всем доброго дня! В предыдущей статье Kawai-Focus 2.1: переезд на новый стек я:
Объяснил, почему перехожу с Kivy на новый стек;
Перевёл часть кода на новый стек: FastAPI + Vue.js + Tauri + Ionic;
Собрал приложение для Linux в формате AppImage;
Рассказал о некоторых критических недостатках текущего решения при запуске на Arch Linux.
В данной статье я расскажу, с какими проблемами, связанными с бинарником, столкнулось моё решение при запуске на Arch Linux у моих знакомых, и почему у меня оно работает нормально на Debian 12 и 13. Также я разберу преимущества и недостатки текущей реализации, покажу возможные пути решения и альтернативы, а лучшее решение применю на практике.
Я снова попрошу Сергея и Ивана запустить моё приложение на Arch Linux, чтобы проверить его работу.
Заваривайте чай, доставайте вкусняшки — пора «удобрять почву для выращивания помидоров»! 🍅
В конце предыдущей статьи я написал, что приложение успешно запустилось у меня на Debian 12 и 13. При этом у двух человек, которые запускали у себя моё приложение на Arch возникли разные проблемы. У одного был белый экран, но создалась база данных, а у второго отрисовался интерфейс, но не создалась база данных и вообще не запустился бекенд в принципе.
У первого человека явно что-то отсутствовало для успешной отрисовки интерфейса приложения Tauri. Дело в том, что на Linux Tauri использует webkit2gtk — порт WebKit для GTK, который предоставляет встраиваемый WebView-компонент для рендеринга веб-страниц внутри GTK-приложений. В AppImage Tauri добавляет необходимые зависимости для его запуска.
На практике это означает, что если WebView не запускается на конкретной системе (например, Arch Linux), то проблема чаще всего лежит не в зависимостях Tauri, а в том, что вместо DE используется WM.
DE (Desktop Environment) — это полный набор: панель, меню, настройки, сессии, менеджер окон, системные сервисы. Примеры: GNOME, KDE, Cinnamon, MATE.
WM (Window Manager) — это только управление окнами, без всего остального.
Почему это важно для Tauri?
Tauri на Linux не просто запускает WebView, ему нужно:
Менеджер окон / compositor — чтобы окно появилось;
Сессия с X11 или Wayland;
GTK / WebKit — чтобы рендерить WebView.
На DE GNOME/KDE всё это есть «из коробки»:
compositor включён (Mutter / KWin);
Wayland/X11 правильно инициализирован;
env vars, dbus, xdg, и т. д. настроены.
На WM:
compositor может быть отсутствовать (или не тот);
env vars (например XDG_RUNTIME_DIR, DISPLAY или WAYLAND_DISPLAY) не выставлены автоматически;
нет «фоновых сервисов», которые нужны GTK/WebKit;
AppImage / Tauri не умеет поднимать compositor сам
Именно поэтому WebKit или GTK-приложение может не показывать окно и просто закрываться без ошибок.
На самом деле здесь не стоит сильно переживать и пытаться сделать приложение работающим на всех возможных конфигурациях. Нужно помнить, что это Linux, и для него не бывает идеальных решений.
У второго человека не запустился бекенд вообще. Это привело к тому, что база данных не создалась (у меня она просто копируется в нужный каталог). Фронтенд, соответственно, не смог обратиться к данным бекенда и не вывел на экран список таймеров с их данными.
Прежде чем рассказать, почему бекенд не запустился, я объясню, как запускается именно мой бинарник и почему я создал его именно так.
Изначально я сделал один большой бинарный файл и поместил его в каталог desktop/src-tauri/bin/. Это самый верный вариант без лишних костылей. Однако при сборке под Linux через Tauri бинарник повредился.
Nuitka в режиме --onefile создаёт бинарник, к которому в «хвост» приклеен архив с Python-файлами. Команда strip (которую Tauri автоматически запускает при создании .deb и AppImage) видит «лишние» данные в конце файла и безжалостно их отрезает. В итоге бинарник становится «пустым» и не работает. Самое интересное: до сборки в Tauri бинарник отлично работал.
После таких ситуаций разработчики часто пытаются запустить то, что не работает, с помощью «костыля». Меня это тоже коснулось. Я написал bash-скрипт, который должен запускать бинарник, и положил его в desktop/src-tauri/bin/, назвав backend-x86_64-unknown-linux-gnu. Папку с бинарником я поместил в desktop/src-tauri/resources/backend/.
Идея была в том, что Tauri не тронет настоящий бинарник в resources, а сам bash-скрипт не будет испорчен. В итоге этот костыль действительно сработал, и бекенд запустился на трёх системах из четырёх.
Мне как автору статей порой нравится разбирать подобные ситуации, однако в настоящей продуктовой разработке подобный "костыль" может стоить разработчику большого количества времени, потраченного впустую. Для моих статей такие ситуации — материал, а для продуктового разработчика это настоящая боль.
Сама ошибка выглядела так: undefined symbol: rl_print_keybinding при запуске бекенда. Она была связана с динамическими зависимостями, которые bash подхватывает внутри AppImage.
У пользователя, запускавшего моё приложение, в качестве shell использовался не bash, а zsh, из-за чего при старте AppImage подтягивалась другая версия библиотеки readline, несовместимая с ожидаемыми символами. Это и приводило к описанной ошибке.
О том, какие есть пути решения этой проблемы и к какому из них я пришёл в итоге, вы узнаете в следующем разделе статьи.
После того как я обнажил проблему с бинарником, его компиляцией и запуском на Arch Linux, пора искать пути решения и рассмотреть варианты, которые у меня есть на данный момент.
Хочу отметить, что я говорю только о бинарнике и функциональности, которую он несёт. Все остальные технологии — Tauri, Vue.js и Ionic — меня устраивают.
Самым простым и быстрым решением в данной ситуации могло бы быть создание универсального скрипта, который корректно запускается как в bash, так и в zsh и стабильно работает на любом Linux-дистрибутиве.
Пример универсального скрипта запуска:
#!/usr/bin/env sh
# Прерывать выполнение при ошибках
set -e
# Определяем директорию, где лежит скрипт
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
# Явно указываем PATH (на случай AppImage / контейнеров)
export PATH="/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
# При необходимости можно зафиксировать использование bash
# export SHELL=/bin/bash
# Запуск бэкенда
exec "$SCRIPT_DIR/backend"Почему это работает?
#!/usr/bin/env sh → используется системный sh, а не конкретный shell;
нет bash-специфичных конструкций (, массивов, source);
одинаково корректно выполняется из bash, zsh, dash;
exec заменяет процесс скрипта процессом приложения (чистый запуск).
В данном случае приведён сугубо абстрактный пример. Однако даже если такой скрипт (после адаптации под конкретный случай) позволит запустить бэкенд на Arch Linux с zsh, он всё равно останется «костылём», лишним звеном в процессе запуска приложения.
Почему это костыль?
Скрипт маскирует первопричину проблемы: Проблема находится на уровне сборки и динамической линковки (readline, AppImage, окружение), а не на этапе запуска. Скрипт лишь подстраивает окружение, не устраняя реальную причину несовместимости.
Неявные зависимости остаются: Приложение продолжает зависеть от конкретных версий библиотек и поведения shell’а, просто эта зависимость становится менее заметной и труднее диагностируемой.
Лишний слой абстракции Вместо прямого и предсказуемого запуска бинарника появляется промежуточный слой, который:
усложняет цепочку запуска;
усложняет отладку;
повышает вероятность ошибок при изменении окружения.
Плохая масштабируемость решения: Сегодня это zsh vs bash, завтра — другая версия glibc, иной ld.so или нестандартный PATH. Количество условий в скрипте начинает расти, и он постепенно превращается в набор хаков.
Почему костыли — это плохо в долгосрочной перспективе?
Хрупкость: костыли работают ровно до первого изменения окружения;
Рост технического долга: временное решение становится «постоянным» и начинает влиять на архитектурные решения;
Снижение прозрачности: новым разработчикам (или даже тебе самому через полгода) становится сложно понять, почему запуск устроен именно так;
Ложное ощущение стабильности: кажется, что проблема решена, хотя на самом деле она просто спрятана.
Да, вы не ослышались — язык Python в данном приложении выглядит очень избыточным. Вес исходной папки со скомпилированным бэкендом (бинарником) и его зависимостями составляет 125,3 МБ.

Вес распакованного AppImage — 431,3 МБ, а итоговый вес приложения в AppImage — 134,1 МБ.
AppImage использует SquashFS, а он очень агрессивно сжимает:
.so библиотеки;
текст (JS, HTML, CSS);
Python .py;
дублирующиеся ELF-секции
Избавление от Python должно уменьшить размер приложения примерно на 30–60 МБ, что является существенным плюсом. Даже сейчас приложение на Tauri весит значительно меньше, чем аналог на Electron, который может достигать 150–300 МБ.
Вторая причина избыточности Python в данном приложении — его простота. В базе данных SQLite3 пока только одна таблица timer, для работы с которой используется FastAPI — известный своей скоростью и асинхронностью фреймворк. При локальной работе с SQLite3, которая является синхронной и лёгкой базой, FastAPI не раскрывает весь свой потенциал, но при этом сам по себе занимает место, как и его зависимости. Бэкенд запускается через сервер Gunicorn, который в данном случае выполняет роль локальной «запускалки» для FastAPI, а не полноценного сервера, обслуживающего множество запросов одновременно.
На Python я могу писать отдельные микросервисы на FastAPI и подключать их к своему приложению в будущем, а также могу создать бэкенд для многопользовательской веб-версии.
Данный вариант сразу решает проблемы со сборкой приложения на Tauri. Так как основа Tauri-приложения уже написана на Rust, встроить логику работы с базой SQLite3 не должно составить труда. Язык Rust гораздо быстрее Python и весит меньше в скомпилированном виде.
Для работы с SQLite3 на Rust есть лёгкие библиотеки, например rusqlite, которые подходят для небольших проектов и используют SQL-запросы напрямую. Также существуют ORM-библиотеки (например, SeaORM или rbatis), которые весят больше и могут быть избыточными для маленьких проектов. Однако ORM удобнее для работы с базой, а также обеспечивает поддержку миграций «из коробки».
Есть два подхода к использованию Rust в моём случае.
Первый вариант — это Tauri + Rust через команды. В этом случае Rust выступает как встроенный бэкенд, а фронтенд на Ionic и Vue.js общается с ним напрямую через invoke() из JavaScript. Вся логика — таймер Pomodoro, работа с SQLite, файловая система и системные API — реализуется в Rust-функциях, помеченных #[tauri::command]. Важно писать код именно через команды, потому что это единственный официальный и безопасный мост между JS-фронтендом и Rust.
Такой подход имеет несколько плюсов: бинарник остаётся компактным, приложение работает быстро, отсутствует сетевой слой, поставка проста (один процесс), а Tauri обеспечивает нативную безопасность. Минусы тоже есть: фронтенд и бэкенд оказываются более тесно связаны, сложнее сделать архитектурно «чистое» API, а при росте логики код команд может превратиться в монолит, если не следить за структурой.
Второй вариант — это локальный веб-сервер на Rust с использованием axum или actix и обычного HTTP API. В этом случае Ionic + Vue.js общаются с localhost как с обычным бэкендом. Такой подход ближе к привычной модели на Python: REST, DTO, middleware, миграции базы данных и тестирование API — всё работает привычным образом.
Преимущества очевидны: слабая связность между фронтендом и бэкендом, лёгкий перенос логики в облако или отдельный сервис, понятная масштабируемость. Однако есть и минусы: размер приложения увеличивается, появляется дополнительный процесс, добавляется сетевой слой и больше точек отказа, что для простого Pomodoro-таймера с SQLite выглядит избыточным.
В Tauri можно работать с SQLite из JS через плагины (tauri-plugin-sql, better-sqlite3 через sidecar или WASM-варианты с ограничениями). База живёт локально, а доступ к ней идёт из фронтенд-слоя (Vue/Ionic) через JS-API.
Это упрощает разработку: один язык, меньше контекста переключения, быстрее итерации. Но важно понимать, что в Tauri такой JS-код выполняется в WebView, а доступ к файлам и БД идёт через разрешённые API — с точки зрения безопасности это нормально, но по производительности чуть хуже, чем прямой Rust.
С подключением plugin-sql Rust нужен минимально (для миграций), а вся логика остаётся в JS, что ближе к моему стеку, привычному как веб-разработчику.
Чтобы масштабировать и сохранить переносимость, логику стоит выносить не в Vue-компоненты, а в отдельный слой: условно core/ или domain/ на чистом JS без привязки к Tauri. Работа с БД осуществляется через абстракцию (интерфейс репозитория), где конкретная реализация для Tauri использует SQLite3, а для веб-версии — IndexedDB / Dexie / SQLite WASM или REST.
Так Ionic Web, Ionic Mobile и Tauri Desktop будут использовать один и тот же бизнес-код (таймеры, сессии, статистика), различаясь только адаптерами хранения данных и системных функций.
Это как раз тот случай, где JS/TS — стратегическое преимущество, если в будущем я захочу развивать мобильные платформы.
Итак, я выбрал для себя оптимальный вариант — отказаться от Python-бинарника и работать с SQLite3 напрямую из JavaScript. Такой подход подразумевает использование CRUD-функций вместо API. Чтобы сохранить универсальность фронтенда и обеспечить его безболезненную интеграцию в веб-версию, я разделю логику таким образом, чтобы при необходимости можно было легко подменить CRUD-реализацию на работу через API, не изменяя остальной код приложения.
Первым делом мне нужно удалить из проекта всё, что связано с Python. Сам код от FastAPI мне ещё пригодится, когда я перейду к написанию веб-версии, поэтому я скопирую его в черновую папку, а не просто удалю. Там было написано несколько API-ручек и функции для работы с CRUD-операциями, поэтому было бы жалко их просто так удалять — для веб-версии они пригодятся в будущем.
На текущий момент моя старая структура проекта выглядит так.

Обзор по текущей структуре:
client — папка с фронтендом, содержимое которой я оставлю в текущем проекте;
desktop — папка с tauri из которой я удалю:
src-tauri/bin/backend-x86_64-unknown-linux-gnu — bash скрипт, который является костылём и которому уже будет нечего запускать;
src-tauri/resurces/backend/* — так как я отказался от бинарника на Python папка backend уже не нужна с её содержимым;
src-tauri/resurces/.env — в данной переменной хранились данные от бекендной части на Python и имя бд. Оставлять её ради названия бд не имеет смысла так это название нет смысла менять и скрывать, а проще захардкодить. Тоже самое качается и .env в корне проекта;
src-tauri/resurces/timer.sqlite3 — файл базы данных, который лучше создавать из кода, а не хранить в ресурсах в готовом виде. Хранить готовый файл базы данных плохая идея в репозитории;
src-tauri/target/* — это каталог сборки Rust, он создаётся cargo автоматически и содержит:
скомпилированные бинарники (debug / release);
промежуточные .rlib, .d, .o;
артефакты Tauri (tauri.bundle, AppImage, deb, dmg и т. п.);
следы старых зависимостей (включая Python-бинари и sidecar’ы, если они раньше были).
main.build и main.dist — это типичные артефакты Nuitka, которые появляются при компиляции Python-скрипта в бинарник и без Python они не нужны:
*.build — временные файлы сборки (C/C++ код, объекты);
*.dist — итоговый дистрибутив (бинарник + зависимости).
src/kawai-focus-v2 — тут хранится бекенд, который пригодится разве что для веб-версии. Скорее всего бекенд я буду хранить в отдельном репозитории (если дойдёт дело до веб-версии), а пока он мне тут не нужен дабы не утежелять весь проект и хранить его без цели;
.python-version — раз нет проекта на Python, то и версия мне больше тут не нужна;
alembic.ini — он от Python проекта поэтому тоже отправится на удаление;
pyproject.toml и uv.lock — файлы от uv, который управляет зависимостями Python проекта тоже не нужны;
Файл .pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: check-case-conflict
- id: check-merge-conflict
- id: end-of-file-fixerЯ оставляю только pre-commit-hooks, так как он полезен для JS, Rust, JSON/YAML, конфигов Tauri, CI и Docker. Остальные, связанные с Python, удаляю.
В прошлой статье я не смог уместить настройки для Tauri поэтому я о них расскажу сейчас, а заодно поправлю их чтоб-бы убрать всё лишнее (оставшееся от работы с python бинарником).
main.rs — это точка входа Rust-бэкенда для Tauri-приложения. Фактически, всё что связано с нативной логикой и запуском приложения происходит через этот файл.
fn main() {
println!("Запуск приложения Tauri...");
app_lib::run();
}Разбор кода:
println! — выводит сообщение в консоль (полезно при отладке);
app_lib::run(); — вызывает функцию запуска приложения.
lib.rs — это файл библиотеки Rust в проекте, где обычно размещают основную логику приложения или модули, которые потом можно вызывать из main.rs или других частей проекта.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("Ошибка при запуске приложения Tauri.");
}Разбор кода:
#[cfg_attr(mobile, tauri::mobile_entry_point)]:
Атрибут для мобильной сборки;
Если таргет — mobile, Tauri использует эту функцию как точку входа приложения.
pub fn run()
Публичная функция, которую вызываешь из main.rs;
Запускает Tauri-приложение.
tauri::Builder::default():
Создаёт «строитель» приложения, где можно подключать окна, плагины, команды.
.run(tauri::generate_context!()):
Стартует event-loop Tauri, подключает фронтенд (HTML/JS);
generate_context!() подхватывает настройки из tauri.conf.json.
.expect("Ошибка при запуске приложения Tauri."):
Если запуск приложения упадёт — программа завершится с сообщением об ошибке.
tauri.conf.json — это главный конфигурационный файл Tauri-проекта, в котором задаются параметры сборки, настройки окон, пути к фронтенду, ресурсы и сборка бандла приложения.
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Kawai-Focus",
"version": "0.1.0",
"identifier": "com.kawai.focus",
"build": {
"frontendDist": "../../client/dist",
"devUrl": "http://localhost:8100",
"beforeDevCommand": "cd ../client && ionic serve",
"beforeBuildCommand": "cd ../client && npm run build"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"label": "main",
"title": "Kawai-Focus",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false,
"devtools": true
}
],
"security": {
"csp": null,
"capabilities": ["default"]
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}Общие настройки:
$schema — ссылка на схему конфигурации Tauri, чтобы IDE проверяла синтаксис;
productName — имя приложения, отображается в заголовке окна и при сборке;
version — версия приложения;
identifier — уникальный идентификатор (bundle ID) для системных сборок.
Блок build:
frontendDist — путь к собранным фронтенд-файлам (HTML/CSS/JS), которые Tauri подгружает в окно;
devUrl — URL для запуска фронтенда в режиме разработки (например, Ionic dev server);
beforeDevCommand — команда, которая выполняется перед запуском Tauri в режиме разработки;
beforeBuildCommand — команда, которая выполняется перед сборкой нативного приложения.
Блок app:
withGlobalTauri — позволяет фронтенду обращаться к глобальному объекту Tauri (window.__TAURI__);
windows — массив окон приложения:
label — уникальный идентификатор окна;
title — заголовок окна;
width / height — размеры окна при запуске;
resizable — можно ли изменять размеры окна;
fullscreen — запускать ли в полноэкранном режиме;
devtools — включить devtools (консоль браузера) при запуске.
security:
csp — политика Content Security Policy; null отключает её;
capabilities — список разрешённых возможностей приложения (например, доступ к файлам, сети и т.д.).
Блок bundle:
active — включить сборку нативного бандла;
targets — платформы для сборки (all = Windows, macOS, Linux);
icon — список иконок разных размеров для сборки приложений на разных системах.
Cargo.toml — это файл манифеста Rust-проекта, который описывает проект, его зависимости, версии Rust и как его собирать. Через него Cargo знает, что собирать, какие библиотеки подключать и какие типы сборки использовать.
[package]
name = "app"
version = "0.1.0"
description = "Kawai-Focus - приложение для фокусировки внимания на основе таймера Pomodoro."
authors = ["Arduinum"]
license = "MIT"
repository = ""
edition = "2021"
rust-version = "1.77.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.5.3", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
log = "0.4"
tauri = { version = "2.9.5", features = [] }
tauri-plugin-log = "2"[package] — информация о проекте: имя, версия, авторы, лицензия и версия Rust:
name / version / description / authors / license / repository — базовая информация о пакете;
edition = "2021" — версия синтаксиса Rust (новые фичи языка);
rust-version = "1.77.2" — минимальная версия Rust для сборки проекта.
[lib] — описание библиотеки Rust внутри проекта: имя и тип сборки:
name = "app_lib" — имя библиотеки, чтобы её можно было вызывать из main.rs;
crate-type = ["staticlib", "cdylib", "rlib"] — типы сборки библиотеки:
staticlib — статическая библиотека;
cdylib — динамическая библиотека для FFI (например, JS/Tauri);
rlib — обычная Rust-библиотека.
[build-dependencies] — зависимости, нужные только при сборке проекта:
tauri-build — используется при сборке Tauri (генерация кода, подключение ресурсов).
[dependencies] — обычные зависимости, используемые в коде проекта:
serde / serde_json — для работы с JSON и сериализации/десериализации.
log — логирование внутри Rust;
tauri — ядро Tauri для запуска окна, событий и команд;
tauri-plugin-log — плагин для логирования в файл и stdout.
Cargo.lock — lockfile Cargo, который фиксирует точные версии (и транзитивные зависимости) для воспроизводимых сборок, прежде всего для бинарей; для библиотек его обычно коммитят опционально.
Принцип работы с cargo похож на uv с poetry. Он легко позволяет управлять нужными зависимостями. Работать с ними нужно из того каталога где находятся файлы Cargo.lock и Cargo.toml. В моём проекте они создались в каталоге desktop/src-tauri.
Примеры команд:
cargo remove name_dependence — удаляет зависимость для проекта или плагин;
cargo add name_dependence — устанавливает нужную зависимость или плагин в проект.
default.json — это файл «capabilities» в Tauri, который задаёт, для каких окон приложения включены те или иные разрешения (permissions) на использование API/плагинов (например, shell, fs), то есть определяет, что фронтенду вообще разрешено делать.
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default"
]
}Разбор конфига:
"$schema": "../gen/schemas/desktop-schema.json" — ссылка на JSON Schema, чтобы редактор/линтер валидировал структуру файла и подсказывал доступные поля;
"identifier": "default" — идентификатор capability-набора (этого профиля разрешений), по которому Tauri понимает, какой набор правил применяется;
"windows": ["main"] — список окон, к которым применяется этот capability-набор; здесь только окно main;
"permissions": ["core:default"] — включён базовый набор разрешений core (минимально необходимое для работы стандартного Tauri API), без дополнительных прав на плагины вроде shell.
Теперь, когда я избавился от Python-бинарника и убрал из настроек Tauri всё, что с ним связано, облегчая тем самым вес проекта, мне нужно начать работать с SQLite3 через JS.
Я выбрал гибридный вариант для работы с SQLite3 — tauri-plugin-sql. Плагин подключается через Rust, но сам код можно писать на JS. Это даёт максимальную производительность и позволяет не слишком углубляться в синтаксис Rust.
При выборе решения нужно учитывать, на каких платформах оно будет работать. Сейчас я окончательно решил, что буду делать своё десктоп-приложение под Linux и Windows. Я бы также хотел мобильную платформу, хотя бы Android — Tauri поддерживает Android. Есть и поддержка iOS, но для этого потребуется macOS + Xcode, а у меня пока нет современного Mac, и на виртуальной машине этим заниматься не очень удобно.
Плагин tauri-plugin-sql должен работать на Linux, Windows и Android, что делает его универсальным. Варианты чисто на JS более тяжеловесные и менее универсальные, поэтому я выбрал гибридный вариант как оптимальный для себя.
Установка плагина:
cargo add tauri-plugin-sqlФрагмент вывода:
Adding tauri-plugin-sql v2.3.1 to dependencies
Features:
- mysql
- postgres
- sqliteСудя по выводу, плагин поддерживает mysql, postgres и sqlite, что довольно удобно для разнообразия вариантов.
Чтобы плагин работал из JS, его необходимо подключить в Rust и выдать ему нужные права. В lib.rs строку подключения нужно добавить в функцию run() над .run(...).
.plugin(tauri_plugin_sql::Builder::default().build())Далее права для плагина задаются в файле default.json, иначе операции с базой будут заблокированы.
Фрагмент конфига:
"permissions": [
"core:default",
"sql:default",
"sql:allow-execute"
]Разбор новых строк конфинига:
"sql:default" — разрешает load, close, select;
"sql:allow-execute" — разрешает CREATE TABLE, INSERT, UPDATE, DELETE через execute().
Проверка зависимостей в Tauri:
cargo checkЧасть вывода команды:
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 08sРазбор вывода:
Finished dev profile — сборка прошла успешно для development-профиля (профиль dev используется по умолчанию для разработки, с отладочной информацией и без оптимизаций для скорости);
[unoptimized + debuginfo] — бинарники не оптимизированы для скорости (unoptimized), но содержат информацию для отладки (debuginfo), чтобы можно было отлаживать код, смотреть стек вызовов, переменные и т.д;
target(s) — все цели сборки (бинарники, библиотеки, плагины), которые были собраны;
in 2m 08s — время, которое заняла проверка/сборка проекта, в данном случае 2 минуты 8 секунд.
То есть фактически это не полная компиляция, а проверка и построение проекта в режиме разработки, чтобы убедиться, что все зависимости и код корректны
О коде на JS и структуре проекта я расскажу в следующей статье, так как материала накопилось очень много, и статья слишком затянется.
Я попросил Ивана и Сергея снова запустить моё приложение. Сейчас оно стало весить 94,7 МБ (раньше — 134 МБ). У Ивана на его Cachy OS (на базе Arch) на этот раз отлично сработал код создания базы данных, и таймеры отобразились в окне приложения.

Над первой проблемой произошла победа, чему я очень обрадовался. Скриншот работающей программы он прислал мне в Telegram. База данных теперь создаётся напрямую, а не копируется, и всё это происходит из кода JS — сработало отлично.
Однако у второго пользователя, Сергея, тоже произошла победа! Он использует WM вместо DE. Недавно он создал issue с описанием фикса этого бага.
Issue в GitHub — это система для отслеживания задач, багов, идей и обсуждений в репозитории. О данном фиксе и issue расскажу подробно в следующей статье, так как в этой уже не успеваю обновить код.

Он даже склонировал мой репозиторий, попробовал исправить у себя и скинул в Telegram скрин успешно запущенного приложения.

Большое спасибо Сергею и Ивану за тестирование запуска у себя и помощь с поиском решения!
Я проделал довольно большую работу: убрал из проекта Python-код и бинарник, перенастроил проект и переписал логику на JS. Последнее вы увидите в следующей статье, так как весь материал не поместился в текущую.
Кроме того, я подумываю о переходе на TypeScript, который имеет ряд преимуществ над JavaScript, таких как строгая типизация через аннотации, автодополнение, более безопасный рефакторинг и предсказуемость кода. Было бы интересно показать один и тот же код на обоих языках и наглядно увидеть, как TypeScript помогает избежать ошибок и упрощает поддержку проекта.
Мне как человеку, который привык к Python с его аннотациями и подсказками типов, обычный JS кажется слегка примитивным и устаревшим в плане инструментов для контроля типов.
Если у вас есть мысли о том, как можно улучшить проект, пишите в комментариях — с удовольствием ознакомлюсь с вашими предложениями!
Читайте продолжение — не пропустите!
Узнали, что было не так с бинарником на Arch;
Рассмотрели альтернативы, которые могут решить проблемы с бинарником;
Внедрили оптимальный вариант для моей ситуации;
В итоге одна из двух проблем на Arch была решена.
Собранный Kawai-Focus_0.1.0_amd64.AppImage;
Репозиторий проекта на Github Kawai-Focus-v2;