feat(web): PWA-Setup über @mana/shared-pwa (Adoption #9)
Some checks are pending
CI / validate (push) Waiting to run
Some checks are pending
CI / validate (push) Waiting to run
createOfflineFirstPWAConfig — schließt sql-wasm.wasm aus dem Precache
(Wordeck nutzt sql.js für lokales Spaced-Repetition). themeBridge('forest')
#117e39 passend zum data-theme='forest'. 2 Shortcuts (Decks + Entdecken).
OfflineBanner/UpdatePrompt/InstallPrompt unter skip-link. Icons aus
wordeck-native AppIcon-1024.
Build grün.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b13c9dd914
commit
633f051f2d
12 changed files with 3479 additions and 56 deletions
|
|
@ -3,7 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Cards-Web \u2014 SvelteKit 2 + Svelte 5 Frontend f\u00fcr cardecky.mana.how.",
|
||||
"description": "Cards-Web — SvelteKit 2 + Svelte 5 Frontend für cardecky.mana.how.",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port 3082 --host",
|
||||
"build": "vite build",
|
||||
|
|
@ -15,14 +15,17 @@
|
|||
"clean": "rm -rf .svelte-kit build .turbo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mana/shared-icons": "^1.0.0",
|
||||
"@mana/shared-pwa": "0.1.0-alpha.3",
|
||||
"@mana/shared-ui-2": "^0.1.0",
|
||||
"@mana/themes": "^0.1.0",
|
||||
"@vite-pwa/sveltekit": "^1.1.0",
|
||||
"@wordeck/domain": "workspace:*",
|
||||
"dompurify": "^3.4.2",
|
||||
"jszip": "^3.10.1",
|
||||
"marked": "^18.0.3",
|
||||
"sql.js": "^1.14.1",
|
||||
"@mana/themes": "^0.1.0",
|
||||
"@mana/shared-ui-2": "^0.1.0",
|
||||
"@mana/shared-icons": "^1.0.0"
|
||||
"workbox-window": "^7.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "^5.2.0",
|
||||
|
|
@ -32,6 +35,7 @@
|
|||
"@types/dompurify": "^3.2.0",
|
||||
"@types/jszip": "^3.4.1",
|
||||
"@types/sql.js": "^1.4.11",
|
||||
"sharp": "^0.34.5",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.2.4",
|
||||
|
|
|
|||
2
apps/web/src/app.d.ts
vendored
2
apps/web/src/app.d.ts
vendored
|
|
@ -1,5 +1,7 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
/// <reference types="vite-plugin-pwa/client" />
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
<html lang="de" data-theme="forest">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="description" content="Wordeck — Spaced Repetition, text-first. Lernkarten-App des Vereins mana e.V." />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<meta name="theme-color" content="#117e39" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<title>Wordeck</title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,33 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import '../app.css';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import ToastStack from '$lib/components/ToastStack.svelte';
|
||||
import { i18n, t } from '$lib/i18n/index.svelte.ts';
|
||||
import { page } from '$app/state';
|
||||
import {
|
||||
InstallPrompt,
|
||||
UpdatePrompt,
|
||||
OfflineBanner,
|
||||
pwaState,
|
||||
} from '@mana/shared-pwa/components';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
// @ts-expect-error virtual module from vite-plugin-pwa
|
||||
const { registerSW } = await import('virtual:pwa-register');
|
||||
const updateSW = registerSW({
|
||||
onNeedRefresh() {
|
||||
pwaState.registerUpdateHandler(updateSW);
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// Dev oder Build ohne PWA-Plugin → ignorieren.
|
||||
}
|
||||
});
|
||||
|
||||
// Hält das `lang`-Attribut am <html> aktuell. Initial-SSR rendert
|
||||
// "de"; sobald i18n-Detection im Browser läuft, ziehen wir nach.
|
||||
$effect(() => {
|
||||
|
|
@ -28,6 +49,10 @@
|
|||
|
||||
<a href="#main" class="skip-link">{t('common.skip_to_content')}</a>
|
||||
|
||||
<OfflineBanner />
|
||||
<UpdatePrompt />
|
||||
<InstallPrompt />
|
||||
|
||||
{#if !isFocusMode && !isLoginPage}
|
||||
<Header />
|
||||
{/if}
|
||||
|
|
|
|||
31
apps/web/src/routes/offline/+page.svelte
Normal file
31
apps/web/src/routes/offline/+page.svelte
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts">
|
||||
function goBack() {
|
||||
if (typeof history !== 'undefined' && history.length > 1) {
|
||||
history.back();
|
||||
} else {
|
||||
location.href = '/';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline · Wordeck</title>
|
||||
</svelte:head>
|
||||
|
||||
<section
|
||||
class="mx-auto mt-12 max-w-md rounded-lg border bg-[hsl(var(--color-card))] border-[hsl(var(--color-border))] p-6 text-center"
|
||||
>
|
||||
<h1 class="text-xl font-semibold">Du bist offline</h1>
|
||||
<p class="mt-2 text-sm text-[hsl(var(--color-muted-foreground))]">
|
||||
Schon geladene Decks und Karten sind im Cache — Spaced-Repetition läuft
|
||||
auch ohne Netz weiter (Wordeck ist text-only by design). Sync mit dem
|
||||
Server passiert, sobald wieder Netz da ist.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="mt-4 rounded-md bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
|
||||
onclick={goBack}
|
||||
>
|
||||
Zurück
|
||||
</button>
|
||||
</section>
|
||||
BIN
apps/web/static/apple-touch-icon.png
Normal file
BIN
apps/web/static/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
apps/web/static/icon-source.png
Normal file
BIN
apps/web/static/icon-source.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
BIN
apps/web/static/pwa-192x192.png
Normal file
BIN
apps/web/static/pwa-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
BIN
apps/web/static/pwa-512x512.png
Normal file
BIN
apps/web/static/pwa-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -7,6 +7,7 @@
|
|||
"sourceMap": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
"noEmit": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,29 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import { SvelteKitPWA } from '@vite-pwa/sveltekit';
|
||||
import { createOfflineFirstPWAConfig, themeBridge } from '@mana/shared-pwa';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
sveltekit(),
|
||||
SvelteKitPWA(
|
||||
// Wordeck nutzt sql.js (WASM) für lokale Spaced-Repetition —
|
||||
// createOfflineFirstPWAConfig excludet WASM-Blobs vom Precache,
|
||||
// damit sie nicht das 8-MiB-Cache-Cap pro Datei sprengen.
|
||||
createOfflineFirstPWAConfig({
|
||||
name: 'Wordeck',
|
||||
shortName: 'Wordeck',
|
||||
description: 'Spaced Repetition, text-first. Lernkarten-App des Vereins mana e.V.',
|
||||
...themeBridge('forest'),
|
||||
shortcuts: [
|
||||
{ name: 'Meine Decks', short_name: 'Decks', url: '/' },
|
||||
{ name: 'Entdecken', short_name: 'Entdecken', url: '/explore' },
|
||||
],
|
||||
})
|
||||
),
|
||||
],
|
||||
server: {
|
||||
port: Number(process.env.WORDECK_WEB_PORT ?? 3082),
|
||||
host: true,
|
||||
|
|
|
|||
3435
pnpm-lock.yaml
generated
3435
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue