diff --git a/k8s/openclaw/openclaw-instance.secret.yaml b/k8s/openclaw/openclaw-instance.secret.yaml index e8fc0ef..72bb429 100644 Binary files a/k8s/openclaw/openclaw-instance.secret.yaml and b/k8s/openclaw/openclaw-instance.secret.yaml differ diff --git a/k8s/web/sasch40.yaml b/k8s/web/sasch40.yaml new file mode 100644 index 0000000..140efc5 --- /dev/null +++ b/k8s/web/sasch40.yaml @@ -0,0 +1,74 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sasch40 + labels: + app: sasch40 + namespace: web +spec: + selector: + matchLabels: + app: sasch40 + replicas: 1 + template: + metadata: + labels: + app: sasch40 + spec: + imagePullSecrets: + - name: "registry-haumdaucher-de" + containers: + - image: registry.haumdaucher.de/sasch40:latest + imagePullPolicy: Always + name: sasch40 + ports: + - containerPort: 80 + resources: + requests: + cpu: 20m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + +--- +apiVersion: v1 +kind: Service +metadata: + name: sasch40 + namespace: web +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + selector: + app: sasch40 + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: sasch40 + namespace: web + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" +spec: + ingressClassName: traefik + tls: + - hosts: + - "sasch40.haumdaucher.de" + secretName: sasch40-haumdaucher-de-tls + rules: + - host: sasch40.haumdaucher.de + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: sasch40 + port: + number: 80 diff --git a/projects/sasch40/README.md b/projects/sasch40/README.md new file mode 100644 index 0000000..65a05b3 --- /dev/null +++ b/projects/sasch40/README.md @@ -0,0 +1,60 @@ +# Saschas Kronkorken Kreuzzug: 40 Jahre Bass & Gerstensaft 👑🍺🔊 + +Ein mobiles 2.5D Pixel-Art Web-Game zum 40. Geburtstag von König Sascha I. +Gehostet unter: `https://sasch40.haumdaucher.de` + +--- + +## 🌟 Spielinhalt & Akte +1. **Prolog (Schloss Haumdaucher Balkon)**: NAT berichtet als Hof-Chronist (mit Bridgerton-Referenzen) vom Verlust der 40 Kronkorken des Reiches. Mop der Erschaffer schaltet die Sprungfedern frei. +2. **Level 1 (Die Hopfen-Pfade)**: 60fps Casual-Jump&Run auf Dächern und Fässern zum Sammeln von Kronkorken und kühlem Hellen. +3. **Die Taverne 'Zum wummernden Ritter'**: Point & Click Adventure mit interaktiven Charakteren: + - Verena (*„Ich putze nach der Party fei ned!“*) + - Armend (schmiedet Kronkorken zum goldenen Subwoofer) + - Lutz im ZTUL-Shirt (Schornsteinfeger) + - Amy in Latzhose (*„Rave the Kingdom!“*) + - Mane (Wander- und Spieletipps) + - Pü (Lichtmeister mit Stroboskop-Fackeln) + - Der Zuluman-Zauberspiegel (Urlaubs-Easter-Egg) + - Bayerisches Beleidigungs-Gstanzl-Fechten +4. **Level 2 (Der Fest-Konvoi)**: Speed-Runner auf dem Bollerwagen mit 135-BPM-Techno und dem jubelnden Haumdaucher Gefolge (Hansi, Viktor, Albin, Fabian, Maier Flo, Guatemalteke, Markus & Nathalie). +5. **Finale & Krönung**: Thronsaal mit Konfetti-Explosion, Auswertung und druck-/screenshotbarer königlicher Urkunde. + +--- + +## 🖼️ Hot-Swappable Asset-Pipeline (Bilder austauschen) +Alle Charakter-Bilder und Sprites sind vollständig vom Code entkoppelt: + +* **Bilder-Ordner**: `projects/sasch40/frontend/public/assets/characters/` + * `sascha/portrait.jpg` (oder `.png`) + * `nat/portrait.jpg` + * `verena/portrait.jpg` + * `mop/portrait.jpg` + * `lutz/portrait.jpg` + * `armend/portrait.jpg` + * `amy/portrait.jpg` +* **Originalfotos**: Liegen in `projects/sasch40/source_photos/` +* **Zentrales Register**: Falls ein Dateipfad oder Name angepasst werden soll, geschieht das einfach in `projects/sasch40/frontend/src/config/characters.ts`. + +--- + +## 🚀 Lokale Entwicklung + +```bash +cd projects/sasch40/frontend +npm install +npm run dev +# Browser öffnen unter: http://localhost:3000 +``` + +--- + +## 🐳 Docker & Kubernetes Deployment + +```bash +cd projects/sasch40/frontend +make all # Baut und pusht registry.haumdaucher.de/sasch40:latest + +# Ingress & Deployment im Cluster anwenden: +kubectl apply -f ../../../k8s/web/sasch40.yaml +``` diff --git a/projects/sasch40/frontend/.gitignore b/projects/sasch40/frontend/.gitignore new file mode 100644 index 0000000..cd1edfd --- /dev/null +++ b/projects/sasch40/frontend/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +*.tsbuildinfo +.DS_Store +*.local diff --git a/projects/sasch40/frontend/Dockerfile b/projects/sasch40/frontend/Dockerfile new file mode 100644 index 0000000..dc424d7 --- /dev/null +++ b/projects/sasch40/frontend/Dockerfile @@ -0,0 +1,20 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json* ./ + +RUN npm install + +COPY . . + +RUN npm run build + +FROM nginx:alpine + +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/projects/sasch40/frontend/Makefile b/projects/sasch40/frontend/Makefile new file mode 100644 index 0000000..7991028 --- /dev/null +++ b/projects/sasch40/frontend/Makefile @@ -0,0 +1,22 @@ +REGISTRY ?= registry.haumdaucher.de +IMAGE_NAME ?= sasch40 +TAG ?= latest +PLATFORM ?= linux/amd64 + +IMAGE ?= $(REGISTRY)/$(IMAGE_NAME):$(TAG) + +.PHONY: build tag push all dev + +all: build tag push + +dev: + npm run dev + +build: + docker build --platform $(PLATFORM) -t $(IMAGE_NAME):$(TAG) . + +tag: + docker tag $(IMAGE_NAME):$(TAG) $(IMAGE) + +push: + docker push $(IMAGE) diff --git a/projects/sasch40/frontend/index.html b/projects/sasch40/frontend/index.html new file mode 100644 index 0000000..5199459 --- /dev/null +++ b/projects/sasch40/frontend/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + Saschas Kronkorken Kreuzzug – 40 Jahre Bass & Gerstensaft + + +
+ + + diff --git a/projects/sasch40/frontend/nginx.conf b/projects/sasch40/frontend/nginx.conf new file mode 100644 index 0000000..e0c74ce --- /dev/null +++ b/projects/sasch40/frontend/nginx.conf @@ -0,0 +1,20 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 30d; + add_header Cache-Control "public, no-transform"; + } +} diff --git a/projects/sasch40/frontend/package-lock.json b/projects/sasch40/frontend/package-lock.json new file mode 100644 index 0000000..6f85141 --- /dev/null +++ b/projects/sasch40/frontend/package-lock.json @@ -0,0 +1,1661 @@ +{ + "name": "sasch40-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sasch40-frontend", + "version": "1.0.0", + "dependencies": { + "canvas-confetti": "^1.9.4", + "lucide-vue-next": "^0.468.0", + "pinia": "^2.3.0", + "vue": "^3.5.13" + }, + "devDependencies": { + "@types/canvas-confetti": "^1.9.0", + "@types/node": "^22.10.2", + "@vitejs/plugin-vue": "^5.2.1", + "typescript": "^5.7.2", + "vite": "^6.0.3", + "vue-tsc": "^2.2.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.4.tgz", + "integrity": "sha512-I+BSHzTAhKN2n7ZwGZsegGcZjDpLqFOMAtJz/u6uFGe0pUFbq56dEHjqJV/ZUdRJtNXNxA+hREUatZBvMR3Oiw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.4.tgz", + "integrity": "sha512-pu3BdjS2LtEzRu2elmGzS3fIeWSZy4BMDIaLNwjorO76+k2d0LMluijhsDx3KQyQBQ/lLUZCQA9/s6csvUfuhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.4.tgz", + "integrity": "sha512-xfSrj9MHnWK9GaSqT9U0ImHtH/N8WZlHLx4cZHiuLcqs640hvZ3hLPd5UR2AZS57FaE8HrRUSpltbZdWRxHiDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.4.tgz", + "integrity": "sha512-bqU99PLJb/dqb3S0GIMdeuyAEETSUgZBoqXYd3Sd+WCsV+MmPhnN6JrotWyir31+QgH7EvvE5/mwGJlEoci8Fw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.4.tgz", + "integrity": "sha512-JinsFZ5G40oXQb+sUuiA5x689vhr6dDYK0H0NL+rwKdL6CqnmYN8PE4ZwfRSoIjrCxqTQG/SLfTtSvHeGxoVlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.4.tgz", + "integrity": "sha512-GAdA4UxpiNm27cLHr2GqXBpAD0x9FqwYBY7/YSP0Ss0/PNi4k8gbviqpIpYbVSRBaS2ZcegXEzgTQMbRNCwxCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.4.tgz", + "integrity": "sha512-qDd6NoA1znaLjp4jR5U/KWCdLAKDJNB8W9ChbbDaKbo0xA+Atln5HK6LFCZ4oJQpemtRZA288DCirFRjrspptw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.4.tgz", + "integrity": "sha512-WtB5Tz5KTNINb8ZA+8sQ7bmjuS1JrRT7YverYIhUGdWWDlpzVWmIwuZE+jidkEXUn1l0zrEkaIMa8dHF3NGcsA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.4.tgz", + "integrity": "sha512-VcQ3L1tjnkKzWjryAVaFhHEWcqOfICX9uxVVoDzm2t0DpgKRHd2zOpVrJc0xsWeBZcBFyYROCIBdyR/fS174pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.4.tgz", + "integrity": "sha512-6+ZQX6P5s0cMDN2Ypb8Lbm2+/sZYmZjdaYny992ujUU9UKi/4CWoJWsl1pNvjWJHNHGK51m+jKGLlh1ylb2ifQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.4.tgz", + "integrity": "sha512-D72ZnvkFkBXOfzMMQLcwfPLyGkKb7HZ9/mf97B7v6/P5Lbv4oFOtSY/uHbS8lH6uKUOxoKiuokdb50XZSzzbJw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.4.tgz", + "integrity": "sha512-piU6BxeqA3O9KSu3kRCIQQtNqFFaTu21SEV4FwaRZowpnj3bLaWPZHw+xFqCs0XlJ+aOH3PTRWGoglH+mKA/OA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.4.tgz", + "integrity": "sha512-/5PGpHwqt2EEEOUs1XwzubE/ucr0dWDQ+to3zqi4Ds7EWpwtQ79wXc4JBoxqj/OwpawTsKWzJxHfSuBOq3DrWA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.4.tgz", + "integrity": "sha512-cX3beZDLWt7G2oJF+nhChiT+qtaihs+S2xi7ziGmVB+2pwPng6D0Ed0HmElQOgv2UsUmSJJLGwpBao/3TDx3VA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.4.tgz", + "integrity": "sha512-1uz2mGWHyptR7DgHHrlbdRAjXK7v7elGZ9lMja910/RP+ZYbX6xAmCiU9UZSX4hqmgtHMv6lr5l3kq1HIOpcag==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.4.tgz", + "integrity": "sha512-nLS8topojxyz7SRpKR2IODRpQ0XPZ+xaOXvT3+hqK/Uy8Lo5HFgkkIBiIrCu5tL5YqzTvgovGw55PwpahTAGig==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.4.tgz", + "integrity": "sha512-gs7DRKotr3l3q+jGPQBjH0ng1FjlEDm5ueQrkw5JtQvtLyEIcLASqAEaor56BhkKRzk+IcQzrcanBdb/bBQn8g==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.4.tgz", + "integrity": "sha512-791ET7W17NnScOZM7h4dX5hYspxE28htPFsb1awY/NRR8+PRNkS53e475rDdxXXDrP+kwnCcNWg9CX5ztn/Aqw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.4.tgz", + "integrity": "sha512-iwZQRcmj7g88g3tzefIrQY7qvmuA/cfYwhrDtTBhsmukO4U2huVO5W+86XacUMRvdSFVAc6kZUZy21JaRwiB9w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.4.tgz", + "integrity": "sha512-dVHFp9gRWrdTpnqQuGfCwd7hOQDatK1VCP2iWhLY/cGrOQs/ucFzJ6A5SRqbXX12ZDI8EUuejSM5kwg+ja7Png==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.4.tgz", + "integrity": "sha512-t3NlauOW6gxZVVFcBEnO62Cb4wbyDFL416gTg1uFI/2tgqYQlf69FbSE115Ajre9I+c26Lk4mcmdFUsS/DGifQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.4.tgz", + "integrity": "sha512-xWuIaSye5FWZF8+UYtVEcHtRJDN5kN9Kfgxx3Kq8XIov9KSKbc1fiqQCm90SKrgQbUXZelbnUhnlUJmfSE7P9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.4.tgz", + "integrity": "sha512-9ALJJUOg/ZflMJepVo2PlgsGxSaxN7SQ4Z8GoZfVlarWr6r3rkHUNsd/zAio7p4YMtChSMXPionxej4Hkf6CXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.4.tgz", + "integrity": "sha512-blj9z5qx/Pv4WU0W1NMFDB97e0JH5ed+aZGywW8WCvp/NhWX/4PFAq5uu6Q0AebNn+Vo6KzUYDT++JzTT5ojlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.4.tgz", + "integrity": "sha512-Erx822VRBwLa124shbj+wNXe//BOgMEctDV0m1aqTQdNO1S69DgNUCFKC1RCeZfixs1J31l6igk1ziyXErbigQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/canvas-confetti": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", + "integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.28", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/canvas-confetti": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz", + "integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/lucide-vue-next": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.468.0.tgz", + "integrity": "sha512-quV/6T8YB1XK0VOEnebg3Byd8Rsan5/m95cvjnuHV4vcS3qEnLAybkrSh0hk3ppavx+V7R1PjNW+mGDvcBdz4A==", + "deprecated": "Package deprecated. Please use @lucide/vue instead.", + "license": "ISC", + "peerDependencies": { + "vue": ">=3.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.4.tgz", + "integrity": "sha512-4U0liVayNIoLp3GFl1FcI8561WepLnZ1rqfraGh7S9B3Ur5F9S283y8Futii7RUU2C/97tOBmBy7nYvhoiOpbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.4", + "@rollup/rollup-android-arm64": "4.63.4", + "@rollup/rollup-darwin-arm64": "4.63.4", + "@rollup/rollup-darwin-x64": "4.63.4", + "@rollup/rollup-freebsd-arm64": "4.63.4", + "@rollup/rollup-freebsd-x64": "4.63.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.4", + "@rollup/rollup-linux-arm-musleabihf": "4.63.4", + "@rollup/rollup-linux-arm64-gnu": "4.63.4", + "@rollup/rollup-linux-arm64-musl": "4.63.4", + "@rollup/rollup-linux-loong64-gnu": "4.63.4", + "@rollup/rollup-linux-loong64-musl": "4.63.4", + "@rollup/rollup-linux-ppc64-gnu": "4.63.4", + "@rollup/rollup-linux-ppc64-musl": "4.63.4", + "@rollup/rollup-linux-riscv64-gnu": "4.63.4", + "@rollup/rollup-linux-riscv64-musl": "4.63.4", + "@rollup/rollup-linux-s390x-gnu": "4.63.4", + "@rollup/rollup-linux-x64-gnu": "4.63.4", + "@rollup/rollup-linux-x64-musl": "4.63.4", + "@rollup/rollup-openbsd-x64": "4.63.4", + "@rollup/rollup-openharmony-arm64": "4.63.4", + "@rollup/rollup-win32-arm64-msvc": "4.63.4", + "@rollup/rollup-win32-ia32-msvc": "4.63.4", + "@rollup/rollup-win32-x64-gnu": "4.63.4", + "@rollup/rollup-win32-x64-msvc": "4.63.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/projects/sasch40/frontend/package.json b/projects/sasch40/frontend/package.json new file mode 100644 index 0000000..01984ef --- /dev/null +++ b/projects/sasch40/frontend/package.json @@ -0,0 +1,25 @@ +{ + "name": "sasch40-frontend", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "canvas-confetti": "^1.9.4", + "lucide-vue-next": "^0.468.0", + "pinia": "^2.3.0", + "vue": "^3.5.13" + }, + "devDependencies": { + "@types/canvas-confetti": "^1.9.0", + "@types/node": "^22.10.2", + "@vitejs/plugin-vue": "^5.2.1", + "typescript": "^5.7.2", + "vite": "^6.0.3", + "vue-tsc": "^2.2.0" + } +} diff --git a/projects/sasch40/frontend/public/assets/characters/amy/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/amy/portrait.jpg new file mode 100644 index 0000000..48da109 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/amy/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/armend/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/armend/portrait.jpg new file mode 100644 index 0000000..bbe8d73 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/armend/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/hansi/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/hansi/portrait.jpg new file mode 100644 index 0000000..f02d105 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/hansi/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/lutz/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/lutz/portrait.jpg new file mode 100644 index 0000000..6a805ee Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/lutz/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/mane/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/mane/portrait.jpg new file mode 100644 index 0000000..e856595 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/mane/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/mario/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/mario/portrait.jpg new file mode 100644 index 0000000..9fba1fb Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/mario/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/mop/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/mop/portrait.jpg new file mode 100644 index 0000000..7b73d13 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/mop/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/nat/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/nat/portrait.jpg new file mode 100644 index 0000000..8cae767 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/nat/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/pue/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/pue/portrait.jpg new file mode 100644 index 0000000..ecb9c2c Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/pue/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/sascha/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/sascha/portrait.jpg new file mode 100644 index 0000000..c716891 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/sascha/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/characters/verena/portrait.jpg b/projects/sasch40/frontend/public/assets/characters/verena/portrait.jpg new file mode 100644 index 0000000..0411a2e Binary files /dev/null and b/projects/sasch40/frontend/public/assets/characters/verena/portrait.jpg differ diff --git a/projects/sasch40/frontend/public/assets/scenes/tavern.jpg b/projects/sasch40/frontend/public/assets/scenes/tavern.jpg new file mode 100644 index 0000000..497d730 Binary files /dev/null and b/projects/sasch40/frontend/public/assets/scenes/tavern.jpg differ diff --git a/projects/sasch40/frontend/public/assets/ui/crown.svg b/projects/sasch40/frontend/public/assets/ui/crown.svg new file mode 100644 index 0000000..d1ff73d --- /dev/null +++ b/projects/sasch40/frontend/public/assets/ui/crown.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/projects/sasch40/frontend/public/assets/ui/gameboy_frame.jpg b/projects/sasch40/frontend/public/assets/ui/gameboy_frame.jpg new file mode 100644 index 0000000..db5421b Binary files /dev/null and b/projects/sasch40/frontend/public/assets/ui/gameboy_frame.jpg differ diff --git a/projects/sasch40/frontend/public/manifest.json b/projects/sasch40/frontend/public/manifest.json new file mode 100644 index 0000000..bfc28a5 --- /dev/null +++ b/projects/sasch40/frontend/public/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Saschas Kronkorken Kreuzzug", + "short_name": "Sasch40", + "description": "40 Jahre Bass & Gerstensaft – Das epische Thronjubiläum", + "start_url": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#1a0f0a", + "theme_color": "#d4af37", + "icons": [ + { + "src": "/assets/ui/crown.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ] +} diff --git a/projects/sasch40/frontend/src/App.vue b/projects/sasch40/frontend/src/App.vue new file mode 100644 index 0000000..1ac84cc --- /dev/null +++ b/projects/sasch40/frontend/src/App.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/projects/sasch40/frontend/src/audio/synthAudio.ts b/projects/sasch40/frontend/src/audio/synthAudio.ts new file mode 100644 index 0000000..3ceca07 --- /dev/null +++ b/projects/sasch40/frontend/src/audio/synthAudio.ts @@ -0,0 +1,285 @@ +// Procedural 8-bit chiptune & medieval techno sound engine using Web Audio API + +class SoundEngine { + private ctx: AudioContext | null = null + private isMuted: boolean = false + private bgmPlaying: boolean = false + private bgmTimer: number | null = null + private step: number = 0 + + private initCtx() { + if (!this.ctx) { + const AudioCtx = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext + this.ctx = new AudioCtx() + } + if (this.ctx.state === 'suspended') { + this.ctx.resume() + } + } + + public toggleMute(): boolean { + this.isMuted = !this.isMuted + if (this.isMuted) { + this.stopBGM() + } else { + this.startBGM() + } + return this.isMuted + } + + public getMuted(): boolean { + return this.isMuted + } + + // SFX: Kronkorken Collect (bright double ding) + public playCoin() { + if (this.isMuted) return + this.initCtx() + if (!this.ctx) return + + const now = this.ctx.currentTime + const osc = this.ctx.createOscillator() + const gain = this.ctx.createGain() + + osc.type = 'triangle' + osc.frequency.setValueAtTime(987.77, now) // B5 + osc.frequency.setValueAtTime(1318.51, now + 0.08) // E6 + + gain.gain.setValueAtTime(0.2, now) + gain.gain.exponentialRampToValueAtTime(0.001, now + 0.35) + + osc.connect(gain) + gain.connect(this.ctx.destination) + + osc.start(now) + osc.stop(now + 0.35) + } + + // SFX: Sprung / Jump (arcade sweep up) + public playJump() { + if (this.isMuted) return + this.initCtx() + if (!this.ctx) return + + const now = this.ctx.currentTime + const osc = this.ctx.createOscillator() + const gain = this.ctx.createGain() + + osc.type = 'square' + osc.frequency.setValueAtTime(150, now) + osc.frequency.exponentialRampToValueAtTime(600, now + 0.15) + + gain.gain.setValueAtTime(0.15, now) + gain.gain.exponentialRampToValueAtTime(0.01, now + 0.15) + + osc.connect(gain) + gain.connect(this.ctx.destination) + + osc.start(now) + osc.stop(now + 0.15) + } + + // SFX: Beer Gulp / Power-up + public playGulp() { + if (this.isMuted) return + this.initCtx() + if (!this.ctx) return + + const now = this.ctx.currentTime + const osc = this.ctx.createOscillator() + const gain = this.ctx.createGain() + + osc.type = 'sawtooth' + osc.frequency.setValueAtTime(400, now) + osc.frequency.exponentialRampToValueAtTime(180, now + 0.25) + + gain.gain.setValueAtTime(0.2, now) + gain.gain.exponentialRampToValueAtTime(0.01, now + 0.25) + + osc.connect(gain) + gain.connect(this.ctx.destination) + + osc.start(now) + osc.stop(now + 0.25) + } + + // SFX: Dialog blip (Monkey Island text chatter) + public playDialogueBlip() { + if (this.isMuted) return + this.initCtx() + if (!this.ctx) return + + const now = this.ctx.currentTime + const osc = this.ctx.createOscillator() + const gain = this.ctx.createGain() + + osc.type = 'sine' + const freqs = [320, 360, 420, 480] + const randomFreq = freqs[Math.floor(Math.random() * freqs.length)] + osc.frequency.setValueAtTime(randomFreq, now) + + gain.gain.setValueAtTime(0.08, now) + gain.gain.exponentialRampToValueAtTime(0.001, now + 0.05) + + osc.connect(gain) + gain.connect(this.ctx.destination) + + osc.start(now) + osc.stop(now + 0.05) + } + + // SFX: Fanfare / Royal Crowning + public playFanfare() { + if (this.isMuted) return + this.initCtx() + if (!this.ctx) return + + const notes = [261.63, 329.63, 392.00, 523.25] // C, E, G, C + notes.forEach((freq, idx) => { + if (!this.ctx) return + const now = this.ctx.currentTime + idx * 0.12 + const osc = this.ctx.createOscillator() + const gain = this.ctx.createGain() + + osc.type = 'triangle' + osc.frequency.setValueAtTime(freq, now) + + gain.gain.setValueAtTime(0.25, now) + gain.gain.exponentialRampToValueAtTime(0.001, now + (idx === 3 ? 0.8 : 0.2)) + + osc.connect(gain) + gain.connect(this.ctx.destination) + + osc.start(now) + osc.stop(now + (idx === 3 ? 0.8 : 0.25)) + }) + } + + // 135 BPM Medieval Techno Chiptune Beat + public startBGM() { + if (this.isMuted || this.bgmPlaying) return + this.initCtx() + this.bgmPlaying = true + + // 135 BPM = 135 beats/min = 2.25 beats/sec -> 16th note = 60 / (135 * 4) = 0.111s + const stepDuration = (60 / 135) / 2 // 8th note = 0.222s + this.step = 0 + + // Medieval Dorian melody scale (D, E, F, G, A, B, C) + const melody = [ + 293.66, 0, 349.23, 392.00, // D4, -, F4, G4 + 440.00, 0, 392.00, 349.23, // A4, -, G4, F4 + 392.00, 440.00, 523.25, 440.00, // G4, A4, C5, A4 + 349.23, 0, 293.66, 0 // F4, -, D4, - + ] + + const bassline = [ + 146.83, 146.83, 146.83, 146.83, // D3 + 174.61, 174.61, 174.61, 174.61, // F3 + 196.00, 196.00, 196.00, 196.00, // G3 + 146.83, 146.83, 220.00, 146.83 // D3, A3 + ] + + const playLoop = () => { + if (!this.bgmPlaying || !this.ctx) return + const now = this.ctx.currentTime + + // 4-to-the-floor Techno Kick on every even step (quarter note) + if (this.step % 2 === 0) { + const kickOsc = this.ctx.createOscillator() + const kickGain = this.ctx.createGain() + kickOsc.frequency.setValueAtTime(140, now) + kickOsc.frequency.exponentialRampToValueAtTime(35, now + 0.1) + kickGain.gain.setValueAtTime(0.35, now) + kickGain.gain.exponentialRampToValueAtTime(0.001, now + 0.18) + kickOsc.connect(kickGain) + kickGain.connect(this.ctx.destination) + kickOsc.start(now) + kickOsc.stop(now + 0.18) + } + + // Hi-Hat / Shaker on offbeats + if (this.step % 2 === 1) { + const hatNode = this.ctx.createBufferSource() + const bufferSize = this.ctx.sampleRate * 0.04 + const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate) + const data = buffer.getChannelData(0) + for (let i = 0; i < bufferSize; i++) { + data[i] = Math.random() * 2 - 1 + } + hatNode.buffer = buffer + + const hatFilter = this.ctx.createBiquadFilter() + hatFilter.type = 'highpass' + hatFilter.frequency.value = 7000 + + const hatGain = this.ctx.createGain() + hatGain.gain.setValueAtTime(0.08, now) + hatGain.gain.exponentialRampToValueAtTime(0.001, now + 0.04) + + hatNode.connect(hatFilter) + hatFilter.connect(hatGain) + hatGain.connect(this.ctx.destination) + hatNode.start(now) + } + + // Bassline (sawtooth filtered) + const bassFreq = bassline[this.step % bassline.length] + if (bassFreq > 0) { + const bassOsc = this.ctx.createOscillator() + const bassGain = this.ctx.createGain() + const bassFilter = this.ctx.createBiquadFilter() + + bassOsc.type = 'sawtooth' + bassOsc.frequency.setValueAtTime(bassFreq, now) + + bassFilter.type = 'lowpass' + bassFilter.frequency.setValueAtTime(450, now) + + bassGain.gain.setValueAtTime(0.12, now) + bassGain.gain.exponentialRampToValueAtTime(0.001, now + 0.2) + + bassOsc.connect(bassFilter) + bassFilter.connect(bassGain) + bassGain.connect(this.ctx.destination) + + bassOsc.start(now) + bassOsc.stop(now + 0.2) + } + + // Medieval Lute Lead Melody + const melFreq = melody[this.step % melody.length] + if (melFreq > 0) { + const melOsc = this.ctx.createOscillator() + const melGain = this.ctx.createGain() + + melOsc.type = 'square' + melOsc.frequency.setValueAtTime(melFreq, now) + + melGain.gain.setValueAtTime(0.08, now) + melGain.gain.exponentialRampToValueAtTime(0.001, now + 0.18) + + melOsc.connect(melGain) + melGain.connect(this.ctx.destination) + + melOsc.start(now) + melOsc.stop(now + 0.18) + } + + this.step++ + this.bgmTimer = window.setTimeout(playLoop, stepDuration * 1000) + } + + playLoop() + } + + public stopBGM() { + this.bgmPlaying = false + if (this.bgmTimer) { + clearTimeout(this.bgmTimer) + this.bgmTimer = null + } + } +} + +export const sound = new SoundEngine() diff --git a/projects/sasch40/frontend/src/components/CertificateModal.vue b/projects/sasch40/frontend/src/components/CertificateModal.vue new file mode 100644 index 0000000..f727e76 --- /dev/null +++ b/projects/sasch40/frontend/src/components/CertificateModal.vue @@ -0,0 +1,434 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/GameBoyFrame.vue b/projects/sasch40/frontend/src/components/GameBoyFrame.vue new file mode 100644 index 0000000..584012f --- /dev/null +++ b/projects/sasch40/frontend/src/components/GameBoyFrame.vue @@ -0,0 +1,558 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/IntroView.vue b/projects/sasch40/frontend/src/components/IntroView.vue new file mode 100644 index 0000000..8c6e29e --- /dev/null +++ b/projects/sasch40/frontend/src/components/IntroView.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/PartyExpressView.vue b/projects/sasch40/frontend/src/components/PartyExpressView.vue new file mode 100644 index 0000000..5aef6d6 --- /dev/null +++ b/projects/sasch40/frontend/src/components/PartyExpressView.vue @@ -0,0 +1,258 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/PlatformerView.vue b/projects/sasch40/frontend/src/components/PlatformerView.vue new file mode 100644 index 0000000..74bd86b --- /dev/null +++ b/projects/sasch40/frontend/src/components/PlatformerView.vue @@ -0,0 +1,542 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/PrologueView.vue b/projects/sasch40/frontend/src/components/PrologueView.vue new file mode 100644 index 0000000..4549340 --- /dev/null +++ b/projects/sasch40/frontend/src/components/PrologueView.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/projects/sasch40/frontend/src/components/TavernView.vue b/projects/sasch40/frontend/src/components/TavernView.vue new file mode 100644 index 0000000..eba82e0 --- /dev/null +++ b/projects/sasch40/frontend/src/components/TavernView.vue @@ -0,0 +1,605 @@ + + + + + diff --git a/projects/sasch40/frontend/src/config/characters.ts b/projects/sasch40/frontend/src/config/characters.ts new file mode 100644 index 0000000..517860a --- /dev/null +++ b/projects/sasch40/frontend/src/config/characters.ts @@ -0,0 +1,129 @@ +export interface CharacterConfig { + id: string + name: string + title: string + portrait: string + description: string + famousQuote: string + themeColor: string +} + +export const CHARACTERS: Record = { + sascha: { + id: 'sascha', + name: 'König Sascha I.', + title: 'Der 40-jährige Imperator', + portrait: '/assets/characters/sascha/portrait.jpg', + description: 'Herrscher über Haumdaucher, Träger der heiligen Sonnenbrille und Beschützer des 135-BPM-Basses.', + famousQuote: 'O zapft is im Reiche! Wo san meine Kronkorken?!', + themeColor: '#d4af37' + }, + mop: { + id: 'mop', + name: 'Mop (Moritz)', + title: 'Der Welten-Erschaffer & Matrix-Architekt', + portrait: '/assets/characters/mop/portrait.jpg', + description: 'Sitzt auf einer Wolke über dem Schloss und baut aus Pixeln und Hopfen diese Welt.', + famousQuote: 'Just adding a few more polygons here... wait, pixels. Pixels.', + themeColor: '#38bdf8' + }, + nat: { + id: 'nat', + name: 'NAT (Josef)', + title: 'Hof-Chronist & Barde des Wildschweins', + portrait: '/assets/characters/nat/portrait.jpg', + description: 'Kennt alle 3 Staffeln Bridgerton in- und auswendig, trägt florale Bomberjacken und erzählt epische Geschichten.', + famousQuote: 'Also pass auf Sascha... ich damals mit meinen Girls ab...', + themeColor: '#22c55e' + }, + verena: { + id: 'verena', + name: 'Königin Verena', + title: 'Herrin des Hofes', + portrait: '/assets/characters/verena/portrait.jpg', + description: 'Liebt Sascha über alles auf der Welt, hat aber ein unumstößliches königliches Edikt erlassen.', + famousQuote: 'Ich hab dich lieb mein Schatz, aber ich putze nach der Party fei ned!', + themeColor: '#ec4899' + }, + hansi: { + id: 'hansi', + name: 'Hansi (Johann)', + title: 'Ritter der floralen Garde', + portrait: '/assets/characters/hansi/portrait.jpg', + description: 'Perfekt gestylter Schnurrbart und florale Plattenrüstung.', + famousQuote: 'Habe die Ehre! Der Bass im Rittersaal ist auf Punkt.', + themeColor: '#6366f1' + }, + lutz: { + id: 'lutz', + name: 'Lutz', + title: 'Der ZTUL-Hofschornsteinfeger', + portrait: '/assets/characters/lutz/portrait.jpg', + description: 'Ein Hüne von Gestalt mit Kappe, runder Brille und dem legendären ZTUL-Trikot.', + famousQuote: 'Glück auf, Euer Durchlaucht! Schornstein ist frei für den Party-Nebel.', + themeColor: '#64748b' + }, + armend: { + id: 'armend', + name: 'Armend', + title: 'Meister-Schmied der Kronkorken-Manufaktur', + portrait: '/assets/characters/armend/portrait.jpg', + description: 'Prachtvoller Vollbart, formt aus 40 Kronkorken den goldenen Sound-Verstärker.', + famousQuote: 'Schlag zu, solange das Blech warm ist!', + themeColor: '#f97316' + }, + amy: { + id: 'amy', + name: 'Amy (Amelie)', + title: 'Die Hof-Künstlerin', + portrait: '/assets/characters/amy/portrait.jpg', + description: 'Kreatives Genie in Latzhose mit Dutt und feinen Kräuter-Zigarren.', + famousQuote: 'Rave the Kingdom! Die Farben brauchen mehr Bass!', + themeColor: '#a855f7' + }, + pue: { + id: 'pue', + name: 'Pü (Jakob)', + title: 'Der Lichtler des Hofes', + portrait: '/assets/characters/pue/portrait.jpg', + description: 'Früherer Club-Lichtmeister, bedient nun die magischen Stroboskop-Fackeln.', + famousQuote: 'Majestät, wann zünden wir die Stroboskop-Fackeln zum Drop?', + themeColor: '#eab308' + }, + mario: { + id: 'mario', + name: 'Mario der Gaukler', + title: 'Hof-Jongleur & Stimmungs-Garant', + portrait: '/assets/characters/mario/portrait.jpg', + description: 'Jongliert geschickt mit Hopfen, Hefe und Bierkrügen.', + famousQuote: 'Hoppla die Waldfee! Ein Krug für den König!', + themeColor: '#14b8a6' + }, + mane: { + id: 'mane', + name: 'Mane (Ramona)', + title: 'Die königliche Wanderin', + portrait: '/assets/characters/mane/portrait.jpg', + description: 'Wandert random mit Rucksack und Wanderstock quer durch alle Bildschirme.', + famousQuote: 'Schee ists hier! Noch 3 Kilometer bis zum Gipfel-Bier!', + themeColor: '#84cc16' + }, + djs: { + id: 'djs', + name: 'Die Hof-DJs', + title: 'Minnesänger der 135 BPM', + portrait: '/assets/characters/pue/portrait.jpg', + description: 'Stehen an der königlichen Kanzel am doppelten Eichenholz-Plattenteller.', + famousQuote: 'Drop it like it\'s 1426! Mehr Bass auf die Burgzinnen!', + themeColor: '#06b6d4' + }, + gefolge: { + id: 'gefolge', + name: 'Das Haumdaucher Gefolge', + title: 'Die feiernde Reichs-Crowd', + portrait: '/assets/characters/hansi/portrait.jpg', + description: 'Viktor der Vampir, der bärtige Guatemalteke, Fabian, Albin, Maier Flo, Markus & Nathalie.', + famousQuote: 'Ein Hoch auf König Sascha! Hoch soll er leben!', + themeColor: '#e11d48' + } +} diff --git a/projects/sasch40/frontend/src/config/dialogues.ts b/projects/sasch40/frontend/src/config/dialogues.ts new file mode 100644 index 0000000..06f1911 --- /dev/null +++ b/projects/sasch40/frontend/src/config/dialogues.ts @@ -0,0 +1,106 @@ +export interface DialogueChoice { + text: string + nextId?: string + action?: 'addKronkorken' | 'unlockZuluman' | 'unlockBridgerton' | 'startPlatformer' | 'startExpress' | 'startFinale' + reward?: number + reply?: { + speakerId: string + text: string + } +} + +export interface DialogueNode { + id: string + speakerId: string + text: string + choices: DialogueChoice[] +} + +export const PROLOGUE_DIALOGUES: Record = { + start: { + id: 'start', + speakerId: 'nat', + text: 'Seyd gegrüßet, Sascha! 40 Lenze zählet dein edles Antlitz! Aber ich sag\'s dir wie\'s is... der Hofstaat ist in heller Aufruhr. Ein Drama wie damals beim königlichen Ball in Staffel 2 von Bridgerton! Sämtliche 40 königlichen Kronkorken san über ganz Haumdaucher verstreut!', + choices: [ + { + text: 'Zefix Josef! Ohne Kronkorken kein Bier, und ohne Bier kein Bass!', + nextId: 'mop_appears', + action: 'unlockBridgerton' + }, + { + text: 'Bridgerton?! Josef, konzentrier dich! Wo san meine 40 Kronkorken?!', + nextId: 'mop_appears' + }, + { + text: 'Lass mich raten... Du mit deinen Girls ab und zack, Leergut weg?', + nextId: 'nat_girls', + action: 'addKronkorken', + reward: 2 + } + ] + }, + nat_girls: { + id: 'nat_girls', + speakerId: 'nat', + text: 'Ja mei, woher weißt du das immer?! Wir zogen durch die Lande, die Musik war laut, der Gerstensaft floss in Strömen... und dann hat der Wind die Korken über die Dächer geweht!', + choices: [ + { + text: 'Na servus. Dann muss der König wohl selbst ran.', + nextId: 'mop_appears' + } + ] + }, + mop_appears: { + id: 'mop_appears', + speakerId: 'mop', + text: 'Habe die Ehre, Sascha! Keine Panik, dein Erschaffer hat schon die Sprungfedern in deine königlichen Stiefel programmiert. Sammle mindestens 15 Kronkorken auf den Hopfen-Pfaden, bevor wir in die Taverne einziehen!', + choices: [ + { + text: 'Auf geht\'s Mop! Starte den Kronkorken-Kreuzzug!', + action: 'startPlatformer' + }, + { + text: 'Zeig mir erst noch meine Sonnenbrille für den Style.', + nextId: 'sascha_ready' + } + ] + }, + sascha_ready: { + id: 'sascha_ready', + speakerId: 'sascha', + text: 'Sonnenbrille sitzt. Hermelinmantel weht im Wind. Haumdaucher, der König kommt!', + choices: [ + { + text: 'Level 1 starten: Die Hopfen-Pfade!', + action: 'startPlatformer' + } + ] + } +} + +export const GSTANZL_DUEL = [ + { + insult: 'Dein Techno-Bass ist so dünn wie schales Exportbier!', + answers: [ + { text: 'Dafür schallert mein Drop lauter als dei ganze Ahnengalerie!', correct: true }, + { text: 'Ich trinke eigentlich lieber Weißwein.', correct: false }, + { text: 'Aber meine Sonnenbrille ist dafür sehr dunkel!', correct: false } + ] + }, + { + insult: 'Mit 40 Lenzen bist du doch reif fürs Altenteil, alter Mann!', + answers: [ + { text: 'Mit 40 fängt der Rave erst an, und i tanz di locker an die Wand!', correct: true }, + { text: 'Ja mei, der Rücken zwickt schon a bisserl.', correct: false }, + { text: 'Ich frage mal schnell meinen Steuerberater.', correct: false } + ] + }, + { + insult: 'Niemand im ganzen Herzogtum feiert deine 135 BPM!', + answers: [ + { text: 'Ganz Haumdaucher bebt, wenn der König den Fader bewegt!', correct: true }, + { text: 'Vielleicht sollte ich lieber Schlager spielen.', correct: false }, + { text: 'Pü hat aber gesagt, das Licht passt dazu.', correct: false } + ] + } +] diff --git a/projects/sasch40/frontend/src/main.ts b/projects/sasch40/frontend/src/main.ts new file mode 100644 index 0000000..a2cedb6 --- /dev/null +++ b/projects/sasch40/frontend/src/main.ts @@ -0,0 +1,8 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import './style.css' +import App from './App.vue' + +const app = createApp(App) +app.use(createPinia()) +app.mount('#app') diff --git a/projects/sasch40/frontend/src/stores/gameStore.ts b/projects/sasch40/frontend/src/stores/gameStore.ts new file mode 100644 index 0000000..71e2b7a --- /dev/null +++ b/projects/sasch40/frontend/src/stores/gameStore.ts @@ -0,0 +1,150 @@ +import { defineStore } from 'pinia' +import { sound } from '../audio/synthAudio' + +export type GameAct = 'intro' | 'prologue' | 'platformer' | 'tavern' | 'party_express' | 'finale' + +export interface GameState { + currentAct: GameAct + kronkorken: number + beers: number + guestsServed: number + gstanzlWon: boolean + partyMood: number + inventory: string[] + unlockedEasterEggs: string[] + startTime: number + elapsedSeconds: number + soundMuted: boolean +} + +export const useGameStore = defineStore('game', { + state: (): GameState => ({ + currentAct: 'intro', + kronkorken: 0, + beers: 0, + guestsServed: 0, + gstanzlWon: false, + partyMood: 20, + inventory: ['Königliche Sonnenbrille', 'Leergutsack'], + unlockedEasterEggs: [], + startTime: Date.now(), + elapsedSeconds: 0, + soundMuted: false, + }), + + getters: { + isArcadeMode(state): boolean { + return state.currentAct === 'platformer' || state.currentAct === 'party_express' + }, + + totalScore(state): number { + const capPoints = state.kronkorken * 100 + const beerPoints = state.beers * 250 + const guestPoints = state.guestsServed * 300 + const gstanzlBonus = state.gstanzlWon ? 1500 : 0 + const eggBonus = state.unlockedEasterEggs.length * 500 + const completionBonus = 2000 + return capPoints + beerPoints + guestPoints + gstanzlBonus + eggBonus + completionBonus + }, + + scoreRank(): { rank: string; title: string; color: string } { + const score = this.totalScore + if (score >= 15000) { + return { + rank: 'S-RANG (LEGENDÄR)', + title: 'Göttlicher Imperator des 135-BPM-Schalldrucks', + color: '#ffd700' + } + } else if (score >= 12000) { + return { + rank: 'A-RANG (HERZOGLICH)', + title: 'Großfürst des Gerstensafts & Rave-Legende', + color: '#38bdf8' + } + } else if (score >= 9000) { + return { + rank: 'B-RANG (RITTERLICH)', + title: 'Ehren-Ritter der Hopfen-Manufaktur', + color: '#4ade80' + } + } else { + return { + rank: 'C-RANG (KNAPPE)', + title: 'Durstiger Party-Knappe von Haumdaucher', + color: '#fbbf24' + } + } + }, + + titleAward(state): string { + return this.scoreRank.title + } + }, + + actions: { + addKronkorken(amount: number = 1) { + this.kronkorken += amount + this.partyMood = Math.min(100, this.partyMood + 2 * amount) + sound.playCoin() + }, + + addBeer() { + this.beers++ + this.partyMood = Math.min(100, this.partyMood + 10) + sound.playGulp() + }, + + serveGuest() { + this.guestsServed++ + this.addBeer() + this.addKronkorken(3) + }, + + setGstanzlWon() { + this.gstanzlWon = true + this.addKronkorken(15) + }, + + addItem(item: string) { + if (!this.inventory.includes(item)) { + this.inventory.push(item) + } + }, + + unlockEasterEgg(egg: string) { + if (!this.unlockedEasterEggs.includes(egg)) { + this.unlockedEasterEggs.push(egg) + this.addKronkorken(5) + } + }, + + setAct(act: GameAct) { + this.currentAct = act + if (act === 'finale') { + sound.playFanfare() + } + }, + + startGame() { + this.currentAct = 'prologue' + sound.startBGM() + }, + + toggleMute() { + this.soundMuted = sound.toggleMute() + }, + + resetGame() { + this.currentAct = 'intro' + this.kronkorken = 0 + this.beers = 0 + this.guestsServed = 0 + this.gstanzlWon = false + this.partyMood = 20 + this.inventory = ['Königliche Sonnenbrille', 'Leergutsack'] + this.unlockedEasterEggs = [] + this.startTime = Date.now() + this.elapsedSeconds = 0 + } + } +}) diff --git a/projects/sasch40/frontend/src/style.css b/projects/sasch40/frontend/src/style.css new file mode 100644 index 0000000..9097e34 --- /dev/null +++ b/projects/sasch40/frontend/src/style.css @@ -0,0 +1,28 @@ +:root { + font-family: 'MedievalSharp', serif, system-ui; + line-height: 1.5; + font-weight: 400; + color-scheme: dark; + background-color: #0f0a07; + color: #fef3c7; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; +} + +body, html { + width: 100%; + height: 100%; + overflow: hidden; + touch-action: manipulation; +} + +button { + user-select: none; + outline: none; +} diff --git a/projects/sasch40/frontend/tsconfig.json b/projects/sasch40/frontend/tsconfig.json new file mode 100644 index 0000000..30ac41f --- /dev/null +++ b/projects/sasch40/frontend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/projects/sasch40/frontend/vite.config.ts b/projects/sasch40/frontend/vite.config.ts new file mode 100644 index 0000000..5981464 --- /dev/null +++ b/projects/sasch40/frontend/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import path from 'path' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + port: 3000, + host: true, + } +}) diff --git a/projects/sasch40/source_photos/amelie.jpg b/projects/sasch40/source_photos/amelie.jpg new file mode 100644 index 0000000..2dd5137 Binary files /dev/null and b/projects/sasch40/source_photos/amelie.jpg differ diff --git a/projects/sasch40/source_photos/armend.jpg b/projects/sasch40/source_photos/armend.jpg new file mode 100644 index 0000000..66805ba Binary files /dev/null and b/projects/sasch40/source_photos/armend.jpg differ diff --git a/projects/sasch40/source_photos/lutz.jpg b/projects/sasch40/source_photos/lutz.jpg new file mode 100644 index 0000000..6a647c7 Binary files /dev/null and b/projects/sasch40/source_photos/lutz.jpg differ diff --git a/projects/sasch40/source_photos/lutz2.jpg b/projects/sasch40/source_photos/lutz2.jpg new file mode 100644 index 0000000..1a9f360 Binary files /dev/null and b/projects/sasch40/source_photos/lutz2.jpg differ diff --git a/projects/sasch40/source_photos/moritz2.jpg b/projects/sasch40/source_photos/moritz2.jpg new file mode 100644 index 0000000..d44bc59 Binary files /dev/null and b/projects/sasch40/source_photos/moritz2.jpg differ diff --git a/projects/sasch40/source_photos/nat.jpg b/projects/sasch40/source_photos/nat.jpg new file mode 100644 index 0000000..978d660 Binary files /dev/null and b/projects/sasch40/source_photos/nat.jpg differ diff --git a/projects/sasch40/source_photos/nat_verena_hansi.jpg b/projects/sasch40/source_photos/nat_verena_hansi.jpg new file mode 100644 index 0000000..b874367 Binary files /dev/null and b/projects/sasch40/source_photos/nat_verena_hansi.jpg differ diff --git a/projects/sasch40/source_photos/sascha_beanie.jpg b/projects/sasch40/source_photos/sascha_beanie.jpg new file mode 100644 index 0000000..4356549 Binary files /dev/null and b/projects/sasch40/source_photos/sascha_beanie.jpg differ diff --git a/projects/sasch40/source_photos/sascha_grill.jpg b/projects/sasch40/source_photos/sascha_grill.jpg new file mode 100644 index 0000000..33a6f8d Binary files /dev/null and b/projects/sasch40/source_photos/sascha_grill.jpg differ diff --git a/projects/sasch40/source_photos/sascha_hoodie.jpg b/projects/sasch40/source_photos/sascha_hoodie.jpg new file mode 100644 index 0000000..01f7891 Binary files /dev/null and b/projects/sasch40/source_photos/sascha_hoodie.jpg differ diff --git a/projects/sasch40/source_photos/sascha_king_original.jpg b/projects/sasch40/source_photos/sascha_king_original.jpg new file mode 100644 index 0000000..eb47614 Binary files /dev/null and b/projects/sasch40/source_photos/sascha_king_original.jpg differ diff --git a/projects/sasch40/source_photos/sascha_purple.jpg b/projects/sasch40/source_photos/sascha_purple.jpg new file mode 100644 index 0000000..9a297f3 Binary files /dev/null and b/projects/sasch40/source_photos/sascha_purple.jpg differ