177 lines
4.1 KiB
TypeScript
177 lines
4.1 KiB
TypeScript
/**
|
|
* System Tray Management
|
|
* Creates and manages the system tray icon and menu
|
|
*/
|
|
import { Tray, Menu, app, nativeImage } from 'electron';
|
|
import { join } from 'path';
|
|
import {
|
|
resolveMainWindow,
|
|
sendWhenWindowReady,
|
|
showMainWindow,
|
|
type EnsureMainWindow,
|
|
type MainWindowProvider,
|
|
} from './window-provider';
|
|
|
|
let tray: Tray | null = null;
|
|
let windowProvider: MainWindowProvider = () => null;
|
|
let ensureWindow: EnsureMainWindow | undefined;
|
|
|
|
/**
|
|
* Resolve the icons directory path (works in both dev and packaged mode)
|
|
*/
|
|
function getIconsDir(): string {
|
|
if (app.isPackaged) {
|
|
return join(process.resourcesPath, 'resources', 'icons');
|
|
}
|
|
return join(__dirname, '../../resources/icons');
|
|
}
|
|
|
|
/**
|
|
* Create system tray icon and menu
|
|
*/
|
|
export function createTray(mainWindow: MainWindowProvider, ensureMainWindow?: EnsureMainWindow): Tray {
|
|
windowProvider = mainWindow;
|
|
ensureWindow = ensureMainWindow;
|
|
if (tray && !tray.isDestroyed()) return tray;
|
|
// Use platform-appropriate icon for system tray
|
|
const iconsDir = getIconsDir();
|
|
let iconPath: string;
|
|
|
|
if (process.platform === 'win32') {
|
|
// Windows: use .ico for best quality in system tray
|
|
iconPath = join(iconsDir, 'icon.ico');
|
|
} else if (process.platform === 'darwin') {
|
|
// macOS: use Template.png for proper status bar icon
|
|
// The "Template" suffix tells macOS to treat it as a template image
|
|
iconPath = join(iconsDir, 'tray-icon-Template.png');
|
|
} else {
|
|
// Linux: use 32x32 PNG
|
|
iconPath = join(iconsDir, '32x32.png');
|
|
}
|
|
|
|
let icon = nativeImage.createFromPath(iconPath);
|
|
|
|
// Fallback to icon.png if platform-specific icon not found
|
|
if (icon.isEmpty()) {
|
|
icon = nativeImage.createFromPath(join(iconsDir, 'icon.png'));
|
|
// Still try to set as template for macOS
|
|
if (process.platform === 'darwin') {
|
|
icon.setTemplateImage(true);
|
|
}
|
|
}
|
|
|
|
// Note: Using "Template" suffix in filename automatically marks it as template image
|
|
// But we can also explicitly set it for safety
|
|
if (process.platform === 'darwin') {
|
|
icon.setTemplateImage(true);
|
|
}
|
|
|
|
tray = new Tray(icon);
|
|
|
|
// Set tooltip
|
|
tray.setToolTip('Makelore - AI Assistant');
|
|
|
|
const showWindow = () => {
|
|
const window = resolveMainWindow(windowProvider) ?? ensureWindow?.() ?? null;
|
|
showMainWindow(window);
|
|
return window;
|
|
};
|
|
|
|
// Create context menu
|
|
const contextMenu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'Show Makelore',
|
|
click: showWindow,
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Makelore Runtime',
|
|
enabled: false,
|
|
},
|
|
{
|
|
label: ' Running',
|
|
type: 'checkbox',
|
|
checked: true,
|
|
enabled: false,
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Quick Actions',
|
|
submenu: [
|
|
{
|
|
label: 'Open Chat',
|
|
click: () => {
|
|
sendWhenWindowReady(showWindow(), 'navigate', '/');
|
|
},
|
|
},
|
|
{
|
|
label: 'Open Settings',
|
|
click: () => {
|
|
sendWhenWindowReady(showWindow(), 'navigate', '/settings');
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Check for Updates...',
|
|
click: () => {
|
|
sendWhenWindowReady(showWindow(), 'update:check');
|
|
},
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Quit Makelore',
|
|
click: () => {
|
|
app.quit();
|
|
},
|
|
},
|
|
]);
|
|
|
|
tray.setContextMenu(contextMenu);
|
|
|
|
// Click to show window (Windows/Linux)
|
|
tray.on('click', () => {
|
|
const window = resolveMainWindow(windowProvider);
|
|
if (window?.isVisible()) {
|
|
window.hide();
|
|
} else {
|
|
showWindow();
|
|
}
|
|
});
|
|
|
|
// Double-click to show window (Windows)
|
|
tray.on('double-click', showWindow);
|
|
|
|
return tray;
|
|
}
|
|
|
|
/**
|
|
* Update tray tooltip with runtime status
|
|
*/
|
|
export function updateTrayStatus(status: string): void {
|
|
if (tray) {
|
|
tray.setToolTip(`Makelore - ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy tray icon
|
|
*/
|
|
export function destroyTray(): void {
|
|
if (tray) {
|
|
tray.destroy();
|
|
tray = null;
|
|
windowProvider = () => null;
|
|
ensureWindow = undefined;
|
|
}
|
|
}
|