From d4a504682610cfe2e72e4469afcaeb0ae394af3f Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Tue, 26 May 2026 15:50:29 +0000 Subject: [PATCH] feat: PWA support - manifest, service worker, icons --- public/icon-192.png | Bin 0 -> 2201 bytes public/icon-512.png | Bin 0 -> 7374 bytes public/index.html | 23 +++++++++++++++ public/manifest.json | 26 +++++++++++++++++ public/sw.js | 68 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 public/icon-192.png create mode 100644 public/icon-512.png create mode 100644 public/manifest.json create mode 100644 public/sw.js diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..62fe69f0f9b9420472aae94a029d5f46c16ff40f GIT binary patch literal 2201 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 H|Lgw%%!Nrf literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html index 1f46447..bb05e78 100644 --- a/public/index.html +++ b/public/index.html @@ -3,6 +3,15 @@ + + + + + + + + + Camila AI @@ -202,5 +211,19 @@ + + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..5686b74 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,26 @@ +{ + "name": "Camila AI", + "short_name": "Camila", + "description": "Assistente virtual da Camila", + "start_url": "/", + "display": "standalone", + "background_color": "#0a0a0a", + "theme_color": "#10a37f", + "orientation": "portrait-primary", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ], + "categories": ["education", "productivity"], + "lang": "pt-BR" +} \ No newline at end of file diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..365abd5 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,68 @@ +const CACHE_NAME = 'camila-ai-v1'; +const urlsToCache = [ + '/', + '/index.html', + '/style.css', + '/app.js', + '/manifest.json', + '/icon-192.png', + '/icon-512.png' +]; + +// Install event - cache assets +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME) + .then((cache) => { + console.log('Camila AI: Cache opened'); + return cache.addAll(urlsToCache); + }) + .then(() => self.skipWaiting()) + ); +}); + +// Activate event - clean old caches +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + console.log('Camila AI: Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); + }).then(() => self.clients.claim()) + ); +}); + +// Fetch event - serve from cache, fallback to network +self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request) + .then((response) => { + if (response) { + return response; + } + return fetch(event.request).then((response) => { + // Don't cache non-successful responses or non-GET requests + if (!response || response.status !== 200 || event.request.method !== 'GET') { + return response; + } + // Clone the response + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }); + }) + .catch(() => { + // If both cache and network fail, show offline page for HTML requests + if (event.request.headers.get('accept').includes('text/html')) { + return caches.match('/'); + } + }) + ); +}); \ No newline at end of file