システムトレイ(タスクトレイ)に常駐させる

Recipe ID: menu-009

アプリケーションをシステムトレイ(Windows のタスクトレイ、macOS のメニューバー)に常駐させる方法を解説します。

Tauri v2 では、tauri::tray モジュール(Core機能)を使用してトレイアイコンを作成・管理します。

前提条件

※ アイコン画像のロード形式に合わせて image-pngimage-ico が必要になる場合があります。

Cargo.toml の設定

Tauri v2 ではトレイ機能は Core (本体) に統合されているため、追加のプラグイン(tauri-plugin-tray など)のインストールは不要です。
ただし、src-tauri/Cargo.tomltray-icon 機能を有効にする必要があります。

[dependencies]
tauri = { version = "2", features = ["tray-icon", "image-png"] }

Permissions (権限) の設定

src-tauri/capabilities/default.jsoncore:tray:default を追加します。

{
  "permissions": [
    ...,
    "core:tray:default",
    "core:menu:default", // メニューを表示する場合
    "core:app:allow-default-window-icon" // defaultWindowIcon を使用する場合
  ]
}

1. バックエンドから作成する (Rust) - 推奨

Rust 側で TrayIconBuilder を使用するのが最も標準的で安定した方法です。

Rust(バックエンド)での作成が推奨される理由:
* 安定した常駐性: フロントエンド(WebView)が閉じられたりリロードされたりしても、Rust 側のプロセスは生き続けるため、トレイアイコンが消失するリスクがありません。
* ウィンドウレス起動: ウィンドウを生成しない(または非表示で起動する)常駐型アプリの場合、フロントエンドが存在しないため Rust での実装が必須となります。
* リソース管理: アイコン画像などのリソースへのアクセスや型安全性が確保しやすく、実装が堅牢になります。

use tauri::menu::{MenuBuilder, MenuItem};
use tauri::tray::TrayIconBuilder;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            // トレイメニューの作成
            let quit_i = MenuItem::with_id(app, "quit", "終了", true, None::<&str>)?;
            let menu = MenuBuilder::new(app)
                .item(&quit_i)
                .build()?;

            // トレイアイコンの作成
            let _tray = TrayIconBuilder::with_id("tray")
                // アプリのデフォルトアイコンを使用(tauri.conf.json由来)
                .icon(app.default_window_icon().unwrap().clone())
                .menu(&menu)
                .show_menu_on_left_click(true) // 左クリックでもメニューを出す場合
                .on_menu_event(|app, event| {
                    match event.id().as_ref() {
                        "quit" => {
                            println!("Quit menu item clicked");
                            app.exit(0);
                        }
                        _ => {}
                    }
                })
                .on_tray_icon_event(|tray, event| {
                     // トレイアイコンに対するクリックイベントなど
                     // (menuイベントとは別です)
                     use tauri::tray::TrayIconEvent;
                     if let TrayIconEvent::Click { .. } = event {
                         println!("Tray icon clicked");
                     }
                })
                .build(app)?;

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

2. フロントエンドから作成する (TypeScript)

システムトレイ(タスクトレイ)の常駐はバックエンドから作成する (Rust) ことが推奨されますが、フロントエンドからも作成可能です。しかし、特別な理由がない限りはバックエンドから作成する (Rust) するのが良いでしょう。

import { TrayIcon } from '@tauri-apps/api/tray';
import { Menu } from '@tauri-apps/api/menu';
import { defaultWindowIcon } from '@tauri-apps/api/app';

// トレイメニューの作成
const menu = await Menu.new({
  items: [
    {
      id: 'quit',
      text: '終了',
      action: () => {
        // import { exit } from '@tauri-apps/plugin-process';
        // await exit(0);
        console.log("Quit clicked");
      }
    }
  ]
});

// トレイアイコンの作成
const tray = await TrayIcon.new({
  id: 'tray',
  tooltip: 'My Tauri App',
  menu: menu,
  action: (event) => {
    // Click, DoubleClick, etc.
    console.log('Tray event', event);
  }
});

// アイコンの設定 (デフォルトアイコンを使用する例)
const icon = await defaultWindowIcon();
if (icon) {
    await tray.setIcon(icon);
}

補足

* 静的構成: 単純なトレイアイコンであれば、tauri.conf.jsonapp > trayIcon 設定を使用することも可能ですが、メニューの動的な制御などを行う場合はコードで記述する「動的構成」が推奨されます。
* マルチプラットフォーム: macOS, Windows, Linux でトレイの挙動(クリック時の動作など)が微妙に異なる場合があります。