新しいサブウィンドウを開く (Vite Multi-page)

Recipe ID: win-017

メインウィンドウとは別の、新しいサブウィンドウをプログラムから開く方法を紹介します。
ここでは Vite の Multi-page App (MPA) 機能を活用し、メイン・サブそれぞれに独立したエントリーポイント(HTML/TypeScript)を持たせる構成を解説します。

この構成は、詳細設定画面やヘルプ画面など、メイン画面とは機能や依存関係が大きく異なるウィンドウを作成する際に適しています。

前提条件

Permissions (権限) の設定

プログラムから新しいウィンドウを作成したり、ウィンドウを閉じたりするには権限が必要です。
src-tauri/capabilities/default.json に以下の権限を追加設定してください。

1. core:webview:allow-create-webview-window: ウィンドウ作成に必要
2. core:window:allow-close: ウィンドウを閉じる操作(appWindow.close())に必要
3. windows: 各権限を適用するウィンドウを指定します。ここではメインウィンドウ (main) に加えて、今回のサンプルで作成するサブウィンドウのラベル settings-window を明示的に指定します。

{
  "permissions": [
    ...,
    "core:window:default",
    "core:webview:allow-create-webview-window",
    "core:window:allow-close"
  ],
  "windows": ["main", "settings-window"]
}

1. ディレクトリ構成と Vite の設定

Vite で複数の HTML ページをビルド対象にする設定を行います。

ディレクトリ構成の例

サブウィンドウ用のファイルを subwindow.html とし、ソースコードを src/subwindow/ ディレクトリ配下に整理する構成例です。

.
├── index.html            # メイン/親ページ
├── subwindow.html        # サブ/子ページ
├── src/
│   ├── main.ts           # メインページ用スクリプト
│   └── subwindow/
│       └── main.ts       # サブページ用スクリプト
├── vite.config.ts
└── ...

vite.config.ts の設定

build.rollupOptions.input に、すべてのエントリーポイント(HTMLファイル)を定義します。

import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  // ...その他の設定
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        subwindow: resolve(__dirname, 'subwindow.html'),
      },
    },
  },
});

path モジュールは Node.js の標準モジュールですが、TypeScript で使用する場合には型定義ファイルのインストールが必要になることがあります。もし path の箇所でエラーが出る場合は、以下のコマンドを実行してください。

npm install -D @types/node

この設定により、ビルド時に subwindow.html も処理され、それぞれ独立した JavaScript バンドルが生成されます。

2. 子ページ(サブウィンドウ)の実装

サブウィンドウ側は、独立した HTML と TypeScript として実装します。

subwindow.html

プロジェクトのルートディレクトリに作成します <body> タグ内で、サブウィンドウ用の TypeScript を読み込みます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sub Window</title>
</head>
<body>
  <div class="container">
    <h1>設定サブウィンドウ</h1>
    <p>ここは独立したページです。</p>
    <button id="close-btn">閉じる</button>
  </div>
  
  <!-- サブウィンドウ用のスクリプト -->
  <script type="module" src="/src/subwindow/main.ts"></script>
</body>
</html>

src/subwindow/main.ts

サブウィンドウ内でのロジックを記述します。

import { getCurrentWindow } from '@tauri-apps/api/window';

// 現在のウィンドウ(自分自身)を取得
const appWindow = getCurrentWindow();

document.getElementById('close-btn')?.addEventListener('click', async () => {
    // 自分自身を閉じる
    await appWindow.close();
});

console.log('サブウィンドウのスクリプトが読み込まれました');

3. 親ページ(メインウィンドウ)の実装

メインウィンドウから WebviewWindow クラスを使ってサブウィンドウを開きます。

src/main.ts

import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

async function openSubWindow() {
  const label = 'settings-window';

  // タブ重複などを防ぐため、既に存在するかチェックする
  const existingWin = await WebviewWindow.getByLabel(label);
  if (existingWin) {
    // 既に開いている場合はフォーカスを当てる
    await existingWin.setFocus();
    return;
  }

  // 新しいウィンドウを作成
  const webview = new WebviewWindow(label, {
    url: '/subwindow.html', // Vite上のパスを指定
    title: '設定',
    width: 600,
    height: 400,
    resizable: false,
    center: true,
  });

  webview.once('tauri://created', () => {
    console.log('サブウィンドウ作成成功');
  });

  webview.once('tauri://error', (e) => {
    console.error('サブウィンドウ作成失敗:', e);
  });
}

// 呼び出し例
document.querySelector('#open-settings')?.addEventListener('click', openSubWindow);

解説

* url指定: new WebviewWindowurl には、Web サーバー上のパス(/subwindow.html)を指定します。Vite の MPA 設定が正しければ、開発中も本番ビルド後も正しくルーティングされます。
* 独立性: この構成により、メインウィンドウとサブウィンドウで異なる CSS やライブラリを読み込むことが可能になり、コードの保守性が向上します。