通知の方法はOSのネイティブ通知機能を使うElectronにはブラウザと同じ Notification API がありそれを使って通知するようにします。
例えば:
new Notification("予定のお知らせ", { body: "歯医者の時間が近いです!" });
これをレンダプロセスへ記述することでOS標準の通知が発生します。
また、通知がデスクトップの右下からスライドする仕様に関しては、インストールしないと機能しません。
環境によってはそれも出ない可能性はありますが最終的にはアクションセンターに必ず通知が表示されます。
私の場合も通知時のサウンドもタスクトレイの右横からスライドもインストールして初めて起動したのでこれが通常のケースなのかなと思います。
ただ、通知は見逃しやすいので今回はサウンドと、アイコンに赤丸表示に変化という機能をつけることで、気づきやすくしました。
アクションセンターの通知
通知の設定。『通知時にサウンド・・・』にチェック。 機能の要件定義繰り返し通知も可能ですが、今回は一回だけとします。
あと、通知の設定は最終的にはOS側で設定する必要があります。
アクションセンターのパレットの右端に『通知の設定』メニューがありますが、ここをクリックで設定します。
これもインストールする前はelectronの名前で表示されていると思います。
実装の流れ予定データ(date, time, alertMinutes) を元に「通知予定時刻」を計算
setInterval で1分おきにチェック。
現在時刻が「通知予定時刻」を過ぎたら通知
一度通知した予定は、再通知しない
また、アプリとしてインストールする前だと通知をOSが受け取らない可能性が高いので、サウンドの設定をし音を鳴らすようにします。あとのことを考えるとwavファイルがいいと思います。本来は1~2秒の短い通知音がいいとは思いますが、朝の目覚めなどに利用する時とかは好きなポップかクラシック音楽なんかでもいいかもしれませんね。
なんにしてもほとんどの不具合はインストールすれば解消します。
まず日付のフォートを桁揃えに変更内部的には計算のしやすさから桁をそろえてませんが、日付の比較などは ISO 8601形式(国際標準の日付表記)でおこなうため、remminderのみ、桁ぞろえで使用します。
function formatDateToISO(dateStr) {
// "2025-9-26" → "2025-09-26"
const [y, m, d] = dateStr.split("-").map(Number);
const mm = String(m).padStart(2, "0");
const dd = String(d).padStart(2, "0");
return `${y}-${mm}-${dd}`;
}
通知機能のコード通知はレンダラープロセスのsetIntervalで1分置きに通知時刻と比較し超えていたら、new Notification().show() で通知するという流れとなります。
通知が済んだ予約はnotifiedに格納していきます。
また、予約を編集する都度、setIntervalも更新する必要があるので、再起動時前の処理をリセットするようにしてあります。
let reminderTimer= null;
let notified = {}; // 通知済みフラグ
function startReminder() {
if (reminderTimer) {
clearInterval(reminderTimer); // すでに動いていたらリセット
}
// 通知チェック間隔(ミリ秒)
const CHECK_INTERVAL = 60 * 1000; // 1分ごと
reminderTimer = setInterval(() => {
const notes = getAllNotes();
const now = new Date();
for (const dateStr in notes) {
const note = notes[dateStr];
if (!note.alertMinutes) continue;
if(note.status == 'done') continue;
//日付をISOフォーマットに変換
const isoDate = formatDateToISO(dateStr);
// UIのtime欄
const target = new Date(`${isoDate}T${note.time || "09:00"}:00`);
const alertTime = new Date(target.getTime() - note.alertMinutes * 60 * 1000);
const key = `${dateStr}-${note.title}`;
if (notified[key]) continue;
console.log("SetInterval起動");
if (now >= alertTime && now < target) {
console.log("通知発生", alertTime);
new Notification([`[${dateStr}/${note.time}: ${note.title}]\n${note.alertMinutes}分前のお知らせ`,
`${note.title}\n${note.body}`
]);
// trayアイコン変更のリクエストをメインに送る
ipcRenderer.send("reminder-alert");
// サウンド
try {
const audio = new Audio("./sounds/Ring10.wav");
audio.play();
} catch (e) { /* ignore */ }
notified[key] = true;
}
}
}, CHECK_INTERVAL);
}
startReminderを呼び出すタイミング今はindex.html のロード完了後に通知用関数startReminderを呼び出します。
document.addEventListener("DOMContentLoaded", () => {
startReminder(i.currentUserKey);
});
main.jsメインプロセスの処理ではデスクトップのタスクトレイにアイコンを表示させ、右クリックするとメニューが表示されるようにします。
Electronのタスクトレイ関連:https://www.electronjs.org/ja/docs/latest/api/tray
const { app, BrowserWindow, Tray, Menu, ipcMain } = require("electron");
const path = require("path");
// 通知ポップアップに必須!
app.setAppUserModelId("com.shv.reminder");
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 800,
icon: path.join(__dirname, "icon.ico"),
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
win.loadFile('index.html'); // ←これまで作ったカレンダーがそのまま使える!
tray = new Tray(path.join(__dirname, "icon.ico"));//
const contextMenu = Menu.buildFromTemplate([
{ label: '開く', click: () => win.show() },
{ label: '終了', click: () => app.quit() }
]);
tray.setToolTip('SHV Reminder');
tray.setContextMenu(contextMenu);
// ウィンドウに戻ったら通常アイコンへ
win.on("focus", () => {
tray.setImage(path.join(__dirname, "icon.ico"));
});
// アプリを開いたら通常アイコンに戻す
app.on("browser-window-focus", () => {
if (tray) {
tray.setImage(__dirname + '/icon.ico');
}
});
}
ipcMain.on("reminder-alert", () => {
if (tray) {
tray.setImage(path.join(__dirname, "icon-alert.ico"));
}
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});

アイコンまず、アイコン画像がないとビルドできないので、必須です。
必要な画像はサイズ256ⅹ256以上のICOファイル2点となります。
通常表示のアイコンと、通知が来た時の赤丸付きアイコンです。
別に赤丸でなくてもアイコンの色自体が変わるなどでもいいと思います。
ファイル名は任意で構いませんが、icon.ico, icon-alert.icoで問題ないかと思います。
通常アイコン
通知時の赤丸付きアイコン