Companion-App: zur deploybaren PWA ausgebaut (statisch)
- 04_Tablet-Quiz/prototype -> app/ (deploybarer Stand). - PWA: manifest.webmanifest + sw.js (Offline-App-Shell) + icon.svg, im <head> eingebunden, Service-Worker-Registrierung. - Debrief-Export als Datei-Download (Markdown UND JSON) ergaenzt. - DEPLOY.md: Anleitung fuer statisches Hosting (nginx/Caddy, HTTPS, Verifikation). - README: Umsetzungsstand + MVP-Haken aktualisiert. - .claude/launch.json (lokale Preview), settings.local.json ge-gitignored. Verifiziert: 40 Stationen / 3 Gates / 45 Quizfragen, JS+SW-Syntax + Manifest valide, alle Assets via http 200. (Inhalte noch in index.html eingebettet; YAML-Pipeline = naechster Schritt.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ab61222cf2
commit
7f0e847561
8 changed files with 196 additions and 9 deletions
66
04_Tablet-Quiz/app/DEPLOY.md
Normal file
66
04_Tablet-Quiz/app/DEPLOY.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Deployment — SLC-Workshop Companion (App)
|
||||
|
||||
**Auftrag für die Server-Claude / Ops:** Diese App **statisch** ausliefern. Kein
|
||||
Build-Schritt, kein Backend, keine Secrets, keine Datenbank.
|
||||
|
||||
## Was das ist
|
||||
- Eine **statische Single-Page-PWA**. Alle Inhalte (Stationen, Quiz, Use-Cases)
|
||||
sind in `index.html` eingebettet — keine Laufzeit-API nötig.
|
||||
- Dateien im Ordner `04_Tablet-Quiz/app/`:
|
||||
- `index.html` — die App
|
||||
- `manifest.webmanifest` — PWA-Manifest
|
||||
- `sw.js` — Service Worker (Offline-Cache der App-Shell)
|
||||
- `icon.svg` — App-Icon
|
||||
|
||||
## Ziel
|
||||
Den Ordner `04_Tablet-Quiz/app/` als **statisches Web-Root** über den vorhandenen
|
||||
Webserver bereitstellen, erreichbar per **HTTPS** (oder localhost).
|
||||
|
||||
> **Wichtig:** Der Service Worker (Offline/Installierbar) läuft **nur über HTTPS
|
||||
> oder localhost**. Über reines `http://` ohne TLS registriert er sich nicht —
|
||||
> die App funktioniert dann trotzdem, nur ohne Offline-Cache.
|
||||
|
||||
## Schritte
|
||||
1. Repo auf dem Server bereitstellen/aktualisieren:
|
||||
`git clone https://git.1789.cloud/patrick/SLC_Game.git` (bzw. `git pull`).
|
||||
2. Den Ordner `SLC_Game/04_Tablet-Quiz/app/` als statisches Root einbinden.
|
||||
|
||||
### Beispiel nginx
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name slc.example.intern; # anpassen
|
||||
# ssl_certificate ... ; ssl_certificate_key ... ;
|
||||
root /srv/SLC_Game/04_Tablet-Quiz/app; # Pfad anpassen
|
||||
index index.html;
|
||||
location = /sw.js { add_header Cache-Control "no-cache"; } # SW immer frisch
|
||||
location / { try_files $uri $uri/ /index.html; }
|
||||
}
|
||||
```
|
||||
|
||||
### Beispiel Caddy (TLS automatisch)
|
||||
```caddy
|
||||
slc.example.intern {
|
||||
root * /srv/SLC_Game/04_Tablet-Quiz/app
|
||||
file_server
|
||||
header /sw.js Cache-Control "no-cache"
|
||||
}
|
||||
```
|
||||
|
||||
## Verifikation nach dem Deploy
|
||||
1. URL öffnen → Startbildschirm „SLC Companion · Schritt 1 · Action Card".
|
||||
2. DevTools → Application → **Service Workers**: `sw.js` ist *activated*.
|
||||
3. Flugmodus/Offline → Seite neu laden → App lädt weiterhin (Offline-Cache).
|
||||
4. Einen Durchlauf spielen → „Debrief" → **↓ Markdown / ↓ JSON** lädt eine Datei.
|
||||
|
||||
## Updates
|
||||
- Bei neuem Stand: `git pull`. Wenn sich App-Assets geändert haben, in `sw.js`
|
||||
die Konstante `CACHE` hochzählen (z. B. `slc-companion-v1` → `-v2`), damit der
|
||||
Service Worker den Cache erneuert.
|
||||
|
||||
## Noch offen (nicht Teil dieses Deploys)
|
||||
- **YAML→Inhaltspipeline:** Inhalte sind aktuell in `index.html` eingebettet.
|
||||
Später aus den Blueprint-`service-lifecycle_*.yaml` generieren (braucht Zugriff
|
||||
aufs Blueprint-Repo). Bis dahin werden Inhalte direkt in `index.html` gepflegt.
|
||||
- **Companion-Chatbot** (optionaler Nachschlage-Bot) — siehe `../README.md` §8;
|
||||
*braucht* ein LLM-Backend und ist daher **nicht** Teil der statischen App.
|
||||
12
04_Tablet-Quiz/app/icon.svg
Normal file
12
04_Tablet-Quiz/app/icon.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
|
||||
<!-- maskable: vollflaechiger Hintergrund, Inhalt im sicheren Mittelbereich -->
|
||||
<rect width="512" height="512" fill="#1d2430"/>
|
||||
<!-- Bahn-Linie -->
|
||||
<line x1="150" y1="256" x2="362" y2="256" stroke="#3a4658" stroke-width="10" stroke-linecap="round"/>
|
||||
<!-- 5 Phasen-Pucks (blau/orange/gruen/teal/lila) -->
|
||||
<circle cx="160" cy="256" r="24" fill="#2F80C9"/>
|
||||
<circle cx="208" cy="256" r="24" fill="#E8893B"/>
|
||||
<circle cx="256" cy="256" r="24" fill="#5BAE5B"/>
|
||||
<circle cx="304" cy="256" r="24" fill="#3FB5B5"/>
|
||||
<circle cx="352" cy="256" r="24" fill="#8E63B5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 669 B |
1236
04_Tablet-Quiz/app/index.html
Normal file
1236
04_Tablet-Quiz/app/index.html
Normal file
File diff suppressed because it is too large
Load diff
15
04_Tablet-Quiz/app/manifest.webmanifest
Normal file
15
04_Tablet-Quiz/app/manifest.webmanifest
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "SLC-Workshop Companion",
|
||||
"short_name": "SLC Companion",
|
||||
"description": "Begleit-App zum SLC-Workshop-Tabletop: Stationsführung, vermittelndes Quiz, Auflösung und Debrief. Offline lauffähig.",
|
||||
"start_url": "./",
|
||||
"scope": "./",
|
||||
"display": "standalone",
|
||||
"orientation": "landscape",
|
||||
"lang": "de",
|
||||
"background_color": "#f4f5f7",
|
||||
"theme_color": "#1d2430",
|
||||
"icons": [
|
||||
{ "src": "icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable" }
|
||||
]
|
||||
}
|
||||
33
04_Tablet-Quiz/app/sw.js
Normal file
33
04_Tablet-Quiz/app/sw.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* Service Worker — SLC-Workshop Companion (App-Shell, offline-first) */
|
||||
const CACHE = "slc-companion-v1";
|
||||
const ASSETS = ["./", "index.html", "manifest.webmanifest", "icon.svg"];
|
||||
|
||||
self.addEventListener("install", (e) => {
|
||||
e.waitUntil(
|
||||
caches.open(CACHE).then((c) => c.addAll(ASSETS)).then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (e) => {
|
||||
e.waitUntil(
|
||||
caches.keys()
|
||||
.then((ks) => Promise.all(ks.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (e) => {
|
||||
if (e.request.method !== "GET") return;
|
||||
e.respondWith(
|
||||
caches.match(e.request).then((hit) =>
|
||||
hit ||
|
||||
fetch(e.request)
|
||||
.then((resp) => {
|
||||
const copy = resp.clone();
|
||||
caches.open(CACHE).then((c) => c.put(e.request, copy));
|
||||
return resp;
|
||||
})
|
||||
.catch(() => caches.match("index.html"))
|
||||
)
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue