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
2 changes: 2 additions & 0 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
);
await toggleDevToolsPanel(
config,
showErrorDetails,
() => setShowErrorDetails((prev) => !prev),
() => setShowErrorDetails(true),
);
Expand Down Expand Up @@ -1665,6 +1666,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
tabFocusTimeoutRef,
showTransientMessage,
settings.merged.general.devtools,
showErrorDetails,
],
);

Expand Down
36 changes: 24 additions & 12 deletions packages/cli/src/utils/devtoolsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,19 @@ describe('devtoolsService', () => {
});

describe('toggleDevToolsPanel', () => {
it('calls toggle when browser opens successfully', async () => {
it('calls toggle (to close) when already open', async () => {
const config = createMockConfig();
const toggle = vi.fn();
const setOpen = vi.fn();

const promise = toggleDevToolsPanel(config, true, toggle, setOpen);
await promise;

expect(toggle).toHaveBeenCalledTimes(1);
expect(setOpen).not.toHaveBeenCalled();
});

it('does NOT call toggle or setOpen when browser opens successfully', async () => {
const config = createMockConfig();
const toggle = vi.fn();
const setOpen = vi.fn();
Expand All @@ -447,18 +459,18 @@ describe('devtoolsService', () => {
mockDevToolsInstance.start.mockResolvedValue('http://127.0.0.1:25417');
mockDevToolsInstance.getPort.mockReturnValue(25417);

const promise = toggleDevToolsPanel(config, toggle, setOpen);
const promise = toggleDevToolsPanel(config, false, toggle, setOpen);

await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateError();

await promise;

expect(toggle).toHaveBeenCalledTimes(1);
expect(toggle).not.toHaveBeenCalled();
expect(setOpen).not.toHaveBeenCalled();
});

it('calls toggle when browser fails to open', async () => {
it('calls setOpen when browser fails to open', async () => {
const config = createMockConfig();
const toggle = vi.fn();
const setOpen = vi.fn();
Expand All @@ -468,18 +480,18 @@ describe('devtoolsService', () => {
mockDevToolsInstance.start.mockResolvedValue('http://127.0.0.1:25417');
mockDevToolsInstance.getPort.mockReturnValue(25417);

const promise = toggleDevToolsPanel(config, toggle, setOpen);
const promise = toggleDevToolsPanel(config, false, toggle, setOpen);

await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateError();

await promise;

expect(toggle).toHaveBeenCalledTimes(1);
expect(setOpen).not.toHaveBeenCalled();
expect(toggle).not.toHaveBeenCalled();
expect(setOpen).toHaveBeenCalledTimes(1);
});

it('calls toggle when shouldLaunchBrowser returns false', async () => {
it('calls setOpen when shouldLaunchBrowser returns false', async () => {
const config = createMockConfig();
const toggle = vi.fn();
const setOpen = vi.fn();
Expand All @@ -488,15 +500,15 @@ describe('devtoolsService', () => {
mockDevToolsInstance.start.mockResolvedValue('http://127.0.0.1:25417');
mockDevToolsInstance.getPort.mockReturnValue(25417);

const promise = toggleDevToolsPanel(config, toggle, setOpen);
const promise = toggleDevToolsPanel(config, false, toggle, setOpen);

await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateError();

await promise;

expect(toggle).toHaveBeenCalledTimes(1);
expect(setOpen).not.toHaveBeenCalled();
expect(toggle).not.toHaveBeenCalled();
expect(setOpen).toHaveBeenCalledTimes(1);
});

it('calls setOpen when DevTools server fails to start', async () => {
Expand All @@ -506,7 +518,7 @@ describe('devtoolsService', () => {

mockDevToolsInstance.start.mockRejectedValue(new Error('fail'));

const promise = toggleDevToolsPanel(config, toggle, setOpen);
const promise = toggleDevToolsPanel(config, false, toggle, setOpen);

await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateError();
Expand Down
19 changes: 16 additions & 3 deletions packages/cli/src/utils/devtoolsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,24 @@ async function startDevToolsServerImpl(config: Config): Promise<string> {

/**
* Handles the F12 key toggle for the DevTools panel.
* Starts the DevTools server, attempts to open the browser,
* and always calls the toggle callback regardless of the outcome.
* Starts the DevTools server, attempts to open the browser.
* If the panel is already open, it closes it.
* If the panel is closed:
* - Attempts to open the browser.
* - If browser opening is successful, the panel remains closed.
* - If browser opening fails or is not possible, the panel is opened.
*/
export async function toggleDevToolsPanel(
config: Config,
isOpen: boolean,
toggle: () => void,
setOpen: () => void,
): Promise<void> {
if (isOpen) {
toggle();
return;
}

try {
const { openBrowserSecurely, shouldLaunchBrowser } = await import(
'@google/gemini-cli-core'
Expand All @@ -228,11 +238,14 @@ export async function toggleDevToolsPanel(
if (shouldLaunchBrowser()) {
try {
await openBrowserSecurely(url);
// Browser opened successfully, don't open drawer.
Copy link
Contributor

Choose a reason for hiding this comment

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

glad to see the draw not open.

return;
} catch (e) {
debugLogger.warn('Failed to open browser securely:', e);
}
}
toggle();
// If we can't launch browser or it failed, open drawer.
setOpen();
} catch (e) {
setOpen();
debugLogger.error('Failed to start DevTools server:', e);
Expand Down
Loading