diff --git a/src/main/main.ts b/src/main/main.ts index e326b9e..a6d38ac 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -41,6 +41,7 @@ protocol.registerSchemesAsPrivileged([ ]); function createWindow(): void { + const isMac = process.platform === 'darwin'; mainWindow = new BrowserWindow({ width: 1400, height: 900, @@ -48,7 +49,17 @@ function createWindow(): void { minHeight: 600, title: 'Blogging Desktop Server', backgroundColor: '#1e1e1e', // VS Code dark background - titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default', + titleBarStyle: isMac ? 'hiddenInset' : 'hidden', + ...(isMac + ? {} + : { + titleBarOverlay: { + color: '#252526', + symbolColor: '#cccccc', + height: 34, + }, + autoHideMenuBar: true, + }), webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: false, diff --git a/tests/engine/mainStartup.test.ts b/tests/engine/mainStartup.test.ts index 59f4a9e..0d43342 100644 --- a/tests/engine/mainStartup.test.ts +++ b/tests/engine/mainStartup.test.ts @@ -6,6 +6,126 @@ describe('main bootstrap preview behavior', () => { vi.resetModules(); }); + it.each(['win32', 'linux'])('uses compact unified window decorations on %s', async (platform) => { + const originalPlatform = process.platform; + Object.defineProperty(process, 'platform', { value: platform }); + + const mockApp = { + name: 'bDS', + whenReady: vi.fn(() => Promise.resolve()), + on: vi.fn(), + quit: vi.fn(), + }; + + const browserWindowCalls: any[] = []; + class BrowserWindowMock { + static getAllWindows = vi.fn(() => [{ id: 1 }]); + + loadURL = vi.fn(); + loadFile = vi.fn(); + on = vi.fn(); + isDestroyed = vi.fn(() => false); + webContents = { + on: vi.fn(), + send: vi.fn(), + openDevTools: vi.fn(), + toggleDevTools: vi.fn(), + }; + + constructor(options: any) { + browserWindowCalls.push(options); + } + } + + vi.doMock('electron', () => ({ + app: mockApp, + BrowserWindow: BrowserWindowMock, + Menu: { + buildFromTemplate: vi.fn(() => ({})), + setApplicationMenu: vi.fn(), + }, + ipcMain: { + on: vi.fn(), + handle: vi.fn(), + removeHandler: vi.fn(), + }, + protocol: { + registerSchemesAsPrivileged: vi.fn(), + handle: vi.fn(), + }, + net: { + fetch: vi.fn(), + }, + shell: { + openExternal: vi.fn(), + openPath: vi.fn(), + }, + })); + + class MockPreviewServer { + start = vi.fn().mockResolvedValue(4123); + stop = vi.fn().mockResolvedValue(undefined); + getBaseUrl = vi.fn(() => 'http://127.0.0.1:4123'); + } + + vi.doMock('../../src/main/engine/PreviewServer', () => ({ + PreviewServer: MockPreviewServer, + })); + + vi.doMock('../../src/main/database', () => ({ + getDatabase: vi.fn(() => ({ + initializeLocal: vi.fn().mockResolvedValue(undefined), + close: vi.fn().mockResolvedValue(undefined), + getLocal: vi.fn(() => ({ + select: vi.fn(() => ({ + from: vi.fn(() => ({ + where: vi.fn(() => ({ + get: vi.fn().mockResolvedValue(null), + })), + })), + })), + })), + getDataPaths: vi.fn(() => ({ database: '/tmp/mock.db' })), + })), + })); + + vi.doMock('../../src/main/ipc', () => ({ + registerIpcHandlers: vi.fn(), + registerChatHandlers: vi.fn(), + initializeChatHandlers: vi.fn(), + cleanupChatHandlers: vi.fn().mockResolvedValue(undefined), + })); + + vi.doMock('../../src/main/database/schema', () => ({ + media: {}, + })); + + vi.doMock('drizzle-orm', () => ({ + eq: vi.fn(), + })); + + vi.doMock('../../src/main/engine/MediaEngine', () => ({ + getMediaEngine: vi.fn(() => ({ + getThumbnailPaths: vi.fn().mockResolvedValue({ small: null }), + })), + })); + + await import('../../src/main/main'); + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(browserWindowCalls[0]).toEqual(expect.objectContaining({ + titleBarStyle: 'hidden', + titleBarOverlay: { + color: '#252526', + symbolColor: '#cccccc', + height: 34, + }, + autoHideMenuBar: true, + })); + + Object.defineProperty(process, 'platform', { value: originalPlatform }); + }); + it('starts preview server during app startup', async () => { const mockApp = { name: 'bDS',