Nextron(Next.js+Electron)を使ってデスクトップアプリの作成を試してみたので共有

2024/08/01に公開

デスクトップアプリを作ったことがなかったものの作業効率化のツールをなるべく時間をかけずに作りたいと思ったので、今回作りました。

成果物は以下の通り
https://prompt-bird.com/

PromptBirdは、生成AI向けのプロンプトを管理・記録するためのメモアプリです。ユーザーはカスタマイズ可能なタブを使用してプロンプトや結果を整理でき、生産性と効率を向上させます。主な機能には、CSV形式でのデータエクスポート、お気に入りのプロンプトのフィルタリング、デスクトップ向けの使いやすいインターフェースがあります。

Nextronとは

https://github.com/saltyshiomix/nextron

Nextronは、Next.jsとElectronを組み合わせて、Web技術を使用してクロスプラットフォームのデスクトップアプリケーションを作成するためのツールです。Nextronを使用すると、Next.jsアプリケーションをElectronでラップして、デスクトップ環境で実行できるアプリケーションに変換できます。

Electronはデスクトップアプリケーションを開発するためのフレームワークで、JavaScript、HTML、CSSを使用してクロスプラットフォームなアプリケーションを作成できます。

npx create-nextron-app MY_APP --example with-tailwindcss

生成するにはこれだけです。特に難しいことを考えることなく

{
  "scripts": {
    "dev": "nextron",
    "build": "nextron build"
  }
}

npm run dev でローカル開発
npm run build でアプリをパッケージ化してくれます。

/renderer にてNext.jsが組み込まれている

ので、良い感じに整えていきます

Navigationのスタイルなどは、今回はtailwind-uiをコピペして時短しました(課金必要あり)
https://tailwindui.com/components

静的モードでの使用

https://github.com/saltyshiomix/nextron/issues/263
この話題を見つけたので、今回Next.jsは静的モードで使用しました。これにより、サーバーサイドレンダリングに関連する問題を回避し、デスクトップアプリケーションとしての性能を最適化しました。

メインプロセスとレンダラープロセスのやり取り

メインプロセスとレンダラープロセスのやり取りを行う際には icpMain を使います。今回PromptBirdであれば、クリップボードへの貼り付け処理がそれに当たります。

ipcMainは、Electronというフレームワークで使われるモジュールの一つです。ipcMainは「インター・プロセス・コミュニケーション(IPC)」の一部で、主にElectronのメインプロセスとレンダラープロセス(ブラウザウィンドウ内で動作するプロセス)間の非同期通信を扱います。具体的には、メインプロセスでイベントリスナーを設定し、レンダラープロセスから送られてくるメッセージを受け取って処理するために使用されます。

preload.tsにて

contextBridge.exposeInMainWorld("electron", {
  writeToClipboard: (text: string) => ipcRenderer.sendSync("write-to-clipboard", text),
});

メインプロセス(background.ts)にて

ipcMain.on("write-to-clipboard", (event, text) => {
  try {
    clipboard.writeText(text);
    ...
  } catch (error) {
    console.error("Failed to write text to clipboard:", error);
    ...
  }
});

rendererプロセスから、

window.electron.writeToClipboard(text);

で呼び出します

簡単です

データの保存

electronではデータの保持はいくつか方法があるみたいですが、今回はサクッと IndexedDB にしました。

実際のコード

処理は大体以下の通りです

import { openDB } from "idb";

const dbName = "____";
const storeName = "____";

async function initDB() {
  return openDB(dbName, 1, {
    upgrade(db) {
      if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName);
      }
    },
  });
}

export async function setValue<T>(key: string, value: T) {
  const db = await initDB();
  const tx = db.transaction(storeName, "readwrite");
  const store = tx.objectStore(storeName);
  await store.put(value, key);
  await tx.done;
}

export async function getValue<T>(key: string): Promise<T> {
  const db = await initDB();
  const tx = db.transaction(storeName, "readonly");
  const store = tx.objectStore(storeName);
  return store.get(key);
}

これをあちこちで呼ぶだけです

所感

  • Next.jsとtailwind-cssを用いるので爆速開発ができて良いと思った
  • Next.jsを使用したことのある人なら、すぐに取り組めると感じた
  • electron自体が提供しているメインプロセスモジュールがいくつかあるので、ちゃんとしたものを作ろうとするとここを読み込む必要がありそう
  • Electron未経験の自分でも半日とかで↑のアプリを作ることができたので爆速開発に向いてそう
  • これでwindowsアプリもmacアプリも一気に作ることができるのは良いなと思いました

Discussion