diff --git a/packages/gitbook/e2e/util.ts b/packages/gitbook/e2e/util.ts index c75eae4e83..a486b0468f 100644 --- a/packages/gitbook/e2e/util.ts +++ b/packages/gitbook/e2e/util.ts @@ -191,21 +191,20 @@ export function runTestCases(testCases: TestsCase[]) { .intercom-lightweight-app { display: none !important; } - - /* Switch image rendering to pixelated */ - img { - image-rendering: pixelated; - } `, threshold: screenshotOptions?.threshold ?? undefined, fullPage: testEntry.fullPage ?? false, beforeScreenshot: async ({ runStabilization }) => { await runStabilization(); await waitForIcons(page); + await roundImageSizes(page); if (screenshotOptions?.waitForTOCScrolling !== false) { await waitForTOCScrolling(page); } }, + afterScreenshot: async () => { + await restoreImageSizes(page); + }, }); } }); @@ -326,7 +325,14 @@ async function waitForIcons(page: Page) { function loadImage(src: string) { return new Promise((resolve, reject) => { const img = new Image(); - img.onload = () => resolve(img); + img.onload = () => { + // Wait two frames to ensure the image has been rendered + requestAnimationFrame(() => { + requestAnimationFrame(() => { + resolve(true); + }); + }); + }; img.onerror = (_error) => reject(new Error(`Failed to load image: ${src}`)); img.src = src; }); @@ -348,6 +354,70 @@ async function waitForIcons(page: Page) { }); } +/** + * Take all images, measure them and set their width and height to rounded values. + */ +async function roundImageSizes(page: Page) { + await page.waitForFunction(async () => { + const images = Array.from(document.querySelectorAll('img')); + await Promise.all( + images.map(async (img) => { + return new Promise((resolve) => { + const setDimensions = () => { + // Mark it as stabilized + img.dataset.stabilized = 'true'; + // Preserve the original width and height + img.dataset.originalWidth = img.style.width ?? ''; + img.dataset.originalHeight = img.style.height ?? ''; + const rect = img.getBoundingClientRect(); + img.style.width = `${Math.round(rect.width)}px`; + img.style.height = `${Math.round(rect.height)}px`; + resolve(); + }; + + if (img.complete) { + setDimensions(); + } else { + const cleanup = () => { + img.removeEventListener('load', handleLoad); + img.removeEventListener('error', handleError); + }; + const handleError = () => { + cleanup(); + resolve(); + }; + const handleLoad = () => { + cleanup(); + setDimensions(); + }; + img.addEventListener('load', handleLoad); + img.addEventListener('error', handleError); + } + }); + }) + ); + return true; + }); +} + +/** + * Restore images to their original size. + */ +async function restoreImageSizes(page: Page) { + await page.evaluate(() => { + const images = Array.from(document.querySelectorAll('img[data-stabilized]')); + images.forEach((img) => { + if (img instanceof HTMLImageElement) { + img.style.width = img.dataset.originalWidth ?? ''; + img.style.height = img.dataset.originalHeight ?? ''; + delete img.dataset.originalWidth; + delete img.dataset.originalHeight; + delete img.dataset.stabilized; + } + }); + }); +} + /** * Wait for TOC to be correctly scrolled into view. */ diff --git a/packages/gitbook/playwright.config.ts b/packages/gitbook/playwright.config.ts index 1f79e89b81..849d9538f6 100644 --- a/packages/gitbook/playwright.config.ts +++ b/packages/gitbook/playwright.config.ts @@ -18,5 +18,8 @@ export default defineConfig({ use: { trace: 'on-first-retry', screenshot: 'only-on-failure', + contextOptions: { + reducedMotion: 'reduce', + }, }, }); diff --git a/packages/gitbook/src/components/Search/SearchButton.tsx b/packages/gitbook/src/components/Search/SearchButton.tsx index 5244dbb60e..ab8516d059 100644 --- a/packages/gitbook/src/components/Search/SearchButton.tsx +++ b/packages/gitbook/src/components/Search/SearchButton.tsx @@ -115,9 +115,15 @@ function Shortcut() { setOperatingSystem(getOperatingSystem()); }, []); - return operatingSystem ? ( + return ( - ) : ( - ); } diff --git a/packages/gitbook/tailwind.config.ts b/packages/gitbook/tailwind.config.ts index 340978ab4c..3efc2f5105 100644 --- a/packages/gitbook/tailwind.config.ts +++ b/packages/gitbook/tailwind.config.ts @@ -297,8 +297,8 @@ const config: Config = { present: 'present .5s ease-out both', scaleIn: 'scaleIn 200ms ease', scaleOut: 'scaleOut 200ms ease', - fadeIn: 'fadeIn 200ms ease', - fadeOut: 'fadeOut 200ms ease', + fadeIn: 'fadeIn 200ms ease forwards', + fadeOut: 'fadeOut 200ms ease forwards', enterFromLeft: 'enterFromLeft 250ms ease', enterFromRight: 'enterFromRight 250ms ease', exitToLeft: 'exitToLeft 250ms ease',