import "dotenv/config"; import { electronApp, optimizer, is } from "@electron-toolkit/utils"; import { app, ipcMain, BrowserWindow, shell } from "electron"; import filesystemPlugin from "@takerofnotes/plugin-filesystem"; import supabasePlugin from "@takerofnotes/plugin-supabase"; import fs from "fs/promises"; import path, { join } from "path"; import __cjs_mod__ from "node:module"; const __filename = import.meta.filename; const __dirname = import.meta.dirname; const require2 = __cjs_mod__.createRequire(import.meta.url); class PluginRegistry { constructor() { this.plugins = /* @__PURE__ */ new Map(); } register(plugin) { if (!plugin.id) { throw new Error("Plugin must have an id"); } this.plugins.set(plugin.id, plugin); } get(id) { return this.plugins.get(id); } list() { return Array.from(this.plugins.values()).map((plugin) => ({ id: plugin.id, name: plugin.name, description: plugin.description, configSchema: plugin.configSchema })); } } const USER_DATA_STRING = "__DEFAULT_USER_DATA__"; class Config { constructor(defaultPlugin) { this.defaultPlugin = defaultPlugin; this.configPath = path.join(app.getPath("userData"), "config.json"); } // Helper to replace placeholders with dynamic values _resolveDefaults(config) { if (Array.isArray(config)) { return config.map((item) => this._resolveDefaults(item)); } else if (config && typeof config === "object") { const resolved = {}; for (const [key, value] of Object.entries(config)) { resolved[key] = this._resolveDefaults(value); } return resolved; } else if (typeof config === "string" && config.includes(USER_DATA_STRING)) { return config.replace(USER_DATA_STRING, app.getPath("userData")); } else { return config; } } async load() { let parsed; try { const raw = await fs.readFile(this.configPath, "utf8"); parsed = JSON.parse(raw); } catch (err) { parsed = null; } if (!parsed || !parsed.activeAdapter) { const defaultConfig = {}; for (const field of this.defaultPlugin.configSchema) { defaultConfig[field.key] = field.default ?? null; } parsed = { ...parsed ? parsed : {}, activeAdapter: this.defaultPlugin.id }; parsed.adapters[this.defaultPlugin.id] = defaultConfig; await this.write(parsed); } else { parsed.adapterConfig = this._resolveDefaults(parsed.adapterConfig); } return parsed; } async write(configObject) { const dir = path.dirname(this.configPath); await fs.mkdir(dir, { recursive: true }); const resolvedConfig = { ...configObject, adapterConfig: this._resolveDefaults(configObject.adapterConfig) }; await fs.writeFile( this.configPath, JSON.stringify(resolvedConfig, null, 2), "utf8" ); } } const preloadPath = join(__dirname, "../preload/index.mjs"); const rendererPath = join(__dirname, "../renderer/index.html"); function createWindow() { const mainWindow = new BrowserWindow({ width: 354, height: 549, show: false, autoHideMenuBar: true, webPreferences: { preload: preloadPath, sandbox: false } }); mainWindow.on("ready-to-show", () => { mainWindow.show(); }); mainWindow.webContents.setWindowOpenHandler((details) => { shell.openExternal(details.url); return { action: "deny" }; }); if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]); } else { mainWindow.loadFile(rendererPath); } } function createNoteWindow(noteId) { const noteWindow = new BrowserWindow({ width: 354, height: 549, autoHideMenuBar: true, webPreferences: { preload: preloadPath, contextIsolation: true, nodeIntegration: false, sandbox: false } }); if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { noteWindow.loadURL( `${process.env["ELECTRON_RENDERER_URL"]}/#/note/${noteId}` ); } else { noteWindow.loadFile(rendererPath, { hash: `/note/${noteId}` }); } } app.whenReady().then(async () => { ipcMain.on("open-note-window", (_, noteId) => { createNoteWindow(noteId); }); const broadcastNoteChange = (event, data) => { BrowserWindow.getAllWindows().forEach((win) => { win.webContents.send(event, data); }); }; const registry = new PluginRegistry(); registry.register(filesystemPlugin); registry.register(supabasePlugin); const config = new Config(filesystemPlugin); const initialConfig = await config.load(); const setActivePlugin = async (pluginId) => { const currentConfig = await config.load(); await config.write({ ...currentConfig, activeAdapter: pluginId }); const plugin = registry.get(pluginId); const adapterConfig = currentConfig.adapters[pluginId] || {}; const adapter = plugin.createAdapter(adapterConfig); await adapter.init(); ipcMain.removeHandler("adapter:call"); ipcMain.handle("adapter:call", async (_, method, args) => { if (!adapter[method]) { throw new Error(`Invalid adapter method: ${method}`); } return await adapter[method](...args); }); broadcastNoteChange("plugin-changed", pluginId); console.log("activePlugin: ", pluginId); return true; }; await setActivePlugin(initialConfig.activeAdapter); ipcMain.handle("getConfig", async () => { return await config.load(); }); ipcMain.handle("setConfig", async (_, newConfig) => { await config.write(newConfig); }); ipcMain.handle("listPlugins", async () => { return registry.list(); }); ipcMain.handle("setActivePlugin", async (_, pluginId) => { return await setActivePlugin(pluginId); }); ipcMain.on("note-changed", (_, event, data) => { broadcastNoteChange(event, data); }); electronApp.setAppUserModelId("com.electron"); app.on("browser-window-created", (_, window) => { optimizer.watchWindowShortcuts(window); }); createWindow(); app.on("activate", function() { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } });