diff --git a/GEMINI.md b/GEMINI.md index adcfb14..2a310c6 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -3,59 +3,58 @@ ## 🚨 Rules **1. Infrastructure as Code is rule #1.** Manual creation of resources (e.g., via `gcloud` or Console) is forbidden. The use of Terraform/Tofu is mandatory. +**2. Agental Protocol (Git & Deployment):** +- Never perform `git add` or `./deploy.sh` before the user has formally accepted the **Walkthrough** artifact. +- Once accepted, the agent is responsible for committing changes and triggering the deployment script. + This document serves as the "Source of Truth" for the Haumdaucher website. It outlines the design principles, technical architecture, and specialized features to ensure consistent future development. ## 🦢 Project Essence -**Haumdaucher** is a community project from Regensburg, Germany. The website is designed to be humorous, culturally rich (Bavarian), and technically "surprising." +**Haumdaucher** is a community project from Regensburg, Germany. The website represents the "HAUMDAUCHER Wurst und Spezialitäten n.e.V." (nicht eingetragener Verein). It is designed to be humorous, culturally rich, and technically "surprising." ## 🎨 Design Principles - **Vibrant Aesthetics**: Each theme must feel like a completely different app. - **Glassmorphism**: Use `backdrop-filter` and semi-transparent backgrounds for a premium feel. -- **Micro-interactions**: Subtle entrance animations (logo spin, hero text slide) and consistent hover states. +- **Micro-interactions**: Subtle entrance animations and consistent hover states. - **Accessibility**: Mobile-first design with safe-area support for PWA usage on iOS/Android. +## ⚖️ Legal & Compliance (n.e.V.) +Maintaining the legal section is critical for German compliance (§ 5 DDG). +- **Entity Type**: The club is a **nicht eingetragener Verein (n.e.V.)**. Do NOT add Registry data (Registergericht/Nummer). +- **Mandatory Impressum Fields**: + - Full Name: `HAUMDAUCHER Wurst und Spezialitäten n.e.V.` + - Vertretungsberechtigter: `Moritz Graf (1. Vorstand)` + - Ladungsfähige Anschrift: `Grabengasse 7, 93059 Regensburg` + - Kontakt: `Telefon: 094183065717, E-Mail: info@haumdaucher.de` +- **Privacy (DSGVO)**: The website uses **Google Firebase Authentication**. The privacy policy must disclose the data processing by Google Ireland Limited and the US data transfer details. + ## 🛠 Technical Specifications - **Framework**: Vue 3 (Composition API) + Vite + TypeScript. -- **State Management**: Centralized in `App.vue` using standard `ref` hooks. Persisted in `localStorage`. +- **State Management**: Centralized in `App.vue` using standard `ref` hooks. - **Theming System**: - Driven by `data-theme` attribute on `:root`. - Defined in `src/assets/styles/global.css`. - - Themes: `Classic`, `Unicorn`, `Luxury`, `Win95`, `NAT`. + - Themes: `Classic` (Light), `Dark` (Premium Charcoal/Gold), `NAT` (Boar Easter Egg). - **Localization**: - Centralized in `src/locales/i18n.ts`. - - Supports `de` (Standard German) and `bar` (Bavarian Dialect). -- **PWA**: - - Managed via `vite-plugin-pwa`. - - Custom icons and standalone manifest for "Add to Home Screen" support. - - -## 🧪 Testing -- **Framework**: Vitest + HappyDOM. -- **Scope**: Lightweight sanity checks (e.g., verifying App mount). -- **Commands**: - - `npm test`: Run tests in watch mode. - - `npm test -- --run`: Run tests once (CI mode). - -## 🕹 The Haumdaucher Game -- **Engine**: HTML5 Canvas rendering. -- **Controls**: Touch-responsive (horizontal drag) and Keyboard (Arrow Keys). -- **Thematization**: The game visual style (backgrounds, player, obstacles) changes dynamically based on the site's active theme. -- **Difficulty**: Balanced (10 levels). Level 10 triggers a "Boar Rain" supermode. - -## 🔐 Progression & Gating -- **NAT Mode**: This theme is locked by default to maintain the "collectible" feel. -- **Unlocking**: - - **Natural**: Reach Level 10 in the game. - - **Backdoor**: Single 1x1 pixel in the bottom-left corner of the site. Clicking it triggers a prompt. Type `nat mode` to unlock. + - Language: `de` (Standard German) only. ## 🚢 Deployment & DevOps -- **Docker**: Dual-stage build (Node build -> Nginx serving). -- **Registry**: `registry.haumdaucher.de`. -- **Kubernetes**: - - Managed via `k8s-manifests.yaml`. - - Features `cert-manager` for SSL and `registry-haumdaucher-de` pull secret. -- **CI/CD Logic**: The `deploy.sh` script handles builds, pushes, and triggers a `kubectl rollout restart` to force deployment updates when utilizing the `latest` image tag. +Deployment is automated via the `./deploy.sh` script. + +**Workflow Details:** +1. **Cloud Sync**: Script ensures the `haumdaucher` namespace exists in Kubernetes. +2. **Config Extraction**: Fetches Firebase credentials directly from Terraform outputs (`terraform output -json firebase_config`). +3. **Build Pipeline**: Docker build passes Firebase credentials as `--build-arg` (VITE_FIREBASE_*). +4. **Distribution**: Pushes the image to `registry.haumdaucher.de/haumdaucher-website:latest`. +5. **K8s Update**: Applies `k8s-manifests.yaml` and triggers a `kubectl rollout restart` to fetch the new image. + +## 🕹 The Haumdaucher Game +- **Engine**: HTML5 Canvas rendering. Game style changes dynamically based on the site's active theme. +- **Unlocking NAT Mode**: + - **Natural**: Reach Level 10. + - **Backdoor**: Single 1x1 pixel in the bottom-left corner. Type `nat mode` into the prompt. ## 📝 Ongoing Maintenance -- **Assets**: Static images should be placed in `public/images/` to avoid bundling issues in production containers. +- **Assets**: Static images should be placed in `public/images/`. - **Style Overrides**: Mobile-first approach is mandatory. Always test with `max-width: 375px`. diff --git a/src/App.vue b/src/App.vue index 59419cb..24392dd 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,9 +12,7 @@ import { useAuth } from './composables/useAuth' const { isAllowed } = useAuth() const theme = ref('classic') -const lang = ref<'de' | 'bar'>('de') const showGame = ref(false) -const showBSOD = ref(false) const isNatUnlocked = ref(false) const boars = ref<{id: number, top: number}[]>([]) @@ -34,7 +32,7 @@ const unlockNat = () => { } const handleBackdoor = () => { - const secret = prompt('Bitte Geheimcode eigem (Unlock NAT):') + const secret = prompt('Bitte Geheimcode eingeben (Unlock NAT):') if (secret?.toLowerCase() === 'nat mode') { unlockNat() alert('🐗 NAT-Modus freigschaltet! Wiedaschaun, reinghaun!') @@ -53,32 +51,18 @@ const startBoarRun = () => { }, 8000) } -const triggerBSOD = () => { - if (theme.value === 'win95') { - showBSOD.value = true - setTimeout(() => showBSOD.value = false, 3000) - } -} - -const toggleLang = (newLang: 'de' | 'bar') => { - lang.value = newLang - localStorage.setItem('haumdaucher-lang', newLang) -} - onMounted(() => { const savedTheme = localStorage.getItem('haumdaucher-theme') if (savedTheme) toggleTheme(savedTheme) - const savedLang = localStorage.getItem('haumdaucher-lang') as 'de' | 'bar' - if (savedLang) lang.value = savedLang - const savedNat = localStorage.getItem('haumdaucher-nat-unlocked') if (savedNat === 'true') isNatUnlocked.value = true }) +// Simplified translation: German only const t = (key: string) => { const keys = key.split('.') - let result: any = messages[lang.value] + let result: any = messages.de for (const k of keys) { if (result[k]) result = result[k] else return key @@ -90,25 +74,24 @@ const t = (key: string) => { @@ -137,19 +123,6 @@ const t = (key: string) => { diff --git a/src/assets/styles/global.css b/src/assets/styles/global.css index 9a4cff2..3f7605c 100644 --- a/src/assets/styles/global.css +++ b/src/assets/styles/global.css @@ -42,10 +42,9 @@ body { .container { width: 100%; - max-width: 100%; - margin: 0; - padding: 0 10px; - /* Reduced from 15px to save space */ + max-width: 1000px; /* Centered content on desktop */ + margin: 0 auto; + padding: 0 20px; box-sizing: border-box; } @@ -94,43 +93,14 @@ p { max-width: 100%; } -/* Theme Overrides (Remain same but refined) */ -[data-theme='unicorn'] { - --bg-color: #fff0fb; - --text-color: #4a148c; - --primary-color: #f06292; - --accent-color: #ba68c8; - --header-bg: rgba(255, 240, 251, 0.85); - --glass-border: rgba(240, 98, 146, 0.2); -} - -[data-theme='luxury'] { - --bg-color: #0a0a0a; - --text-color: #e0e0e0; +/* Theme Overrides */ +[data-theme='dark'] { + --bg-color: #0f0f0f; + --text-color: #f0f0f0; --primary-color: #d4af37; - --accent-color: #e5e4e2; - --header-bg: rgba(10, 10, 10, 0.85); - --glass-border: rgba(212, 175, 55, 0.3); -} - -[data-theme='win95'] { - --bg-color: #008080; - --text-color: #000000; - --primary-color: #c0c0c0; - --accent-color: #808080; - --header-bg: #c0c0c0; - --glass-border: #ffffff; - --font-family: 'MS Sans Serif', Tahoma, sans-serif; - cursor: url('https://cur.cursors-4u.net/games/gam-4/gam373.cur'), auto; -} - -[data-theme='win95'] * { - border-radius: 0 !important; -} - -[data-theme='win95'] .fancy-glass { - border: 2px solid; - border-color: #ffffff #808080 #808080 #ffffff; + --accent-color: #ffffff; + --header-bg: rgba(15, 15, 15, 0.85); + --glass-border: rgba(212, 175, 55, 0.2); } [data-theme='nat'] { diff --git a/src/components/layout/Header.vue b/src/components/layout/Header.vue index 1a5de52..ac99943 100644 --- a/src/components/layout/Header.vue +++ b/src/components/layout/Header.vue @@ -1,17 +1,17 @@