Newer
Older
TelosDB / src / backend / electron-main.js
import { app, BrowserWindow, Menu, nativeImage, Tray } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
import { CONFIG, TRAY_MENU_LABELS, TRAY_TOOLTIP, WINDOW_TITLE } from './config.js';
import { Logger } from './logger.js';
import { startMcpServer } from './mcp-server.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

let mainWindow;
let tray;

/**
 * システムトレイを作成・初期化
 * @throws {Error} アイコン読み込みに失敗した場合
 */
function createTray() {
  try {
    const isWin = process.platform === 'win32';
    const iconName = isWin ? 'icon.ico' : 'icon.png';
    const iconPath = path.join(__dirname, iconName);
    
    Logger.debug('Creating tray icon', { iconPath });
    const icon = nativeImage.createFromPath(iconPath);
    
    if (icon.isEmpty()) {
      Logger.warn('Tray icon is empty');
    }

    tray = new Tray(icon);
    const contextMenu = Menu.buildFromTemplate([
      {
        label: TRAY_MENU_LABELS.open,
        click: () => {
          Logger.debug('Opening main window from tray');
          mainWindow.show();
        },
      },
      { type: 'separator' },
      {
        label: TRAY_MENU_LABELS.quit,
        click: () => {
          Logger.info('Quit application from tray');
          app.isQuitting = true;
          app.quit();
        },
      },
    ]);

    tray.setToolTip(TRAY_TOOLTIP);
    tray.setContextMenu(contextMenu);
    
    tray.on('double-click', () => {
      Logger.debug('Tray double-click');
      mainWindow.show();
    });

    Logger.info('Tray created successfully');
  } catch (error) {
    Logger.error('Failed to create tray', error);
    throw error;
  }
}

/**
 * メインウィンドウを作成・初期化
 * @throws {Error} ウィンドウ作成に失敗した場合
 */
function createWindow() {
  try {
    Logger.debug('Creating main window', CONFIG.window);
    
    mainWindow = new BrowserWindow({
      width: CONFIG.window.width,
      height: CONFIG.window.height,
      show: true,
      webPreferences: {
        nodeIntegration: true,
        contextIsolation: false,
      },
    });

    const htmlPath = path.join(__dirname, 'index.html');
    mainWindow.loadFile(htmlPath);
    mainWindow.setTitle(WINDOW_TITLE);

    mainWindow.on('close', (event) => {
      if (!app.isQuitting) {
        Logger.debug('Minimizing to tray instead of closing');
        event.preventDefault();
        mainWindow.hide();
      }
      return false;
    });

    mainWindow.on('closed', () => {
      Logger.debug('Main window closed');
      mainWindow = null;
    });

    Logger.info('Main window created successfully');
  } catch (error) {
    Logger.error('Failed to create main window', error);
    throw error;
  }
}

/**
 * アプリケーション初期化
 */
app.whenReady().then(async () => {
  try {
    Logger.info('Electron app is ready');
    createWindow();
    createTray();

    const mcpPort = CONFIG.mcp.defaultPort;
    await startMcpServer(mcpPort);
    Logger.info(`MCP server started on port ${mcpPort}`);
  } catch (error) {
    Logger.error('Failed to initialize application', error);
    app.quit();
  }
});

/**
 * 全ウィンドウが閉じても終了しない(常駐アプリケーション)
 */
app.on('window-all-closed', () => {
  Logger.debug('All windows closed, keeping app alive');
  // 常駐のため何もしない
});

/**
 * 終了前処理
 */
app.on('before-quit', () => {
  Logger.info('Quitting application');
  app.isQuitting = true;
});