diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 392205a..8376b5f 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -37,6 +37,9 @@ jobs: - name: Generate SVG variants run: node scripts/generate-svg-variants.js + - name: Generate PWA cache list + run: node scripts/generate-pwa-cache-list.js + - name: Prepare Pages artifact run: | mkdir -p ./gh-pages-artifact/build @@ -50,6 +53,7 @@ jobs: cp -r public/data ./gh-pages-artifact/ cp -r public/logos ./gh-pages-artifact/ cp -r public/logos_gen ./gh-pages-artifact/ + cp public/pwa-files-to-cache.json ./gh-pages-artifact/ if [ -f public/CNAME ]; then cp public/CNAME ./gh-pages-artifact/CNAME fi diff --git a/.gitignore b/.gitignore index fde3786..3c95cd1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ yarn.lock # Build output public/build/ +public/pwa-files-to-cache.json # macOS specific files .DS_Store diff --git a/Makefile b/Makefile index 1720ee6..5115b14 100644 --- a/Makefile +++ b/Makefile @@ -6,21 +6,19 @@ CONTAINER_NAME = slogos-dev DEV_PORT = 5006 # Main targets -.PHONY: all build start stop restart logs clean scan-logos dev rebuild favicon deps-favicon build-with-favicons sync-packages convert-colors generate-svg-variants +.PHONY: all build start stop restart logs clean scan-logos dev rebuild favicon build-with-favicons generate-svg-variants pwa-cache-list run update-lock all: build start -# Development mode with hot reloading -dev: +dev: pwa-cache-list $(DOCKER_COMPOSE) -f compose.dev.yml up --build -# Build the Docker container build: @echo "Building the Logo Gallery container..." $(DOCKER_COMPOSE) -f compose.dev.yml build # Start the application in the background -start: +start: pwa-cache-list @echo "Starting Logo Gallery application on port $(DEV_PORT)..." $(DOCKER_COMPOSE) -f compose.dev.yml up -d @echo "Application is running at http://localhost:$(DEV_PORT)" @@ -81,3 +79,9 @@ generate-svg-variants: @echo "Generating SVG variants with color sets..." $(DOCKER_COMPOSE) -f compose.dev.yml run --rm $(CONTAINER_NAME) node scripts/generate-svg-variants.js @echo "SVG variants have been generated" + +# Generate PWA cache list +pwa-cache-list: + @echo "Generating PWA cache list..." + $(DOCKER_COMPOSE) -f compose.dev.yml run --rm $(CONTAINER_NAME) npm run pwa-cache-list + diff --git a/ToDo.md b/ToDo.md index 1842fc2..45359db 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1,7 +1,7 @@ -[] Improove: To the tiny card add the color chooser. It should be one circle that display current color, on click it should open color picker. -[] Improove: In the preview page, add full header. -[] Improove: Split header into two parts: static top and dynamic bottom. -[] Improove: In the preview page, add possibility select custom color for each target. -[] Strategy: Add differents base/collections of images: Flags, -[] Strategy: WebApp, PWA -[] Improove: Add to filter Image variants - just logo, line logo, square logo +[ ] Improove: To the tiny card add the color chooser. It should be one circle that display current color, on click it should open color picker. +[ ] Improove: In the preview page, add full header. +[ ] Improove: Split header into two parts: static top and dynamic bottom. +[ ] Improove: In the preview page, add possibility select custom color for each target. +[ ] Strategy: Add differents base/collections of images: Flags, +[v] Strategy: WebApp, PWA +[ ] Improove: Add to filter Image variants - just logo, line logo, square logo diff --git a/package.json b/package.json index b166d2e..94860b7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "scan-logos": "node scripts/scanLogos.js", "generate-favicons": "node scripts/generateFavicons.js", "generate-variants": "node scripts/generate-svg-variants.js", - "variants": "npm run generate-variants" + "variants": "npm run generate-variants", + "pwa-cache-list": "node scripts/generate-pwa-cache-list.js" }, "devDependencies": { "@rollup/plugin-commonjs": "^17.0.0", diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000..15f7b7d Binary files /dev/null and b/public/icon-192.png differ diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..88452e2 Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/index.html b/public/index.html index 6a098e9..376dd29 100644 --- a/public/index.html +++ b/public/index.html @@ -12,6 +12,20 @@ + + + + + +
diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..24e51bd --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "Logo Gallery", + "description": "A collection of company and brand logos accessible via GitHub Pages", + "start_url": ".", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#ffffff", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..f2d370f --- /dev/null +++ b/public/sw.js @@ -0,0 +1,54 @@ +// Basic service worker for caching static assets +// List of all files in the public folder to cache +const CACHE_NAME = 'pwa-cache-v5'; + +self.addEventListener('install', event => { + event.waitUntil( + fetch('/pwa-files-to-cache.json') + .then(response => response.json()) + .then(files => { + return caches.open(CACHE_NAME).then(cache => { + return cache.addAll(files); + }); + }) + .catch(err => { + console.error('Failed to fetch pwa-files-to-cache.json', err); + }) + ); + self.skipWaiting(); +}); + +self.addEventListener('activate', event => { + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.filter(name => name !== CACHE_NAME).map(name => caches.delete(name)) + ); + }) + ); + self.clients.claim(); +}); + +self.addEventListener('fetch', event => { + if (event.request.method !== 'GET') return; + const url = new URL(event.request.url); + // Remove query params for cache matching for static files + let cacheKey = url.pathname; + // Only do this for files we know are static (e.g., /data/, /logos/, /logos_gen/, /public/) + if (cacheKey.startsWith('/data/') || cacheKey.startsWith('/logos/') || cacheKey.startsWith('/logos_gen/')) { + // ignore query params + } else { + cacheKey = event.request.url; + } + event.respondWith( + caches.match(cacheKey).then(response => { + if (response) return response; + return fetch(event.request).catch(() => { + if (event.request.mode === 'navigate') { + return caches.match('/index.html'); + } + return Response.error(); + }); + }) + ); +}); diff --git a/scripts/generate-pwa-cache-list.js b/scripts/generate-pwa-cache-list.js new file mode 100644 index 0000000..26708fb --- /dev/null +++ b/scripts/generate-pwa-cache-list.js @@ -0,0 +1,45 @@ +// Moved from project root to scripts directory for consistency +// Node.js script to generate a list of all files in public, logos, and logos_gen for PWA caching +const fs = require('fs'); +const path = require('path'); + +const projectRoot = path.join(__dirname, '..'); +const publicDir = path.join(projectRoot, 'public'); +const logosDir = path.join(projectRoot, 'logos'); +const logosGenDir = path.join(projectRoot, 'logos_gen'); + +// List of files to ignore +const IGNORED_FILES = ['.DS_Store', 'CNAME', 'pwa-files-to-cache.json', '.gitignore']; + +function walkDir(dir, baseUrl = '') { + let results = []; + fs.readdirSync(dir).forEach(file => { + if (IGNORED_FILES.includes(file)) return; // Ignore listed files + const filePath = path.join(dir, file); + const relPath = path.join(baseUrl, file).replace(/\\/g, '/'); + if (fs.statSync(filePath).isDirectory()) { + results = results.concat(walkDir(filePath, relPath)); + } else { + results.push('/' + relPath); + } + }); + return results; +} + +function safeWalkDir(dir, baseUrl = '') { + if (!fs.existsSync(dir)) return []; + return walkDir(dir, baseUrl); +} + +const publicFiles = walkDir(publicDir, '').filter(f => !f.endsWith('sw.js')); +const logosFiles = safeWalkDir(logosDir, 'logos'); +const logosGenFiles = safeWalkDir(logosGenDir, 'logos_gen'); + +const allFiles = Array.from(new Set([...publicFiles, ...logosFiles, ...logosGenFiles])); + +fs.writeFileSync( + path.join(publicDir, 'pwa-files-to-cache.json'), + JSON.stringify(allFiles, null, 2) +); + +console.log('PWA files-to-cache list generated with', allFiles.length, 'files.'); diff --git a/scripts/generateFavicons.js b/scripts/generateFavicons.js index a4ea807..6465028 100644 --- a/scripts/generateFavicons.js +++ b/scripts/generateFavicons.js @@ -10,6 +10,8 @@ const srcSvgPath = path.join(__dirname, '../public/favicon.svg'); const pngOutputPath = path.join(__dirname, '../public/apple-touch-icon.png'); const icoOutputPath = path.join(__dirname, '../public/favicon.ico'); const faviconPngPath = path.join(__dirname, '../public/favicon.png'); +const icon192Path = path.join(__dirname, '../public/icon-192.png'); +const icon512Path = path.join(__dirname, '../public/icon-512.png'); // Ensure the favicon.svg file exists if (!fs.existsSync(srcSvgPath)) { @@ -55,10 +57,20 @@ async function generateFavicons() { fs.copyFileSync(faviconPngPath, icoOutputPath); console.log(`Created ${icoOutputPath} (Note: This is actually a PNG file renamed to .ico)`); + // Generate icon-192.png for PWA + const icon192 = await jimp.read(tempPngPath); + await icon192.resize(192, 192).writeAsync(icon192Path); + console.log(`Created ${icon192Path}`); + + // Generate icon-512.png for PWA + const icon512 = await jimp.read(tempPngPath); + await icon512.resize(512, 512).writeAsync(icon512Path); + console.log(`Created ${icon512Path}`); + // Clean up temporary file fs.unlinkSync(tempPngPath); - console.log('Favicon generation completed successfully!'); + console.log('Favicon and PWA icon generation completed successfully!'); } catch (error) { console.error('Error generating favicons:', error); process.exit(1); diff --git a/src/main.js b/src/main.js index c53db90..c35c960 100644 --- a/src/main.js +++ b/src/main.js @@ -1,3 +1,9 @@ import App from './App.svelte'; new App({ target: document.getElementById('app') }); + +if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('/sw.js'); + }); +}