Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions ui/desktop/src/components/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { IpcRendererEvent } from 'electron';
import { Outlet, useLocation } from 'react-router-dom';
import { motion } from 'framer-motion';
import { Menu } from 'lucide-react';
Expand Down Expand Up @@ -37,6 +38,21 @@ const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) =
const chatContext = useChatContext();
const isOnPairRoute = location.pathname === '/pair';

const [isFullScreen, setIsFullScreen] = useState(false);

useEffect(() => {
if (!safeIsMacOS) return;
window.electron
.getIsFullScreen()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the fullscreen IPC method to renderer test mocks

When AppLayout mounts under the existing mocked macOS renderer environment, this unconditional call now requires window.electron.getIsFullScreen, but the current Electron mocks used by the App tests/global renderer setup only define platform, getSetting, setSetting, etc. That makes affected App/AppLayout tests throw TypeError: window.electron.getIsFullScreen is not a function as soon as the effect runs in CI unless the new preload API is added to those mocks or this call is guarded.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4daffad — added getIsFullScreen / on / off to the renderer test mock in src/test/setup.ts. All 346 tests still pass locally.

.then(setIsFullScreen)
.catch(() => {});
const handler = (_event: IpcRendererEvent, ...args: unknown[]) => {
setIsFullScreen(Boolean(args[0]));
};
window.electron.on('fullscreen-change', handler);
return () => window.electron.off('fullscreen-change', handler);
}, [safeIsMacOS]);

const {
isNavExpanded,
setIsNavExpanded,
Expand Down Expand Up @@ -146,8 +162,9 @@ const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) =
};
}, [isPushTopNav]);

const headerPadding = safeIsMacOS ? 'pl-[96px]' : 'pl-4';
const headerTop = safeIsMacOS ? 'top-[15px]' : 'top-[11px]';
const needsTrafficLightInset = safeIsMacOS && !isFullScreen;
const headerPadding = needsTrafficLightInset ? 'pl-[96px]' : 'pl-4';
const headerTop = needsTrafficLightInset ? 'top-[15px]' : 'top-[11px]';

// Determine flex direction based on navigation position (for push mode)
const getLayoutClass = () => {
Expand Down
13 changes: 13 additions & 0 deletions ui/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,14 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => {
}
});

const broadcastFullScreenState = () => {
if (!mainWindow.isDestroyed()) {
mainWindow.webContents.send('fullscreen-change', mainWindow.isFullScreen());
}
};
mainWindow.on('enter-full-screen', broadcastFullScreenState);
mainWindow.on('leave-full-screen', broadcastFullScreenState);

// Handle mouse back button (button 3)
// Use type assertion for non-standard Electron event
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -1793,6 +1801,11 @@ ipcMain.handle('is-any-window-focused', () => {
return BrowserWindow.getFocusedWindow() !== null;
});

ipcMain.handle('get-is-fullscreen', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
return win?.isFullScreen() ?? false;
});

// Add file/directory selection handler
ipcMain.handle('select-file-or-directory', async (_event, defaultPath?: string) => {
const dialogOptions: OpenDialogOptions = {
Expand Down
2 changes: 2 additions & 0 deletions ui/desktop/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ type ElectronAPI = {
getSpellcheckState: () => Promise<boolean>;
openNotificationsSettings: () => Promise<boolean>;
isAnyWindowFocused: () => Promise<boolean>;
getIsFullScreen: () => Promise<boolean>;
onMouseBackButtonClicked: (callback: () => void) => void;
offMouseBackButtonClicked: (callback: () => void) => void;
on: (
Expand Down Expand Up @@ -271,6 +272,7 @@ const electronAPI: ElectronAPI = {
getSpellcheckState: () => ipcRenderer.invoke('get-spellcheck-state'),
openNotificationsSettings: () => ipcRenderer.invoke('open-notifications-settings'),
isAnyWindowFocused: () => ipcRenderer.invoke('is-any-window-focused'),
getIsFullScreen: () => ipcRenderer.invoke('get-is-fullscreen'),
onMouseBackButtonClicked: (callback: () => void) => {
// Wrapper that ignores the event parameter.
const wrappedCallback = (_event: Electron.IpcRendererEvent) => callback();
Expand Down
3 changes: 3 additions & 0 deletions ui/desktop/src/test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,8 @@ Object.defineProperty(window, 'electron', {
return Promise.resolve();
}),
showMessageBox: vi.fn(() => Promise.resolve({ response: 0 })),
getIsFullScreen: vi.fn(() => Promise.resolve(false)),
on: vi.fn(),
off: vi.fn(),
},
});