fix(browser): preserve chat focus during agent browsing

This commit is contained in:
2026-09-27 15:23:23 +08:00
parent 220e89992b
commit a52e747a00
10 changed files with 323 additions and 16 deletions

View File

@@ -32,6 +32,7 @@ const electronMocks = vi.hoisted(() => {
webContents: unknown;
setBounds: ReturnType<typeof vi.fn>;
setVisible: ReturnType<typeof vi.fn>;
getVisible: ReturnType<typeof vi.fn>;
}> = [];
const permissionHandler = vi.fn();
const permissionCheckHandler = vi.fn();
@@ -66,11 +67,13 @@ const electronMocks = vi.hoisted(() => {
isDevToolsOpened: vi.fn().mockReturnValue(false),
reload: vi.fn(),
executeJavaScript: vi.fn().mockResolvedValue({ ok: true }),
capturePage: vi.fn().mockResolvedValue({}),
close: vi.fn(),
setWindowOpenHandler: vi.fn(),
});
readonly setBounds = vi.fn();
readonly setVisible = vi.fn();
readonly getVisible = vi.fn().mockReturnValue(true);
constructor(readonly options: unknown) {
nativeViews.push(this);
@@ -102,6 +105,29 @@ describe('ElectronAgentBrowserAdapter', () => {
electronMocks.browserSession.removeAllListeners();
});
it.each([false, true])('captures a page without presenting a hidden native view (visible=%s)', async (visible) => {
const adapter = new ElectronAgentBrowserAdapter({} as never);
const view = adapter.createView('browser-screenshot');
const native = electronMocks.nativeViews[0];
native.getVisible.mockReturnValue(visible);
const contents = native.webContents as InstanceType<typeof electronMocks.MockWebContentsView>['webContents'];
const params = { format: 'jpeg', quality: 80, clip: { x: 0, y: 0, width: 320, height: 200, scale: 1 } };
contents.debugger.sendCommand.mockResolvedValue({ data: 'screenshot' });
await expect(view.webContents.debugger.sendCommand('Page.captureScreenshot', params, 'child-session'))
.resolves.toEqual({ data: 'screenshot' });
if (visible) {
expect(contents.capturePage).not.toHaveBeenCalled();
} else {
expect(contents.capturePage).toHaveBeenCalledExactlyOnceWith(undefined, { stayHidden: true, stayAwake: false });
expect(contents.capturePage.mock.invocationCallOrder[0]).toBeLessThan(contents.debugger.sendCommand.mock.invocationCallOrder[0]);
}
expect(contents.debugger.sendCommand).toHaveBeenCalledWith('Page.captureScreenshot', params, 'child-session');
expect(native.setVisible).not.toHaveBeenCalled();
contents.capturePage.mockClear();
await view.webContents.debugger.sendCommand('Runtime.evaluate', { expression: '1' });
expect(contents.capturePage).not.toHaveBeenCalled();
});
it('creates an isolated sandboxed WebContentsView and mounts it in Main', async () => {
const addChildView = vi.fn();
const removeChildView = vi.fn();