diff --git a/.gitignore b/.gitignore index 0242b2f..69ad8a3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ cypress/videos .idea .claude +.serena \ No newline at end of file diff --git a/components/shared/scroll-icons.tsx b/components/shared/scroll-icons.tsx index 533528b..d910b66 100644 --- a/components/shared/scroll-icons.tsx +++ b/components/shared/scroll-icons.tsx @@ -49,35 +49,45 @@ function ScrollIcons() {
Trusted by teams and individuals from
- {logos.map((item, index) => ( -
- {item.name} { + // Extract logo name from path (e.g., '/images/google.svg' -> 'google') + const logoName = item.logo.replace('/images/', '').replace('.svg', ''); + + return ( +
-
- ))} + className={'logo opacity-100'} + > + + + +
+ ); + })}
diff --git a/dev.env b/dev.env new file mode 100644 index 0000000..f0df815 --- /dev/null +++ b/dev.env @@ -0,0 +1,4 @@ +ENVIRONMENT=test +NEXT_PUBLIC_API_KEY=REQUIRED +NEXT_PUBLIC_API_BASE_URL=http://localhost:8000 +NEXT_PUBLIC_SITE_BASE_URL=http://localhost:3000 diff --git a/next.config.mjs b/next.config.mjs index bc82ec7..48c1501 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -121,6 +121,16 @@ const nextConfig = { source: '/(.*)', headers: securityHeaders, }, + { + // Cache SVG sprite sheet for 1 year (immutable) + source: '/images/company-logos-sprite.svg', + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + }, { source: '/:all*(docx)', headers: [ diff --git a/package.json b/package.json index 7d45e26..d6009e1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "format": "prettier \"**/*.{css,js,json,jsx,ts,tsx}\"", "test": "NODE_ENV=test cypress open", "test:headless": "NODE_ENV=test cypress run --browser chrome", - "generate-sitemap": "node scripts/generate-sitemap.js" + "generate-sitemap": "node scripts/generate-sitemap.js", + "generate-sprite": "node scripts/generate-sprite.js" }, "devDependencies": { "@types/lodash-es": "^4.17.8", diff --git a/public/images/company-logos-sprite.svg b/public/images/company-logos-sprite.svg new file mode 100644 index 0000000..3b76f09 --- /dev/null +++ b/public/images/company-logos-sprite.svg @@ -0,0 +1,709 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/generate-sprite.js b/scripts/generate-sprite.js new file mode 100644 index 0000000..8c70d01 --- /dev/null +++ b/scripts/generate-sprite.js @@ -0,0 +1,97 @@ +const fs = require('fs'); +const path = require('path'); + +const imagesDir = path.join(__dirname, '../public/images'); +const outputFile = path.join(__dirname, '../public/images/company-logos-sprite.svg'); +const pagesConfigPath = path.join(__dirname, '../lib/config/pages.tsx'); + +const extractLogoFilesFromConfig = () => { + if (!fs.existsSync(pagesConfigPath)) { + console.warn(`Warning: pages config not found at ${pagesConfigPath}`); + return []; + } + + const configContent = fs.readFileSync(pagesConfigPath, 'utf8'); + const developersMatch = configContent.match(/developers:\s*{[\s\S]*?logos:\s*\[([\s\S]*?)\]\s*,\s*}/); + + if (!developersMatch) { + console.warn('Warning: Unable to locate developers logos in config; falling back to empty list.'); + return []; + } + + const logosSection = developersMatch[1]; + const matches = logosSection.matchAll(/logo:\s*['"]([^'"]+)['"]/g); + + const seen = new Set(); + const logos = []; + + for (const match of matches) { + const logoPath = match[1]; + if (!logoPath.startsWith('/images/')) { + continue; + } + + const fileName = path.basename(logoPath); + + if (!fileName.toLowerCase().endsWith('.svg') || seen.has(fileName)) { + continue; + } + + seen.add(fileName); + logos.push(fileName); + } + + return logos; +}; + +// Derive the logo list from the single source of truth in pages config +const logoFiles = extractLogoFilesFromConfig(); + +if (!logoFiles.length) { + console.warn('No logos discovered from config; sprite generation will not output any symbols.'); +} + +console.log('Generating SVG sprite sheet...'); + +let symbols = []; + +logoFiles.forEach((file) => { + const filePath = path.join(imagesDir, file); + + if (!fs.existsSync(filePath)) { + console.warn(`Warning: ${file} not found, skipping...`); + return; + } + + const svgContent = fs.readFileSync(filePath, 'utf8'); + + // Extract viewBox and paths from the SVG + const viewBoxMatch = svgContent.match(/viewBox="([^"]*)"/); + const viewBox = viewBoxMatch ? viewBoxMatch[1] : '0 0 100 100'; + + // Extract everything between and tags + const contentMatch = svgContent.match(/]*>([\s\S]*)<\/svg>/); + const content = contentMatch ? contentMatch[1].trim() : ''; + + // Get the logo name without extension + const logoName = file.replace('.svg', ''); + + // Create a symbol for this logo + symbols.push(` +${content} + `); + + console.log(`āœ“ Added ${logoName}`); +}); + +// Create the sprite sheet SVG +const spriteContent = ` +${symbols.join('\n')} +`; + +// Write the sprite file +fs.writeFileSync(outputFile, spriteContent); + +console.log(`\nāœ… Sprite sheet generated successfully!`); +console.log(`šŸ“ Location: ${outputFile}`); +console.log(`šŸ“Š Total logos: ${symbols.length}`);