【JavaScript】ここがしんどかったよTauri開発【Rust】

2024年2月2日にTauri 2.0がβ版となりました。昨年から個人的にいくつかTauriの1系でデスクトップアプリケーションを開発してきたのですが、ここが辛かった！というのを列挙してみました。

因みに公開しているのは以下となります。

- [Iron Beads Editor(Mac OS版)](https://github.com/bu-kurokky/IBE)
- [String Scouter(Mac OS版)(Windows版)](https://black-tree.net/stringscouter/)

尚、Iron Beads Editor(以下IBE)に関しては元々Vanilla.jsでweb側を作っていて、それの機能追加版となります。

## なぜTauriを選択したのか？

まずTauriを選択する前に以下のプラットフォームを検討しました。

|Platform|所感とか|
| --- | --- |
| [Electron](https://www.electronjs.org/) | The 王道！Chromiumを同時にインストールさせる、ノウハウ沢山しかし、バイナリがでかい |
| [gluon](https://github.com/gluon-framework/gluon) | 各配布先のユーザーのnode.jsを使うので非エンジニアに対してハードル高杉FireFox対応してるDenoやBunが使える （だから何？）|
| [Tauri](https://tauri.app) | Rust製…今回の記事 |
| [neutralino](https://neutralino.js.org/) | インドの人が開発。DBが貧弱と言われるがjsonで管理できるものなら問題ない感じ |

ElectronはLinux用のGUIツールが必要だった際にやったことあったので、neutralinoとTauriで同時に開発していったのですがドキュメントの見やすさでTauriに軍配が上がりました。  
それでも色々ひっかかりました。以下は本題のひっかかり所です。

## JS側のサンプルは充実しているのにRust側のExampleが貧弱

多分、Tauri側のコンセプトがRustを最低限書けばあとはJSを書くだけで動くというのがあるのでしょうが明らかにRust側のサンプルが少なすぎるんですね。  
例えば、windowのタイトルを動的に変更したい場合JS側は[setTitle](https://tauri.app/v1/api/js/window/#settitle)にきちんとサンプルがありますが、Rust側の[set_title](https://docs.rs/tauri/1.5.4/tauri/window/struct.Window.html#method.set_title)には最低限の記述しかありません。  

ググると必ずJS側のサンプルばかりがhitして「Rust側でどうやって実装するんだっけ？」みたいな時に何度か迷子になっていました。（そもそもmethodが存在するか？のチェックを何度もしていたので迷子になった）
開発効率を考えるとRust側の方のdocsにもExampleがあった方が親切だと思いました。

## WRYに引きづられる
当たり前のことですが結構重要だったので記述します。  
TauriはWebViewの部分を[WRY(WebView Rendering Library）](https://github.com/tauri-apps/wry)を使っています。([Servo](https://www.publickey1.jp/blog/24/rustservoelectrontauri.html)の様な代替のものも検討されていますが、多くはWRYを使っていると思っています)

「何が言いたいか？」というとChromeとか最新ブラウザで動くものをTauriに移植しようとしてもWRYで対応していないタグやCSSがあって動かないということです。

実際にIBEのweb版ではdialogタグを使っていたのですが、WRYでは対応してませんでした。

```html
<dialog>ダイアログの内容</dialog>
```

他にもdomの画像化に対して[html2canvas](https://github.com/niklasvh/html2canvas)を使っていたのですが、cssのcounter-increment部分が上手くできなくて[htmlToImage](https://github.com/bubkoo/html-to-image)で無理やり実装しました。

## release版だけ起こる謎のバグ

前述したWRYの対応していないタグに似ていますが、JSでHTMLのinput type="color"の値を動的に変更しようとするとdev版では何も問題ないのにBuildしてrelease版を使うとWRY部分だけ再起動してしまう謎のバグがありました。(ログにも出ない)  

[2024/02/15追記]
思い出したことがあったので追記します。
日本語と英語対応しようと思っていた時に戻り値違いがあってdevだと「ja-JP」でbuildすると「en-JP」になることもありました。

```rust
#[tauri::command]
fn is_japanese() -> bool {
    let current_locale = current_locale::current_locale();
    let current_lang = current_locale.ok().unwrap();
    //println!("current_lang: {}", current_lang);
    //devだと「ja-JP」buildだと「en-JP」が戻り値になる
    if current_lang.ends_with("JP") {
      return true;
    }
    return false;
}
```
[追記ここまで]

## Mac/Windowでクレートの挙動が違う…

String Scouterでは自分の拘りもあって変わったUIにしました。  
で、やっぱり透過をしたら影をつけたくなるので[window-shadows](https://github.com/tauri-apps/window-shadows)をやったところWindowsで以下のようになりました。(四角のWindowにしか対応してない…)

![スクリーンショット 2024-02-09 17.39.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043424/5f8f389f-7732-6b0e-96b8-cf8e441b7a78.png)


また、所謂send keyとして[enigo](https://github.com/enigo-rs/enigo)を使いましたが、Macでは「Key::Layout('c')」で落ちました。  
で、仕方なく[raw](https://docs.rs/enigo/latest/enigo/keycodes/enum.Key.html#variant.Raw)を使うことで対応できたんですが、今度はWindowsだと動かなくて…

```rust
if cfg!(target_os = "macos") {
    enigo.key_down(Key::Meta);
    enigo.key_click(Key::Raw(8));
    enigo.key_up(Key::Meta);
} else {
    enigo.key_down(Key::Control);
    enigo.key_click(Key::Layout('c'));
    enigo.key_up(Key::Control);
}
```

みたいな処理で対応しました。

## OSで挙動が違う

まぁ、クレートなら仕方ないと思いますがこっちはTauriそのものです。  
String Scouterでは必要な時にアプリケーションのWindowをActive状態(focusをする)にしていたのですが、Macだとhideして状態のwindowをshowしていなくてもset_focusすればActiveになるんですが、Windowsだと絶対にhideしたものはshowしなければダメでした。


```rust
let window = app.get_window("main").unwrap();
window.hide().unwrap();
//↑の状態で↓のmethodを叩くとMacはActiveになるがWindowsはダメ
window.set_focus().unwrap();
//ちゃんとhideしたらshowしましょう
window.show().unwrap();
```

## menuに対するselectedをチェックするメソッドがない(文字列をチェックするのもない)

以下のようなメニューがあった際、通常ならば「チェックの有無」でToggleすると思うのですが、「set_selected(true)」はあるのに「get」がなくて、これを不整合なくやるには一々flagで管理するか何らかのstorageなどに保存していくことになるので面倒でした。  

![スクリーンショット 2024-02-14 15.04.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043424/85a0625d-294c-dd29-d4d8-de36184560e9.png)


(個人的には色々検討した結果Toggleにはしなかったのですが、クレートを使わずに対応する場合だとlocalstorageに入れるなどでしょうか？)


## AIが使えない（GeminiもChat GPTも架空のmethodを教えてくる）

Chat GPTに関しては情報が古めなこともあり仕方ない部分もありますが、Gemini(旧Bard)も存在しないmethodをガンガンおすすめしてきて（前述したset_selectedのget版とか）当たり前ですが、こういうあまり需要がなかったりニッチなものはAIも役に立たないなぁと思いました。

存在しないクレートをおすすめしたりするのは本当勘弁して欲しかったです。

尚、精度としてはGeminiの方がリファクタリングも含めて圧倒的でした。

## それでもTauriは楽だ！

もっとひっかかり所はあったのですが、印象的なものを記述しました。  
まぁ、String Scouterのようなものならば[AutoHotkey](https://www.autohotkey.com/)などでサクッと作れますが、GUIにこだわったりOSを超えてとなると微妙です。(特に昨今同じOSでもCPUがIntelとArm系が混じっていたりしますし)

Tauriはマルチプラットフォームなのに出てくる記事がMacのみだったりWindowsのみだったり複数のOSでbuildした記事があまりなく四苦八苦しました。  
Tauriは複数OSのバイナリも「Github Actionsがあれば楽勝！」みたいなのも見かけたのですが、ちゃんと実機である程度buildできる状態にしてからでないとCIに任せられないのはElectronでもGoでもなんでも一緒なのと、もうちょっとマルチプラットフォームでのBuildの記事やノウハウが広がるといいなと、version2.0に期待しています。

とは言え、Electronと比較してバイナリサイズの小ささやRustの恩恵を得られますし、QtやGTKでは表現し辛いGUIもGLなんかを書かずにCSSでサクっと作れるのは凄く恩恵があると思います。  
個人的には[Servo](https://www.publickey1.jp/blog/24/rustservoelectrontauri.html)に期待して今後も使っていこうと思いました。