---
title: "デスクトップアプリのビルド手順"
---

## 概要

MorningStatusApp デスクトップアプリ（Electrobun）のビルドとインストーラ・DMG 作成手順。

インストーラ／アプリバンドルには Next.js standalone サーバーが同梱されており、アプリ起動時に自動的にサーバーが立ち上がります。

## 前提条件

### Windows

- [Inno Setup 7](https://jrsoftware.org/isinfo.php) がインストール済みであること
  - `winget install JRSoftware.InnoSetup.7` でインストール可能
  - インストール先: `%LOCALAPPDATA%\Programs\Inno Setup 7\ISCC.exe`
- `.env.local`（プロジェクトルート）が存在すること（接続先 Blob URL・API キーを含む）

### macOS

- Bun がインストール済みであること（`curl -fsSL https://bun.sh/install | bash`）
- `.env.local`（プロジェクトルート）が存在すること（接続先 Blob URL・API キーを含む）

## Windows ビルド手順（推奨：一括ビルド）

`build-installer.ps1` が以下をすべて自動実行します：

1. `DESKTOP_MODE=1` で Next.js standalone ビルド
2. Electrobun アプリビルド
3. Inno Setup でインストーラ生成

```powershell
pwsh -File setup/build-installer.ps1
```

成果物: `setup/Output/MorningStatusApp-Setup-{version}.exe`

## macOS ビルド手順（推奨：一括ビルド）

`build-macos.sh` が以下をすべて自動実行します：

1. `DESKTOP_MODE=1` で Next.js standalone ビルド
2. Electrobun アプリビルド（`.app` バンドル生成）
3. Next.js standalone を `.app` バンドル内にコピー

```bash
bash setup/build-macos.sh
```

成果物:
- `.app` バンドル: `desktop/build/dev-macos-arm64/MorningStatusApp-dev.app`（Apple Silicon）
- DMG: `desktop/artifacts/`（Electrobun が自動生成する場合）

> **注意:** macOS ビルドは macOS 環境で実行すること。

## ビルド手順（ステップ別）

### 1. Next.js standalone ビルド

```bash
DESKTOP_MODE=1 bun run next build
```

`DESKTOP_MODE=1` を指定すると `next.config.ts` が `output: 'standalone'` を有効化し、
`.next/standalone/` に自己完結型サーバーが生成されます。

### 2. Electrobun アプリのビルド

```bash
cd desktop
bunx electrobun build
```

成果物:
- Windows: `desktop/build/dev-win-x64/MorningStatusApp-dev/`
- macOS ARM64: `desktop/build/dev-macos-arm64/MorningStatusApp-dev.app/`
- macOS Intel: `desktop/build/dev-macos-x64/MorningStatusApp-dev.app/`

### 3a. インストーラのビルド（Windows）

```powershell
pwsh -File setup/build-installer.ps1
```

- バージョンは root の `package.json` から自動取得する
- ISCC.exe が見つからない場合はエラーメッセージが表示される

### 3b. Next.js を .app バンドルにコピー（macOS）

`build-macos.sh` が自動実行するが、手動でも実行可能：

```bash
APP=desktop/build/dev-macos-arm64/MorningStatusApp-dev.app
RESOURCES=$APP/Contents/Resources
cp -r .next/standalone/. $RESOURCES/nextjs/standalone/
cp -r .next/static       $RESOURCES/nextjs/standalone/.next/static
cp -r public             $RESOURCES/nextjs/standalone/public
cp .env.local            $RESOURCES/nextjs/standalone/.env.local
```

### 4. インストール

**Windows:** 生成した `MorningStatusApp-Setup-{version}.exe` を実行する。

- インストール先: `%ProgramFiles%\MorningStatusApp`（ユーザーが変更可能）
- スタートメニュー: `MorningStatusApp` グループが作成される
- アンインストール: コントロールパネル／設定からアンインストール可能

**macOS:** `MorningStatusApp-dev.app` を `/Applications` にコピーして起動する。

## アプリを改修した場合のフロー

```
1. package.json のバージョンを更新する（必要な場合）
2. （Windows）pwsh -File setup/build-installer.ps1
   （macOS）  bash setup/build-macos.sh
```

## 起動フロー（インストール済みアプリ）

1. `launcher.exe`（Windows）/ `MorningStatusApp`（macOS）→ Electrobun ランチャー起動
2. `bun/index.js` Worker 起動
3. Worker が `Resources/nextjs/standalone/server.js` を検出し、同梱の `bun`（または `bun.exe`）で Next.js サーバーを起動
4. `.env.local`（standalone ディレクトリに配置）から接続先設定を読み込み
5. サーバー起動待機後、WebView ウィンドウを `http://localhost:3001` で表示

## インストーラに含まれるファイル

| 配置先 | 内容 |
|---|---|
| `{app}/bin/` | Electrobun バイナリ（`bun.exe`, `launcher.exe` 等） |
| `{app}/Resources/app/bun/` | Electrobun ワーカー (`index.js`) |
| `{app}/Resources/nextjs/standalone/` | Next.js standalone サーバー |
| `{app}/Resources/nextjs/standalone/.next/static/` | 静的アセット |
| `{app}/Resources/nextjs/standalone/public/` | public ディレクトリ |
| `{app}/Resources/nextjs/standalone/.env.local` | 接続設定（Blob URL・API キー） |

## バージョン管理

- Windows インストーラのバージョンは `build-installer.ps1` が root の `package.json` から読み取り `/DAppVersion=X.Y.Z` として Inno Setup に渡す
- Electrobun アプリ（macOS）のバージョンは `desktop/electrobun.config.ts` と `desktop/package.json` で管理する
- Windows インストーラバージョンを上げる場合: root の `package.json` を更新
- Electrobun アプリバージョンを上げる場合: `desktop/electrobun.config.ts` と `desktop/package.json` を更新

## アイコンの更新手順

アイコン素材は `desktop/assets/` に配置する。

```
desktop/assets/
  icons8-コーヒーカップ-100.png  # ソース画像（Icons8）
  icon.ico                        # Windows 用（100px PNG から生成）
  icon.iconset/                   # macOS 用（sharp で各サイズ生成済み）
    icon_16x16.png
    icon_16x16@2x.png
    icon_32x32.png
    icon_32x32@2x.png
    icon_128x128.png
    icon_128x128@2x.png
    icon_256x256.png
    icon_256x256@2x.png
    icon_512x512.png
```

### アイコンを差し替える場合

より高解像度のソース画像（512px 以上推奨）を入手したら、以下のスクリプトで再生成する：

```bash
# Windows（Bun）: icon.ico を再生成
bun -e "
const pngPath = 'desktop/assets/<新しい画像>.png';
const icoPath = 'desktop/assets/icon.ico';
const pngData = await Bun.file(pngPath).arrayBuffer();
const pngBytes = new Uint8Array(pngData);
const buf = new ArrayBuffer(6 + 16 + pngBytes.length);
const view = new DataView(buf);
view.setUint16(0, 0, true); view.setUint16(2, 1, true); view.setUint16(4, 1, true);
view.setUint8(6, 0); view.setUint8(7, 0); view.setUint8(8, 0); view.setUint8(9, 0);
view.setUint16(10, 1, true); view.setUint16(12, 32, true);
view.setUint32(14, pngBytes.length, true); view.setUint32(18, 22, true);
new Uint8Array(buf, 22).set(pngBytes);
await Bun.write(icoPath, buf);
"

# macOS: iconset を再生成（macOS 環境で実行）
mkdir -p desktop/assets/icon.iconset
sips -z 16 16   <ソース>.png --out desktop/assets/icon.iconset/icon_16x16.png
sips -z 32 32   <ソース>.png --out desktop/assets/icon.iconset/icon_16x16@2x.png
sips -z 32 32   <ソース>.png --out desktop/assets/icon.iconset/icon_32x32.png
sips -z 64 64   <ソース>.png --out desktop/assets/icon.iconset/icon_32x32@2x.png
sips -z 128 128 <ソース>.png --out desktop/assets/icon.iconset/icon_128x128.png
sips -z 256 256 <ソース>.png --out desktop/assets/icon.iconset/icon_128x128@2x.png
sips -z 256 256 <ソース>.png --out desktop/assets/icon.iconset/icon_256x256.png
sips -z 512 512 <ソース>.png --out desktop/assets/icon.iconset/icon_256x256@2x.png
sips -z 512 512 <ソース>.png --out desktop/assets/icon.iconset/icon_512x512.png
iconutil -c icns desktop/assets/icon.iconset  # .icns も生成される
```

## 注意事項

- `setup/Output/` はリポジトリから除外（`.gitignore` 設定済み）
- `desktop/build/` / `desktop/artifacts/` も同様に除外済み
- `.env.local`（プロジェクトルート）には API キー等が含まれるため、インストーラに同梱されるファイルを公開しないこと
