// //本コードは個人の学習目的で作成されたものであり、いかなる保証も行いません。 //利用はすべて自己責任でお願いします。 // --------------------------------------------------------- ↓リマインダー開発4までのファイル構成と内容。 //ファイル構成 D:\xampp8\htdocs\shv-reminder ├index.html ├main.js ├preload.js ├package.json ├package-lock.json //自動生成ファイル ├node_modules/ //自動生成フォルダ └data/ //自動生成フォルダ /* -------------------------------------------------------------- */ // index.html シンプルカレンダー
/* --------------------------------- ------------------------------- */ //main.js import { app, BrowserWindow, ipcMain, Notification, Tray, Menu } from "electron"; import path from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // アプリ用の保存先を「shv-reminder」に固定 app.setAppUserModelId("com.shv.reminder"); app.setPath("userData", path.join(app.getPath("appData"), "shv-reminder")); let tray = null; let mainWindow; function createWindow() { mainWindow = new BrowserWindow({ width: 1000, height: 800, icon: path.join(__dirname, "icon.ico"), webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: false, contextIsolation: true, sandbox: false } }); // レンダラから保存先を問い合わせできるようにする ipcMain.handle("get-user-data-path", () => { return app.getPath("userData"); }); mainWindow.loadFile("index.html"); // ✅ closeイベントをフック(×を押したとき終了させない) mainWindow.on("close", (event) => { event.preventDefault(); // デフォルトの終了を止める mainWindow.hide(); // ウィンドウを隠す(タスクトレイに残る) }); tray = new Tray(path.join(__dirname, "icon.ico")); const contextMenu = Menu.buildFromTemplate([ { label: "開く", click: () => mainWindow.show() }, { label: "隠す", click: () => mainWindow.hide() }, { type: "separator" }, { label: "設定", submenu: [ { label: "自動起動を有効化", type: "checkbox", checked: true, click: (menuItem) => { app.setLoginItemSettings({ openAtLogin: menuItem.checked, path: process.execPath, }); } }, { label: "通知テスト", click: () => { new Notification({ title: "テスト通知", body: "これはサンプル通知です。" }).show(); } } ] }, { type: "separator" }, { label: "終了", click: () => { tray.destroy(); app.quit(); } } ]); tray.setToolTip("SHV Reminder"); tray.setContextMenu(contextMenu); } // ✅ 通知イベント ipcMain.on("show-reminder", (event, { title, body, meta }) => { const notification = new Notification({ title, body }); notification.show(); // 通知が来たらアイコンを赤丸付きに変える tray.setImage(path.join(__dirname, "icon-alert.ico")); }); ipcMain.on("update-badge", (event, count) => { app.setBadgeCount(count); }); // ✅ アプリを開いたら通常アイコンに戻す app.on("browser-window-focus", () => { if (tray) { tray.setImage(path.join(__dirname, "icon.ico")); } }); app.whenReady().then(() => { createWindow(); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") app.quit(); }); /* ---------------------------------------------------------------- */ //preload.js import { contextBridge, ipcRenderer } from "electron"; import path from "path"; import fs from "fs"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); let dataDir = null; ipcRenderer.invoke("get-user-data-path").then((userDataPath) => { dataDir = path.join(userDataPath, "data"); if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true }); }); contextBridge.exposeInMainWorld("electronAPI", { // JSON 読み込み readJSON: (filename) => { const pathinfo = { "package.json": () => { const __dirname = path.dirname(__filename); return path.join(__dirname, `${filename}`); }, default: () => { return path.join(dataDir, `${filename}`); } }; try { const fn = pathinfo[filename] ?? pathinfo.default; const filePath = fn(); if (!fs.existsSync(filePath)) return {}; return JSON.parse(fs.readFileSync(filePath, "utf-8")); } catch (err) { return {}; } }, updateBadge: (count) => ipcRenderer.send("update-badge", count), // JSON 書き込み writeJSON: (filename, data) => { try { const filePath = path.join(dataDir, filename); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8"); } catch (err) { } }, // 🔔 通知を main に依頼 sendReminder: (title, body, meta) => { ipcRenderer.send("show-reminder", { title, body, meta }); }, // 通知サポート有無 canNotify: () => ipcRenderer.invoke("can-notify"), //タスクアイコンから予定追加処理 onOpenAddNote: (callback) => ipcRenderer.on("open-add-note", callback), //通知先の予定を選択 onFocusNote: (callback) => ipcRenderer.on("focus-note", (event, data) => { callback(data); }), }); contextBridge.exposeInMainWorld("appEnv", { NODE_ENV: process.env.NODE_ENV }); /* ---------------------------------------------------------------- */ //package.json { "name": "shv-reminder", "version": "1.1.0", "description": "Simple Reminder", "main": "main.js", "type": "module", "scripts": { "dev": "cross-env NODE_ENV=development electron .", "start": "cross-env NODE_ENV=production electron .", "dist": "electron-builder" }, "keywords": [], "author": "Studio Happyvalley", "license": "ISC", "devDependencies": { "cross-env": "^10.1.0", "electron": "^38.1.2", "electron-builder": "^26.0.12" }, "build": { "appId": "com.shv.reminder", "productName": "SHV Reminder", "win": { "target": "nsis", "icon": "icon.ico" }, "nsis": { "oneClick": false, "perMachine": false, "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "createStartMenuShortcut": true, "runAfterFinish": true } } }