diff --git a/public/admin/index.html b/public/admin/index.html index 6a11d21..4486b3a 100644 --- a/public/admin/index.html +++ b/public/admin/index.html @@ -65,6 +65,30 @@ h1, h2, h3 { font-family: "Fraunces", "Playfair Display", Georgia, serif !important; } + + /* Schwebender "Installieren"-Button — nur sichtbar, wenn der Browser eine Installation + wirklich anbietet (siehe Script unten). position:fixed statt normaler Dokumentfluss, + damit er unabhängig davon funktioniert, wie Sveltia sein eigenes UI in aufbaut. */ + #install-btn { + display: none; + position: fixed; + bottom: 1.2rem; + right: 1.2rem; + z-index: 9999; + align-items: center; + gap: 0.5em; + padding: 0.7em 1.1em; + border-radius: 999px; + background: linear-gradient(135deg, #9b7fd6, #2e1b47); + border: 1px solid rgba(155, 127, 214, 0.6); + color: #f4f1f7; + font-family: "Inter", system-ui, sans-serif; + font-size: 0.88rem; + font-weight: 600; + cursor: pointer; + box-shadow: 0 10px 24px -8px rgba(0, 0, 0, 0.55); + } + #install-btn:hover { filter: brightness(1.1); } @@ -72,5 +96,37 @@ direkt im GitHub-Repo. Kein eigener Server nötig — siehe config.yml für die Feld-Definitionen (Produkte, Kategorien). --> + + + diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..6bce56c --- /dev/null +++ b/public/sw.js @@ -0,0 +1,41 @@ +// Minimaler, bewusst zurückhaltender Service Worker — NICHT für "volles Offline", sondern +// hauptsächlich damit der Browser die Seite überhaupt als installierbare App erkennt (die +// meisten Browser verlangen dafür einen registrierten Service Worker mit echtem +// fetch-Handler) und als kleiner Bonus: falls doch mal keine Verbindung besteht, können bereits +// besuchte Seiten trotzdem noch geöffnet werden. +// +// WICHTIG: strikt "network-first" — bei bestehender Verbindung wird IMMER zuerst das echte +// Netzwerk gefragt, der Cache dient nur als Rückfalllösung für den Offline-Fall. Das ist bewusst +// so gewählt, damit z.B. der Adminbereich (Sveltia CMS) NIE veraltete Inhalte aus dem Cache +// zeigt, solange eine Internetverbindung besteht. + +const CACHE_NAME = "vandiy-shell-v1"; + +self.addEventListener("install", () => { + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil( + caches.keys().then((keys) => + Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))) + ).then(() => self.clients.claim()) + ); +}); + +self.addEventListener("fetch", (event) => { + if (event.request.method !== "GET") return; + // Cross-Origin-Requests (Google Fonts, PayPal-SDK, GitHub-API des Admin-Bereichs, ...) + // unangetastet lassen — nur die eigene Seite selbst wird für den Offline-Fall vorgehalten. + if (new URL(event.request.url).origin !== self.location.origin) return; + + event.respondWith( + fetch(event.request) + .then((response) => { + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone)).catch(() => {}); + return response; + }) + .catch(() => caches.match(event.request).then((cached) => cached || Response.error())) + ); +}); diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index 6d10fb9..cba482a 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -23,6 +23,7 @@ export const ui = { onlyXLeft: (n: string) => `Nur noch ${n} auf Lager`, maxInCart: "Maximale verfügbare Menge ist bereits im Warenkorb", maxInCartShort: "Bereits im Warenkorb", browseShop: "Jetzt stöbern", viewAll: "Alle ansehen →", viewAllSale: "Alle Angebote →", toShop: "Zum Shop →", moreAboutMe: "Mehr über mich →", discoverNow: "Jetzt entdecken", + installApp: "App installieren", installAppShort: "Installieren", vatNote: "Gemäß §19 UStG wird keine Umsatzsteuer berechnet und ausgewiesen.", allPricesPlusShipping: "Alle Preise zzgl. Versand.", plusShipping: "zzgl. Versand", quantity: "Anzahl", photoSoon: "Produktfoto folgt", portraitSoon: "Porträtfoto von VanVan folgt", @@ -210,6 +211,7 @@ export const ui = { onlyXLeft: (n: string) => `Only ${n} left in stock`, maxInCart: "The maximum available quantity is already in your cart", maxInCartShort: "Already in cart", browseShop: "Browse now", viewAll: "View all →", viewAllSale: "View all offers →", toShop: "To the shop →", moreAboutMe: "More about me →", discoverNow: "Discover now", + installApp: "Install app", installAppShort: "Install", vatNote: "Under §19 UStG (German small-business rule) no VAT is charged or shown.", allPricesPlusShipping: "All prices plus shipping.", plusShipping: "plus shipping", quantity: "Quantity", photoSoon: "Product photo coming soon", portraitSoon: "VanVan's portrait photo coming soon", @@ -397,6 +399,7 @@ export const ui = { onlyXLeft: (n: string) => `Nume no ${n} a Lager`, maxInCart: "D'maximal verfügbari Mengi isch scho im Warenchorb", maxInCartShort: "Scho im Warenchorb", browseShop: "Jetzt use luege", viewAll: "Alles aluege →", viewAllSale: "Alli Aktione →", toShop: "Zum Shop →", moreAboutMe: "Meh über mich →", discoverNow: "Jetzt aluege", + installApp: "App installiere", installAppShort: "Installiere", vatNote: "Gemäss §19 UStG (dütsches Rächt) wird kei Umsatzsteuer berechnet und usgwiese.", allPricesPlusShipping: "Alli Pryse zzgl. Versand.", plusShipping: "zzgl. Versand", quantity: "Aazahl", photoSoon: "Produktfoto chunnt bald", portraitSoon: "Porträtfoto vo VanVan chunnt bald", @@ -584,6 +587,7 @@ export const ui = { onlyXLeft: (n: string) => `Plus que ${n} en stock`, maxInCart: "La quantité maximale disponible est déjà dans votre panier", maxInCartShort: "Déjà dans le panier", browseShop: "Découvrir la boutique", viewAll: "Tout voir →", viewAllSale: "Toutes les offres →", toShop: "Vers la boutique →", moreAboutMe: "En savoir plus sur moi →", discoverNow: "Découvrir maintenant", + installApp: "Installer l'app", installAppShort: "Installer", vatNote: "Conformément à l'§19 UStG (droit allemand), aucune TVA n'est calculée ni indiquée.", allPricesPlusShipping: "Tous les prix hors frais de port.", plusShipping: "hors frais de port", quantity: "Quantité", photoSoon: "Photo du produit à venir", portraitSoon: "Photo de VanVan à venir", diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index b4c3267..f3bbaa2 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -71,6 +71,12 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
+