fix(browser): wait for panel surface before capture
This commit is contained in:
@@ -339,6 +339,25 @@ const WebviewBrowser: React.FC<BrowserPaneProps> = ({ initialUrl, directory, tab
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'browser.capture') {
|
if (action === 'browser.capture') {
|
||||||
|
// A user may close the panel after browser.open. Chromium then removes
|
||||||
|
// the zero-width webview's composited surface and capturePage() fails
|
||||||
|
// with UnknownVizError. Reveal this existing browser tab again and let
|
||||||
|
// the layout paint before asking Electron for the image.
|
||||||
|
useUIStore.getState().openContextBrowser(directory, webview.getURL());
|
||||||
|
const surfaceDeadline = Date.now() + 1_200;
|
||||||
|
let previousWidth = 0;
|
||||||
|
let stableSamples = 0;
|
||||||
|
while (stableSamples < 2 && Date.now() < surfaceDeadline) {
|
||||||
|
const width = webview.getBoundingClientRect().width;
|
||||||
|
stableSamples = width >= 2 && Math.abs(width - previousWidth) < 0.5
|
||||||
|
? stableSamples + 1
|
||||||
|
: 0;
|
||||||
|
previousWidth = width;
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||||
|
}
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
||||||
|
});
|
||||||
// Wait for a settled page first: a screenshot of a half-painted layout is
|
// Wait for a settled page first: a screenshot of a half-painted layout is
|
||||||
// worse than none, because it looks like a finished one.
|
// worse than none, because it looks like a finished one.
|
||||||
await waitForIdle();
|
await waitForIdle();
|
||||||
@@ -450,7 +469,7 @@ const WebviewBrowser: React.FC<BrowserPaneProps> = ({ initialUrl, directory, tab
|
|||||||
await waitForIdle();
|
await waitForIdle();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, [annotationHost, loadUrl, waitForIdle]);
|
}, [annotationHost, directory, loadUrl, waitForIdle]);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() => registerBrowserController({ run: runControlAction }),
|
() => registerBrowserController({ run: runControlAction }),
|
||||||
|
|||||||
+7
@@ -15,6 +15,7 @@ import { useUIStore } from '@/stores/useUIStore';
|
|||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const contextPanelSource = readFileSync(join(__dirname, '..', 'ContextPanel.tsx'), 'utf-8');
|
const contextPanelSource = readFileSync(join(__dirname, '..', 'ContextPanel.tsx'), 'utf-8');
|
||||||
|
const browserPaneSource = readFileSync(join(__dirname, '..', '..', 'browser', 'BrowserPane.tsx'), 'utf-8');
|
||||||
const DIRECTORY = '/path/to/repository';
|
const DIRECTORY = '/path/to/repository';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -40,4 +41,10 @@ describe('issue #3175 browser capture while the context panel is closed', () =>
|
|||||||
expect(panel.tabs[0]?.mode).toBe('browser');
|
expect(panel.tabs[0]?.mode).toBe('browser');
|
||||||
expect(panel.tabs[0]?.targetPath).toBe('https://example.com');
|
expect(panel.tabs[0]?.targetPath).toBe('https://example.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('reveals the browser again if it was closed before capture', () => {
|
||||||
|
expect(browserPaneSource).toContain(
|
||||||
|
'openContextBrowser(directory, webview.getURL())',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user