chore: Initial commit of Opencord base state
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
import {
|
||||
app,
|
||||
BrowserWindow,
|
||||
Tray,
|
||||
Menu,
|
||||
Notification,
|
||||
nativeImage,
|
||||
ipcMain,
|
||||
shell,
|
||||
} from 'electron';
|
||||
import path from 'path';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let tray: Tray | null = null;
|
||||
let isQuitting = false;
|
||||
|
||||
const SERVER_URL = process.env.OPENCORD_URL || 'http://localhost:3000';
|
||||
|
||||
function createWindow(): void {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1280,
|
||||
height: 800,
|
||||
minWidth: 940,
|
||||
minHeight: 500,
|
||||
title: 'Opencord',
|
||||
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
|
||||
backgroundColor: '#313338',
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.loadURL(SERVER_URL);
|
||||
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow?.show();
|
||||
});
|
||||
|
||||
mainWindow.on('close', (event) => {
|
||||
if (!isQuitting) {
|
||||
event.preventDefault();
|
||||
mainWindow?.hide();
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
// Open external links in default browser
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
return { action: 'deny' };
|
||||
});
|
||||
}
|
||||
|
||||
function createTray(): void {
|
||||
// Create a 16x16 tray icon (blue circle for Opencord)
|
||||
const size = 16;
|
||||
const canvas = Buffer.alloc(size * size * 4);
|
||||
const cx = size / 2;
|
||||
const cy = size / 2;
|
||||
const r = size / 2 - 1;
|
||||
for (let y = 0; y < size; y++) {
|
||||
for (let x = 0; x < size; x++) {
|
||||
const idx = (y * size + x) * 4;
|
||||
const dist = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2);
|
||||
if (dist <= r) {
|
||||
canvas[idx] = 0x58; // R (blurple)
|
||||
canvas[idx + 1] = 0x65; // G
|
||||
canvas[idx + 2] = 0xf2; // B
|
||||
canvas[idx + 3] = 0xff; // A
|
||||
} else {
|
||||
canvas[idx] = 0;
|
||||
canvas[idx + 1] = 0;
|
||||
canvas[idx + 2] = 0;
|
||||
canvas[idx + 3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
const icon = nativeImage.createFromBuffer(canvas, { width: size, height: size });
|
||||
tray = new Tray(icon);
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Show Opencord',
|
||||
click: () => {
|
||||
mainWindow?.show();
|
||||
mainWindow?.focus();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Hide',
|
||||
click: () => {
|
||||
mainWindow?.hide();
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Quit',
|
||||
click: () => {
|
||||
isQuitting = true;
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
tray.setToolTip('Opencord');
|
||||
tray.setContextMenu(contextMenu);
|
||||
|
||||
tray.on('click', () => {
|
||||
if (mainWindow?.isVisible()) {
|
||||
mainWindow.hide();
|
||||
} else {
|
||||
mainWindow?.show();
|
||||
mainWindow?.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showNotification(title: string, body: string): void {
|
||||
if (Notification.isSupported()) {
|
||||
const notification = new Notification({
|
||||
title,
|
||||
body,
|
||||
silent: false,
|
||||
});
|
||||
|
||||
notification.on('click', () => {
|
||||
mainWindow?.show();
|
||||
mainWindow?.focus();
|
||||
});
|
||||
|
||||
notification.show();
|
||||
}
|
||||
}
|
||||
|
||||
function registerIpcHandlers(): void {
|
||||
ipcMain.on('show-notification', (_event, data: { title: string; body: string }) => {
|
||||
showNotification(data.title, data.body);
|
||||
});
|
||||
|
||||
ipcMain.on('set-badge-count', (_event, count: number) => {
|
||||
if (app.setBadgeCount) {
|
||||
app.setBadgeCount(count);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('minimize-window', () => {
|
||||
mainWindow?.minimize();
|
||||
});
|
||||
|
||||
ipcMain.on('maximize-window', () => {
|
||||
if (mainWindow?.isMaximized()) {
|
||||
mainWindow.unmaximize();
|
||||
} else {
|
||||
mainWindow?.maximize();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('close-window', () => {
|
||||
mainWindow?.close();
|
||||
});
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
registerIpcHandlers();
|
||||
createWindow();
|
||||
createTray();
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
} else {
|
||||
mainWindow.show();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
isQuitting = true;
|
||||
});
|
||||
Reference in New Issue
Block a user