From d46d7edffe89e51d6972a9d8a82834b89140328f Mon Sep 17 00:00:00 2001 From: plebeius Date: Mon, 29 Dec 2025 13:55:20 +0100 Subject: [PATCH] perf: preload theme button and background images on app startup --- scripts/generate-asset-manifest.js | 19 ++++++++++++++++++- src/app.tsx | 5 +++++ src/lib/utils/preload-utils.ts | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/lib/utils/preload-utils.ts diff --git a/scripts/generate-asset-manifest.js b/scripts/generate-asset-manifest.js index a1c051f5..5e385c91 100644 --- a/scripts/generate-asset-manifest.js +++ b/scripts/generate-asset-manifest.js @@ -18,6 +18,15 @@ const notFoundImages = readdirSync(notFoundDir) .filter((file) => /^not-found-.*\.(jpg|jpeg|gif|png)$/i.test(file)) .map((file) => `assets/not-found/${file}`); +// Scan for button images (theme-specific buttons like cross, plus, minus, help, button-fade) +const buttonsDir = join(publicDir, 'assets', 'buttons'); +const buttonImages = readdirSync(buttonsDir) + .filter((file) => /\.(jpg|jpeg|gif|png)$/i.test(file)) + .map((file) => `assets/buttons/${file}`); + +// Theme background images +const themeBackgrounds = ['assets/background-fade.png', 'assets/background-fade-blue.png']; + // Generate TypeScript file with proper formatting (matches prettier config) const formatArray = (arr) => { if (arr.length === 0) return '[]'; @@ -32,6 +41,12 @@ const output = `// Auto-generated file - do not edit manually export const BANNERS = ${formatArray(banners)} as const; export const NOT_FOUND_IMAGES = ${formatArray(notFoundImages)} as const; + +// Theme button images (cross, plus, minus, help, button-fade variants) +export const THEME_BUTTON_IMAGES = ${formatArray(buttonImages)} as const; + +// Theme background pattern images +export const THEME_BACKGROUND_IMAGES = ${formatArray(themeBackgrounds)} as const; `; // Ensure directory exists @@ -41,4 +56,6 @@ mkdirSync(generatedDir, { recursive: true }); const outputPath = join(generatedDir, 'asset-manifest.ts'); writeFileSync(outputPath, output, 'utf8'); -console.log(`✅ Generated asset manifest with ${banners.length} banners and ${notFoundImages.length} not-found images`); +console.log( + `✅ Generated asset manifest with ${banners.length} banners, ${notFoundImages.length} not-found images, ${buttonImages.length} button images, and ${themeBackgrounds.length} background images`, +); diff --git a/src/app.tsx b/src/app.tsx index 6e2f59f3..fe91b98f 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -3,6 +3,7 @@ import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom' import { useAccountComment } from '@plebbit/plebbit-react-hooks'; import { initSnow, removeSnow } from './lib/snow'; import { isAllView, isModView, isSubscriptionsView } from './lib/utils/view-utils'; +import { preloadThemeAssets } from './lib/utils/preload-utils'; import useReplyModalStore from './stores/use-reply-modal-store'; import useCreateBoardModalStore from './stores/use-create-board-modal-store'; import useSpecialThemeStore from './stores/use-special-theme-store'; @@ -31,6 +32,10 @@ import DirectoryModal from './components/directory-modal'; import DisclaimerModal from './components/disclaimer-modal'; import SettingsModal from './components/settings-modal'; +// Preload all theme assets (buttons, backgrounds) immediately on app load +// to prevent visible loading delays when switching themes +preloadThemeAssets(); + const BoardLayout = () => { const { accountCommentIndex, boardIdentifier } = useParams(); const location = useLocation(); diff --git a/src/lib/utils/preload-utils.ts b/src/lib/utils/preload-utils.ts new file mode 100644 index 00000000..5c6d80a1 --- /dev/null +++ b/src/lib/utils/preload-utils.ts @@ -0,0 +1,21 @@ +import { THEME_BUTTON_IMAGES, THEME_BACKGROUND_IMAGES } from '../../generated/asset-manifest'; + +/** + * Preloads an array of image URLs by creating Image objects. + * Images are loaded in the background and cached by the browser. + */ +const preloadImages = (imagePaths: readonly string[]): void => { + imagePaths.forEach((path) => { + const img = new Image(); + img.src = `/${path}`; + }); +}; + +/** + * Preloads all theme-related assets (buttons, backgrounds) on app startup. + * This prevents visible loading delays when switching themes. + */ +export const preloadThemeAssets = (): void => { + preloadImages(THEME_BUTTON_IMAGES); + preloadImages(THEME_BACKGROUND_IMAGES); +};