backsplash-app
Version:
An AI powered wallpaper app.
231 lines (201 loc) • 6.75 kB
text/typescript
import { app, BrowserWindow, nativeImage, Tray } from "electron";
import path from "path";
import { registerMenuIpc } from "src/ipc/menuIPC";
import { registerServerIpc } from "src/ipc/serverIPC";
import { registerStoreIpc } from "src/ipc/storeIPC";
import { registerWallpaperIpc } from "src/ipc/wallpaperIPC";
import { registerLicenseIpc } from "src/ipc/licenseIPC";
import { registerWindowStateChangedEvents } from "src/windowState";
import { WallpaperService } from "./ipc/services/wallpaperService";
import { storeServiceInstance } from "./main";
import { registerAutoLaunchHandlers, cleanupAutoLaunchHandlers } from "./ipc/handlers/autoLaunchHandlers";
let appWindow: BrowserWindow;
let appTray: Tray | null = null; // Manage tray at module level
/**
* Clean up the tray icon
*/
export const cleanupTray = () => {
if (appTray && !appTray.isDestroyed()) {
try {
console.log("[AppWindow] Cleaning up tray icon");
appTray.destroy();
appTray = null;
} catch (error) {
console.error("[AppWindow] Error cleaning up tray:", error);
}
}
};
/**
* Configure auto-launch settings for the application
*/
function configureAutoLaunch() {
const launchAtLogin = storeServiceInstance.get("launchAtLogin") as boolean | undefined;
if (launchAtLogin !== undefined) {
app.setLoginItemSettings({
openAtLogin: launchAtLogin,
openAsHidden: true, // Start minimized
});
}
}
/**
* Create Application Window
* @returns { BrowserWindow } Application Window Instance
*/
export function createAppWindow(wallpaperService: WallpaperService): BrowserWindow {
const width = 375;
const height = 415;
// Configure auto-launch settings
configureAutoLaunch();
const windowOptions: Electron.BrowserWindowConstructorOptions = {
show: false,
frame: false,
width,
height,
resizable: false,
fullscreenable: false,
transparent: true,
skipTaskbar: true,
backgroundColor: "#1a1a1a",
...(process.platform === "darwin" && { type: "panel" }),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
preload: path.join(__dirname, "preload.js"),
},
};
// Clean up any existing tray before creating a new one
cleanupTray();
// Determine appropriate icon path
let iconPath;
if (app.isPackaged) {
// In production, use path relative to the app's resource directory
iconPath = path.join(process.resourcesPath, "assets", "icons", "16x16.png");
} else {
// In development, use path relative to the project root
iconPath = path.join(app.getAppPath(), "assets", "icons", "16x16.png");
}
// Create the tray icon using nativeImage
const trayIcon = nativeImage.createFromPath(iconPath);
appTray = new Tray(trayIcon);
try {
// Set tooltip for the tray
appTray.setToolTip("Backsplash");
} catch (error) {
console.error("Failed to create tray icon:", error);
}
// Create new window instance
appWindow = new BrowserWindow(windowOptions);
// Set visibility properties for macOS upfront
if (process.platform === "darwin") {
appWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
appWindow.setFullScreenable(false);
}
// Load the index.html of the app window.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
appWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
appWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}
const showWindow = () => {
if (!appWindow || !appTray) return;
// Get the tray position and window bounds
const trayPos = appTray.getBounds();
const windowPos = appWindow.getBounds();
let x = 0;
let y = 0;
switch (process.platform) {
case "win32":
x = Math.round(trayPos.x + trayPos.width / 2 - windowPos.width / 2);
y = Math.round(trayPos.y - height);
break;
case "darwin":
x = Math.round(trayPos.x + trayPos.width / 2 - windowPos.width / 2);
y = Math.round(trayPos.y + trayPos.height);
break;
case "freebsd":
case "linux":
case "sunos":
default:
x = screen.width - width - 10;
y = 10;
break;
}
// Position and show the window
appWindow.setPosition(x, y, false);
// Show the window (panel type handles activation policy)
appWindow.show();
// if (!app.isPackaged) {
// appWindow.webContents.openDevTools({ mode: "detach" });
// }
};
const toggleWindow = () => {
if (!appWindow) return;
if (appWindow.isVisible()) {
appWindow.hide();
} else {
showWindow();
}
};
// Register Inter Process Communication for main process
registerMainIPC(wallpaperService);
// Toggle showing the window when the tray item is clicked
if (appTray) {
appTray.on("click", toggleWindow);
}
// Close all windows when main window is closed
appWindow.on("close", () => {
cleanupTray();
// Clean up auto-launch handlers that are tied to the window
if (appWindow) {
cleanupAutoLaunchHandlers(appWindow);
}
appWindow = null;
// Don't call app.quit() here to avoid circular calls with before-quit
});
// Hide the window when it loses focus (clicked outside)
appWindow.on("blur", () => {
if (process.env.NODE_ENV === "development") {
return;
}
// Check if the window still exists and is visible before hiding
if (appWindow && !appWindow.isDestroyed() && appWindow.isVisible()) {
appWindow.hide();
}
});
return appWindow;
}
/**
* Register Inter Process Communication
*/
function registerMainIPC(wallpaperService: WallpaperService) {
/**
* Here you can assign IPC related codes for the application window
* to Communicate asynchronously from the main process to renderer processes.
*/
registerWindowStateChangedEvents(appWindow);
registerMenuIpc(appWindow);
registerWallpaperIpc(wallpaperService);
registerStoreIpc(storeServiceInstance);
registerServerIpc(storeServiceInstance);
registerLicenseIpc(storeServiceInstance);
// Make sure auto-launch handlers are registered
try {
registerAutoLaunchHandlers(appWindow);
} catch (error) {
console.error("Failed to register auto-launch handlers:", error);
}
}
// In your main process
app.on("before-quit", () => {
// Clean up resources, close connections
// Don't call window.close() here as it can cause circular calls
// The windows will be destroyed automatically when the app quits
});
// Make sure child windows are properly closed
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});