tauri 2.0素振り
Tauri 2.0が出そうなのとrustをちょっとかじったのでなんとかなるんじゃないかと思って再度試してみる。
環境はmacOS。
pnpmが好きなのでこれで作成。
~ % pnpm create tauri-app --rc
...
Your system is missing dependencies (or they do not exist in $PATH):
╭──────┬───────────────────────────────────────────────────────────────────╮
│ Rust │ Visit https://www.rust-lang.org/learn/get-started#installing-rust │
╰──────┴───────────────────────────────────────────────────────────────────╯
...
あれ、cargoがPATHにない。
以前試したときに入れたままのはずなんだが...いきなり出鼻をくじかれたなw
rust入れ直して再挑戦。
frontendをrustで書くってのはどういうことなんだと思って調べたら、ロジックをweb assemblyで書くってことなのね。
いや違うな。
LeptosってのはJSXみたいにHTMLライクにrustコード内に書けるのか。Rustのマクロすごいな。
ただUIまで全部Rustでやるのはまだ違う気がするな。
ここはおとなしくTS選んでおこう。
pnpm, react, typescriptの構成。l
~ % pnpm create tauri-app --rc
✔ Project name · tauri-v2-app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · React - (https://react.dev/)
✔ Choose your UI flavor · TypeScript
Template created! To get started run:
cd tauri-v2-app
pnpm install
pnpm tauri android init
pnpm tauri ios init
For Desktop development, run:
pnpm tauri dev
For Android development, run:
pnpm tauri android dev
For iOS development, run:
pnpm tauri ios dev
できたディレクトリに入っておもむろにgit initして全部突っ込む。
pnpm tauri dev
するとcargoでmoduleをもりもりダウンロードし始めた。正味一分くらいでデスクトップアプリが起動した。command + Rでreloadができないのに気づく。
さてコードの中身を見てみる。
その前にbuildしてみるか。package.jsonにあまりtaskが書かれていないが、たぶんこれか。おもむろに実行してみる。identifierをcom.tauri.dev
から変えておかないと怒られるので適当に変えておく。
npx tauri build
ターミナルがFinderの許可くれ、とか出てきたからNoにしたらビルド失敗した。
デフォルトの設定だと.dmgを作って開こうとするのか。そこでFinderへのアクセス許可がいるんだな。
Error failed to bundle project: error running bundle_dmg.sh: `failed to run /Users/tomotaka/tauri-v2-app/src-tauri/target/release/bundle/dmg/bundle_dmg.sh`
とりあえずしばらく.dmgは必要ないのでskipしたい。
tauri.config.jsonのbundle.targetsをappにしたらskipした。
https://tauri.app/v1/api/config/#bundleconfig.targets
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index cc579f4..ae5b43b 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -22,7 +22,7 @@
},
"bundle": {
"active": true,
- "targets": "all",
+ "targets": "app",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
...
Finished 1 bundle at:
.../tauri-v2-app/src-tauri/target/release/bundle/macos/tauri-v2-app.app
バイナリはここにできるみたい。
~/tauri-v2-app on main% file src-tauri/target/release/bundle/macos/tauri-v2-app.app/Contents/MacOS/tauri-v2-app
src-tauri/target/release/bundle/macos/tauri-v2-app.app/Contents/MacOS/tauri-v2-app: Mach-O 64-bit executable arm64
これでreleaseビルド作成までOK
さて改めて構成とコードを眺めてみる。これは今の構成で、将来的には変わる余地が多分にあるはず。
とはいえ、変わったとしても構成要素自体は基本的に同じである期待。
~/tauri-v2-app on main% tree
.
|-- README.md
|-- index.html
|-- package.json
|-- public
| |-- tauri.svg
| `-- vite.svg
|-- src
| |-- App.css
| |-- App.tsx
| |-- assets
| | `-- react.svg
| |-- main.tsx
| `-- vite-env.d.ts
|-- src-tauri
| |-- Cargo.toml
| |-- build.rs
| |-- capabilities
| | `-- default.json
| |-- icons
| | |-- 128x128.png
| | |-- ...
| | `-- icon.png
| |-- src
| | |-- lib.rs
| | `-- main.rs
| `-- tauri.conf.json
|-- tsconfig.json
|-- tsconfig.node.json
`-- vite.config.ts
10 directories, 41 files
src
がTypeScript用で、src-tauri
がrustのmain processなのかな。
この辺を中心に見てみる。
tsconfig.json
tsconfig.node.json
vite.config.ts
src-tauri/tauri.conf.json
src-tauri/Cargo.tomol
tsconfig.*
tsconfig.*
に特に目立ったところはなし。
vite.config.ts
pluginはreactのみ。clearScreen
は初めてみた。server
は開発用サーバーの設定の模様。
TAURI_DEV_HOST
が設定されていると有効になるのか。1420と1421をそれぞれ固定で使う模様。
tauri dev
で起動して得られるTAURI prefixを環境変数は以下。TAURI_DEV_HOST
はないので、カスタマイズ用なのか。
TAURI_ENV_ARCH: 'aarch64',
TAURI_ENV_DEBUG: 'true',
TAURI_ENV_FAMILY: 'unix',
TAURI_ENV_PLATFORM: 'darwin',
TAURI_ENV_PLATFORM_VERSION: '14.3.1',
TAURI_ENV_TARGET_TRIPLE: 'aarch64-apple-darwin',
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/
export default defineConfig(async () => ({
plugins: [react()],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
},
}));
tauri dev
で起動してlocalhost:1420にブラウザでアクセスするとUIが見れる。
pnpm dev
でvite
だけ起動しているが、UIだけの開発は:1420
でやれると。
tauri
のサブコマンドがないのでdevとbuildくらい足しておく。
~/tauri-v2-app on main% cat package.json
{
"name": "tauri-v2-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-rc.0",
"@tauri-apps/plugin-shell": "2.0.0-rc.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@tauri-apps/cli": "2.0.0-rc.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.5.4",
"vite": "^5.4.0"
}
}
diff --git a/package.json b/package.json
index 03c15d7..7347748 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,9 @@
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
- "tauri": "tauri"
+ "tauri": "tauri",
+ "tauri:dev": "tauri dev",
+ "tauri:build": "tauri buildw"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-rc.0",
src-tauri/tauri.conf.json
build.beforeDevCommand
、beforeBuildCommand
というのが目に入る。なんとなく用途は想像つく。dev
のほうは開発用のdev serverを起動するのか。つまりvite
が叩かれると。
それ以外は特に目立ったところなしか。Electronでいうelectron-builderの設定ファイルみたいなファイルっぽい。公式に組み込まれているのは助かる。publishまで
{
"productName": "tauri-v2-app",
"version": "0.0.0",
"identifier": "net.tmtk.dev",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "tauri-v2-app",
"width": 800,
"height": 600
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "app",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
tauriのサブコマンドを見るとsignerとかまであるから、publishまでをサポートしていると思っていいだろう。
~/tauri-v2-app on main% npx tauri -h
Command line interface for building Tauri apps
Usage: npm run npx [OPTIONS] <COMMAND>
Commands:
init Initialize a Tauri project in an existing directory
dev Run your app in development mode
build Build your app in release mode and generate bundles and installers
bundle Generate bundles and installers for your app (already built by `tauri build`)
android Android commands
ios iOS commands
migrate Migrate from v1 to v2
info Show a concise list of information about the environment, Rust, Node.js and their versions as well as a few relevant project configurations
add Add a tauri plugin to the project
plugin Manage or create Tauri plugins
icon Generate various icons for all major platforms
signer Generate signing keys for Tauri updater or sign files
completions Generate Tauri CLI shell completions for Bash, Zsh, PowerShell or Fish
permission Manage or create permissions for your app or plugin
capability Manage or create capabilities for your app
help Print this message or the help of the given subcommand(s)
Options:
...
src-tauri/Cargo.toml
設定ファイルの確認最後はCargo.toml。TOMLってまだ慣れてなくてぱっと見構造が理解できないときがあるんだけど、これは[[...]]
とかは使われてなくてまあ分かる。
Rustにはまだあまり慣れてないのでこの辺をきっかけに調べていく。
-
[lib]
があるからlibraryとしてビルドされる? -
crate-type
とか知らないので後で調べる。 -
serde*
はSerde用のメジャーなRustのmoduleなのかな。https://serde.rs -
tauri
はこれ?https://crates.io/crates/tauri -
tauri-plugin-shell
はこれ https://crates.io/crates/tauri-plugin-shell
[package]
name = "tauri-v2-app"
version = "0.0.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "tauri_v2_app_lib"
crate-type = ["lib", "cdylib", "staticlib"]
[build-dependencies]
tauri-build = { version = "2.0.0-rc", features = [] }
[dependencies]
tauri = { version = "2.0.0-rc", features = [] }
tauri-plugin-shell = "2.0.0-rc"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
entry pointはsrc-tauri/src/lib.rs
のrun
の模様。
他にはGreetボタンが押されたときに呼ばれるハンドラが定義されている。
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
println!("Hello from Tauri!");
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
TypeScriptからはinvoke
を介して呼んでる。
# App.tsx
import { invoke } from "@tauri-apps/api/core";
...
async function greet() {
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
setGreetMsg(await invoke("greet", { name }));
}
invoke_handler
で別のハンドラが足せるのかと思って足してみたが、400エラーがdevtoolsのコンソールに表示される。helloをgreetの前に登録したらgreetは動いたので後勝ちで上書きしている模様。複数のハンドラ登録するにはどうするか後で調べる。
あと当たり前だがTauriなので開くコンソールはSafariのdevtoolsが開いて少し新鮮だった。
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 291bed7..9ef1f17 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -4,11 +4,18 @@ fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
+#[tauri::command]
+fn hello(_name: &str) {
+ println!("Hello from Rust!");
+}
+
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
+ // println!("Hello from Tauri!");
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet])
+ .invoke_handler(tauri::generate_handler![hello])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
とあるので読んでみる。v1だけど。
こんだけだったw そうか、配列だもんな。
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 9ef1f17..0287005 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -14,8 +14,7 @@ pub fn run() {
// println!("Hello from Tauri!");
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
- .invoke_handler(tauri::generate_handler![greet])
- .invoke_handler(tauri::generate_handler![hello])
+ .invoke_handler(tauri::generate_handler![greet, hello])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
ハンドラの引数と戻り値を複雑にしてみる。オブジェクトや配列を渡す。
handler関数の引数の名前とinvokeに与えるobjectのfield名が対応するみたいで、どうやってんだこれってなってる。tauri::command
がよしなにやってるんだろうな。
戻り値はserializableな値なら返せそう。
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 9ef1f17..62907e5 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,12 +1,26 @@
+use serde::Serialize;
...
+#[derive(Serialize)]
+struct User {
+ name: String,
+ age: u32,
+}
+
#[tauri::command]
-fn hello(_name: &str) {
- println!("Hello from Rust!");
+fn hello(name: &str, age: u32, enabled: bool, arr: Vec<i32>, obj: serde_json::Value) -> User {
+ println!(
+ "Hello from Rust!, {}! {}! {}! {:#?}! {}!",
+ name, age, enabled, arr, obj
+ );
+ User {
+ name: "Alice".to_string(),
+ age: 30,
+ }
}
diff --git a/src/App.tsx b/src/App.tsx
index 86aa4b1..c1d2029 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -45,6 +45,23 @@ function App() {
<button type="submit">Greet</button>
</form>
+ <form
+ className="row"
+ onSubmit={async (e) => {
+ e.preventDefault();
+ const v = await invoke("hello", {
+ age: 29,
+ name: "John",
+ enabled: true,
+ arr: [1, 2, 3],
+ obj: { a: 1, b: 2 },
+ });
+ console.log(v);
+ }}
+ >
+ <button type="submit">Hello</button>
+ </form>
+
<p>{greetMsg}</p>
</div>
);
tailwindcss
tailwindcss入れてみる。
公式の手順でそのまま有効にできた。
remix
remixをSPAモードで入れてみる。とりあえずrouting用。
appDirectoryはtauriが生成したsrc
をそのまま使うようにした。
src/routes
以下は割愛。
~/tauri-v2-app on main% pnpm i -D @remix-run/react @remix-run/dev vite-tsconfig-paths
...
~/tauri-v2-app on remix-config-js% git diff main -- package.json vite.config.ts
diff --git a/package.json b/package.json
index 36ccc6d..797e268 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,8 @@
"react-dom": "^18.3.1"
},
"devDependencies": {
+ "@remix-run/dev": "^2.11.1",
+ "@remix-run/react": "^2.11.1",
"@tauri-apps/cli": "2.0.0-rc.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
@@ -26,6 +28,7 @@
"postcss": "^8.4.41",
"tailwindcss": "^3.4.9",
"typescript": "^5.5.4",
- "vite": "^5.4.0"
+ "vite": "^5.4.0",
+ "vite-tsconfig-paths": "^5.0.1"
}
}
diff --git a/vite.config.ts b/vite.config.ts
index f74d1b2..a8388ab 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,12 +1,18 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+import { vitePlugin as remix } from "@remix-run/dev";
+import tsconfigPaths from "vite-tsconfig-paths";
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/
export default defineConfig(async () => ({
- plugins: [react()],
+ plugins: [
+ // react(),
+ remix({ ssr: false, appDirectory: "./src" }),
+ tsconfigPaths(),
+ ],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
UI周り〜Rustとの通信周りは大体素振り完了。
あとはRustに慣れるだけだな。
Rust側のTauriのAPIを調べてみるかな。その前にsecurity modelとか把握する必要がありそう。
この辺を読むか。
この時点でリリースビルドを試そうとしてtauri build
したらindex.htmlが読めないという表示に。
remixにしたので、buildもremixを使う必要があった。生成されたファイルの参照先 frontendDist
も変更。
スタイルシートの効きがなんかdevとrelease buildで違う気がするけど、まあいいか。
diff --git a/package.json b/package.json
index 198eaaf..85803d7 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,8 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite",
- "build": "tsc && vite build",
+ "dev": "remix vite:dev",
+ "build": "remix vite:build",
"preview": "vite preview",
"tauri": "tauri",
"tauri:dev": "tauri dev",
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 9a4354d..d93cd5a 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -6,7 +6,7 @@
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
- "frontendDist": "../dist"
+ "frontendDist": "../build/client"
},
"app": {
"windows": [
スタイルシートの効きがなんかdevとrelease buildで違う気がするけど、まあいいか。
tailwindは効いてるみたい。
tauriが生成したApp.cssが効いてないみたいだな、というかむしろdevで効いてるほうがおかしいように見えてきた。
そうね、効いてないページで明示的にimport "../App.css"
とか読んでやるとスタイルが変わるから、devで効いてるほうがこれはおかしいな。
まあデフォルトのApp.cssとか実際は消すだろうからいいか。
へー、CLIも作れるのか、と思ったらこのページはtauri
コマンドのI/Fだった。
コードはここに置いた。