Разрабатываем десктопное приложение для заметок с помощью Tauri (React + Rust)

mapolvqq4uunxfqoaviv3g9km9y.jpeg


Привет, друзья!

В данном туториале мы разработаем десктопное приложение с помощью Tauri. Tauri — это фреймворк для создания десктопных приложений, похожий на Electron, но позволяющий использовать Rust вместо Node.js, например, для взаимодействия с файловой системой.

В качестве фреймворка для разработки пользовательского интерфейса я буду использовать React и TypeScript, а для работы с зависимостями для JavaScript — Yarn.

Источником вдохновения для меня послужила эта замечательная статья.

Обратите внимание: я буду разрабатывать приложение под Windows x64, в других операционных системах (и архитектурах) детали реализации будут немного отличаться.

Приложение будет представлять собой своего рода однострочный терминал для записи заметок в файл tasks.txt, находящийся в домашней директории (для Windows — это C:\Users\[User]). Приложение будет запускаться с помощью сочетания клавиш Ctrl + Shift + Q и завершаться при нажатии Esc.

Вот как это будет выглядеть:


pmvnh4ypi7atztwrdiljiumk6gk.png

Репозиторий с кодом проекта.

Если вам это интересно, прошу под кат.


Подготовка и настройка проекта

Обратите внимание: на вашей машине должны быть установлены Node.js и Rust. Про установку Rust можно почитать здесь и здесь.

Если в качестве редактора кода вы используете VSCode, рекомендую установить этот набор расширений для работы с Rust.

Windows


  • При установке Build Tools for Visual Studio 2022 выбираем полезную нагрузку Разработка классических приложений на C++.


backsqnds5nyzfxpsq2vk7zyk3e.png

  • После установки Rust выполняем команду rustup default stable-msvc в терминале для переключения на набор инструментов MSVC.

Создаем новый проект Tauri:

yarn create tauri-app


  • Вводим название приложения, например, Tauri Focus;
  • выбираем инструмент для создания шаблона фронтенда create-vite;
  • добавляем пакет @tauri-apps/api;
  • выбираем шаблон react-ts.


cetknnxwmpv2xdioof8z-4sat-o.png

Обратите внимание: в Windows командная строка должна быть запущена от имени администратора.

Переходим в созданную директорию tauri-focus и приводим ее к следующему виду:


_g_f2vtscpnjrp16hz4oonogiqu.png

Пока не обращайте внимание на файлы postcss.config.js и tailwind.config.js, скоро они у вас появятся.

Выполняем команду yarn tauri dev для запуска приложения в режиме для разработки. Обратите внимание: при первом запуске Rust потребуется скомпилировать файлы приложения, поэтому придется немного подождать (при последующих запусках будет использоваться кеш).

На этом подготовка и настройка проекта завершены. Переходим к разработке пользовательского интерфейса.


Пользовательский интерфейс

Для стилизации приложения будем использовать TailwindCSS. Устанавливаем необходимые пакеты, находясь в корневой директории проекта:

yarn add -D tailwindcss postcss autoprefixer

Инициализируем Tailwind:

yarn tailwindcss init -p

Добавляем в tailwind.config.js следующую строку:

module.exports = {
  // если хотите, можете оставить здесь только `tsx`
  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
  // ...
}

Импортируем стили в src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Редактируем файл src/App.tsx:

import React, { useState } from 'react'

function App() {
  const [text, setText] = useState('')

  const addTask = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter') {
      console.log(text)
    }
  }

  return (
     setText(e.target.value)}
      onKeyDown={addTask}
    />
  )
}

export default App

У нас имеется (темное) поле размером 600x60px для ввода (зеленого) текста заметки. При нажатии Enter значение данного поля выводится в консоль.

Далее нам нужна функция для записи заметки в файл.


Взаимодействие с файловой системой

Определяем команду Tauri/функцию для записи заметки в файле src-tauri/src/main.rs:

// импорт зависимостей
use std::fs::OpenOptions;
use std::io::Write;

#[tauri::command]
fn add_task(text: String) {
  let mut file = OpenOptions::new()
    .create(true)
    .append(true)
    .open("../tasks.txt")
    .expect("Ошибка при открытии файла");

  writeln!(file, "{}", text).expect("Ошибка при записи файла");
}

Добавляем эту функцию в список команд:

fn main() {
  let context = tauri::generate_context!();
  tauri::Builder::default()
    // !
    .invoke_handler(tauri::generate_handler![add_task])
    .menu(tauri::Menu::os_default(&context.package_info().name))
    .run(context)
    .expect("Ошибка при запуске приложения");
}

Редактируем обработчик addTask в src/App.tsx (вызов Rust из фронтенда):

import { invoke, process } from '@tauri-apps/api'

// ...
const addTask = async (e: React.KeyboardEvent) => {
  switch (e.key) {
    // при нажатии `Enter` вызываем `add_task` с текстом заметки
    case 'Enter':
      try {
        await invoke('add_task', { text })
        setText('')
      } catch (e) {
        console.error(e)
      }
      break
    // при нажатии `Esc` завершаем процесс
    case 'Escape':
      return process.exit()
    default:
      return
  }
}
// ...


Проверка работоспобности приложения и последние штрихи

Перед запуском приложения необходимо немного отредактировать файл tauri.conf.json:

"tauri": {
  "bundle": {
    "identifier": "app.tauri.focus",
  },
  "windows": [
    {
      "fullscreen": false,
      "resizable": false,
      "center": true,
      "width": 600,
      "height": 60,
      "title": "Tauri Focus App",
      "decorations": false
    }
  ]
}

decorations: false означает скрытие заголовка, остальное, думаю, понятно.


  • Выполняем команду yarn tauri dev (обратите внимание, что фронтенд можно отлаживать отдельно по адресу http://localhost:3000);
  • вводим несколько тестовых значений, например, test, test2, test3;
  • видим, что в корне проекта появился файл tasks.txt с нашими заметками.


8kjlyhzcmpotbvg5_3t6cg-ba1a.png

Если вас смущает ошибка на этой строке:

let context = tauri::generate_context!();

Просто создайте сборку фронтенда с помощью команды yarn build.

Отлично. Приложение работает, как ожидается. Однако корень проекта не слишком удачное место для хранения файла tasks.txt. Кроме того, мы еще не сгенерировали установочный файл.

Для универсального доступа к домашней директории потребуется пакет home («крейт» в терминологии Rust). Добавляем в файл src-tauri/src/Cargo.toml такую строку:

[dependencies]
# ...
home = "0.5.3"

Импортируем пакет в main.rs и определяем переменную для пути к домашней директории:

// ...
use home::home_dir;

fn add_task(text: String) {
  // !
  let mut path = home_dir()
    .expect("Ошибка доступа к домашней директории");
  // добавляем в путь название файла для заметок
  path.push("tasks.txt");

  let mut file = OpenOptions::new()
    .create(true)
    .append(true)
    // !
    .open(path)
    .expect("Ошибка при открытии файла");

  writeln!(file, "{text}").expect("Ошибка при записи файла");
}

Выполняем команду yarn tauri build:


2iwkiie3xl12zyax5y32x5l0gwo.png

Это приводит к генерации установочного файла src-tauri/target/release/bundle/msi/tauri-focus_0.1.0_x64_en-US.msi.

Также в директории src-tauri/target/release генерируется исполняемый файл tauri-focus.exe.


  • Создаем ярлык этого файла на рабочем столе;
  • открываем свойства ярлыка;
  • в поле «Быстрый вызов» вводим сочетание клавиш для запуска приложения, например, Ctrl + Shift + Q;
  • применяем изменения.

Нажимаем Ctrl + Shift + Q и вводим парочку заметок:


yp4qwjdzpz2qotr_xnxfb-rj3lk.png

Нажатие Esc завершает работу приложения.

Пожалуй, это все, чем я хотел поделиться с вами в данной статье. Надеюсь, вы нашли для себя что-то интересное и не зря потратили время.

Благодарю за внимание и happy coding!


coe2kha8u8_pypip-2k3wk3ppa0.png

© Habrahabr.ru