mirror of
https://github.com/shadoll/sLogos.git
synced 2025-12-20 03:26:59 +00:00
feat: Add PWA support with service worker and caching
- Implemented service worker registration in main.js - Added icons for PWA in manifest.json - Created a basic service worker (sw.js) for caching static assets - Generated a list of files to cache using a Node.js script (generate-pwa-cache-list.js) - Added icon images (icon-192.png and icon-512.png) for PWA - Defined PWA manifest with app details and icon references
This commit is contained in:
4
.github/workflows/gh-pages.yml
vendored
4
.github/workflows/gh-pages.yml
vendored
@@ -37,6 +37,9 @@ jobs:
|
|||||||
- name: Generate SVG variants
|
- name: Generate SVG variants
|
||||||
run: node scripts/generate-svg-variants.js
|
run: node scripts/generate-svg-variants.js
|
||||||
|
|
||||||
|
- name: Generate PWA cache list
|
||||||
|
run: node scripts/generate-pwa-cache-list.js
|
||||||
|
|
||||||
- name: Prepare Pages artifact
|
- name: Prepare Pages artifact
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./gh-pages-artifact/build
|
mkdir -p ./gh-pages-artifact/build
|
||||||
@@ -50,6 +53,7 @@ jobs:
|
|||||||
cp -r public/data ./gh-pages-artifact/
|
cp -r public/data ./gh-pages-artifact/
|
||||||
cp -r public/logos ./gh-pages-artifact/
|
cp -r public/logos ./gh-pages-artifact/
|
||||||
cp -r public/logos_gen ./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
|
if [ -f public/CNAME ]; then
|
||||||
cp public/CNAME ./gh-pages-artifact/CNAME
|
cp public/CNAME ./gh-pages-artifact/CNAME
|
||||||
fi
|
fi
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ yarn.lock
|
|||||||
|
|
||||||
# Build output
|
# Build output
|
||||||
public/build/
|
public/build/
|
||||||
|
public/pwa-files-to-cache.json
|
||||||
|
|
||||||
# macOS specific files
|
# macOS specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
14
Makefile
14
Makefile
@@ -6,21 +6,19 @@ CONTAINER_NAME = slogos-dev
|
|||||||
DEV_PORT = 5006
|
DEV_PORT = 5006
|
||||||
|
|
||||||
# Main targets
|
# 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
|
all: build start
|
||||||
|
|
||||||
# Development mode with hot reloading
|
dev: pwa-cache-list
|
||||||
dev:
|
|
||||||
$(DOCKER_COMPOSE) -f compose.dev.yml up --build
|
$(DOCKER_COMPOSE) -f compose.dev.yml up --build
|
||||||
|
|
||||||
# Build the Docker container
|
|
||||||
build:
|
build:
|
||||||
@echo "Building the Logo Gallery container..."
|
@echo "Building the Logo Gallery container..."
|
||||||
$(DOCKER_COMPOSE) -f compose.dev.yml build
|
$(DOCKER_COMPOSE) -f compose.dev.yml build
|
||||||
|
|
||||||
# Start the application in the background
|
# Start the application in the background
|
||||||
start:
|
start: pwa-cache-list
|
||||||
@echo "Starting Logo Gallery application on port $(DEV_PORT)..."
|
@echo "Starting Logo Gallery application on port $(DEV_PORT)..."
|
||||||
$(DOCKER_COMPOSE) -f compose.dev.yml up -d
|
$(DOCKER_COMPOSE) -f compose.dev.yml up -d
|
||||||
@echo "Application is running at http://localhost:$(DEV_PORT)"
|
@echo "Application is running at http://localhost:$(DEV_PORT)"
|
||||||
@@ -81,3 +79,9 @@ generate-svg-variants:
|
|||||||
@echo "Generating SVG variants with color sets..."
|
@echo "Generating SVG variants with color sets..."
|
||||||
$(DOCKER_COMPOSE) -f compose.dev.yml run --rm $(CONTAINER_NAME) node scripts/generate-svg-variants.js
|
$(DOCKER_COMPOSE) -f compose.dev.yml run --rm $(CONTAINER_NAME) node scripts/generate-svg-variants.js
|
||||||
@echo "SVG variants have been generated"
|
@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
|
||||||
|
|
||||||
|
|||||||
14
ToDo.md
14
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: 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: In the preview page, add full header.
|
||||||
[] Improove: Split header into two parts: static top and dynamic bottom.
|
[ ] Improove: Split header into two parts: static top and dynamic bottom.
|
||||||
[] Improove: In the preview page, add possibility select custom color for each target.
|
[ ] Improove: In the preview page, add possibility select custom color for each target.
|
||||||
[] Strategy: Add differents base/collections of images: Flags,
|
[ ] Strategy: Add differents base/collections of images: Flags,
|
||||||
[] Strategy: WebApp, PWA
|
[v] Strategy: WebApp, PWA
|
||||||
[] Improove: Add to filter Image variants - just logo, line logo, square logo
|
[ ] Improove: Add to filter Image variants - just logo, line logo, square logo
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"scan-logos": "node scripts/scanLogos.js",
|
"scan-logos": "node scripts/scanLogos.js",
|
||||||
"generate-favicons": "node scripts/generateFavicons.js",
|
"generate-favicons": "node scripts/generateFavicons.js",
|
||||||
"generate-variants": "node scripts/generate-svg-variants.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": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
|
|||||||
BIN
public/icon-192.png
Normal file
BIN
public/icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/icon-512.png
Normal file
BIN
public/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
@@ -12,6 +12,20 @@
|
|||||||
<link rel="icon" href="favicon.png" sizes="32x32">
|
<link rel="icon" href="favicon.png" sizes="32x32">
|
||||||
<link rel="icon" href="favicon.ico" sizes="any">
|
<link rel="icon" href="favicon.ico" sizes="any">
|
||||||
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||||
|
|
||||||
|
<!-- Manifest and theme color for PWA -->
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
<!-- To test PWA in Safari: Open Web Inspector > Storage tab. You should see 'Service Workers' and 'Manifest'. -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/sw.js').then(function(reg) {
|
||||||
|
console.log('Service worker registered.', reg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
20
public/manifest.json
Normal file
20
public/manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
54
public/sw.js
Normal file
54
public/sw.js
Normal file
@@ -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();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
45
scripts/generate-pwa-cache-list.js
Normal file
45
scripts/generate-pwa-cache-list.js
Normal file
@@ -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.');
|
||||||
@@ -10,6 +10,8 @@ const srcSvgPath = path.join(__dirname, '../public/favicon.svg');
|
|||||||
const pngOutputPath = path.join(__dirname, '../public/apple-touch-icon.png');
|
const pngOutputPath = path.join(__dirname, '../public/apple-touch-icon.png');
|
||||||
const icoOutputPath = path.join(__dirname, '../public/favicon.ico');
|
const icoOutputPath = path.join(__dirname, '../public/favicon.ico');
|
||||||
const faviconPngPath = path.join(__dirname, '../public/favicon.png');
|
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
|
// Ensure the favicon.svg file exists
|
||||||
if (!fs.existsSync(srcSvgPath)) {
|
if (!fs.existsSync(srcSvgPath)) {
|
||||||
@@ -55,10 +57,20 @@ async function generateFavicons() {
|
|||||||
fs.copyFileSync(faviconPngPath, icoOutputPath);
|
fs.copyFileSync(faviconPngPath, icoOutputPath);
|
||||||
console.log(`Created ${icoOutputPath} (Note: This is actually a PNG file renamed to .ico)`);
|
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
|
// Clean up temporary file
|
||||||
fs.unlinkSync(tempPngPath);
|
fs.unlinkSync(tempPngPath);
|
||||||
|
|
||||||
console.log('Favicon generation completed successfully!');
|
console.log('Favicon and PWA icon generation completed successfully!');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating favicons:', error);
|
console.error('Error generating favicons:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
import App from './App.svelte';
|
import App from './App.svelte';
|
||||||
|
|
||||||
new App({ target: document.getElementById('app') });
|
new App({ target: document.getElementById('app') });
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('/sw.js');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user