diff --git a/src/main/classes/windows/PhoneIslandWindow.ts b/src/main/classes/windows/PhoneIslandWindow.ts index 9e72ea07..634ce53b 100644 --- a/src/main/classes/windows/PhoneIslandWindow.ts +++ b/src/main/classes/windows/PhoneIslandWindow.ts @@ -1,5 +1,6 @@ import { PAGES } from '@shared/types' import { BaseWindow } from './BaseWindow' +import { ipcMain } from 'electron' export class PhoneIslandWindow extends BaseWindow { @@ -30,16 +31,48 @@ export class PhoneIslandWindow extends BaseWindow { thickFrame: false, trafficLightPosition: { x: 0, y: 0 }, webPreferences: { - nodeIntegration: true + nodeIntegration: true, + backgroundThrottling: false } }) - // Set window level to ensure it appears above fullscreen applications - // This works across all platforms (macOS, Windows, Linux) this.addOnBuildListener(() => { const window = this.getWindow() if (window) { + // Set window level to ensure it appears above fullscreen applications window.setAlwaysOnTop(true, 'screen-saver') + + // Override console methods in the renderer to serialize objects before logging. + // Without this, Chromium's console-message event converts objects to "[object Object]". + window.webContents.on('did-finish-load', () => { + window.webContents.executeJavaScript(` + (function() { + const serialize = (arg) => { + if (arg === null || arg === undefined) return String(arg); + if (typeof arg === 'object') { + try { return JSON.stringify(arg); } catch(e) { return String(arg); } + } + return String(arg); + }; + ['log', 'info', 'warn', 'error', 'debug'].forEach((method) => { + const original = console[method].bind(console); + console[method] = (...args) => { + original(...args.map(serialize)); + }; + }); + })(); + `) + }) + + // Forward all console messages from PhoneIsland renderer to main process log file + // Uses 'phone-island-log' channel which always writes to file (not gated by isDev) + window.webContents.on('console-message', (_event, level, message, line, sourceId) => { + const levelMap = ['DEBUG', 'INFO', 'WARNING', 'ERROR'] + const levelStr = levelMap[level] || 'INFO' + const source = sourceId ? sourceId.split('/').pop() : 'unknown' + const timestamp = new Date().toISOString().replace('T', ' ').replace('Z', '') + ipcMain.emit('phone-island-log', {}, `${timestamp} [PhoneIsland] [${levelStr}] ${message} (${source}:${line})`) + }) } }) } diff --git a/src/main/lib/windowConstructor.ts b/src/main/lib/windowConstructor.ts index f974efec..345cd408 100644 --- a/src/main/lib/windowConstructor.ts +++ b/src/main/lib/windowConstructor.ts @@ -31,7 +31,8 @@ export function createWindow( preload: join(__dirname, '../preload/index.js'), sandbox: false, contextIsolation: false, - nodeIntegration: true + nodeIntegration: true, + ...config.webPreferences, }, hiddenInMissionControl: false, }) diff --git a/src/main/main.ts b/src/main/main.ts index 33ac0a59..eee61fc8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -201,6 +201,11 @@ function startLogger() { if (message && isDev()) logOnFile(message) }) + // Always log PhoneIsland renderer messages (for audio diagnostics in production) + ipcMain.on('phone-island-log', (_e, message) => { + if (message) + logOnFile(message) + }) function deleteLogFile() { if (fs.existsSync(logFilePath)) {