# Nimuli — Handoff für neue Claude-Session **Datum:** 2026-05-16 **Repo:** https://git.nexlab.at/boban/nimuli.git **VM:** Debian 13, IP 10.10.90.142, User: nexxo (sudo) **Spec:** docs/superpowers/specs/2026-05-15-nimuli-design.md --- ## Aktueller Stand ### Was läuft - Docker-Compose hoch (nginx, app, queue, scheduler, reverb, mysql, redis, mailpit) - Laravel 12 + Livewire v3 + Octane + Tailwind v4 installiert - NPM-Reverse-Proxy mit SSL aktiv für: - app.nimuli.com → VM:80 - nimu.li → VM:80 - ws.nimuli.com → VM:8080 (Websockets ON) - mail.intern.nimuli.com → VM:8025 - Vite über app.nimuli.com (kein vite.nimuli.com mehr) - Login funktioniert für Dev-User ### Was kaputt ist (PRIO 1) 1. **Theme-Flash bei Hard-Reload** — Page rendert kurz im White-Mode, springt dann auf Dark-Mode. Inakzeptabel. 2. **Modals funktionieren GAR NICHT** — `@livewire('wire-elements-modal')` möglicherweise nicht im Layout. Klick auf Modal-Trigger-Buttons macht nichts. 3. **Laravel Echo nicht definiert** — Console-Warning: "Laravel Echo cannot be found". Reverb-Broadcasts greifen nicht. 4. **Manifest-Icon-Fehler** — `Error while trying to use the following icon from the Manifest`. Favicon/Manifest-Pfade kaputt. 5. **Sidebar-Layout falsch** — Header + Footer sollen fix sein, nur Mittelteil scrollable. Aktuell vermutlich anders. 6. **Alpine doppelt** — `Detected multiple instances of Alpine running` Console-Warning. Livewire v3 bringt Alpine mit, separater Import killt. 7. **Frontend fehlt** — Nur Stock-Breeze-Blades sichtbar. Kein echtes Nimuli-UI mit Sidebar/Topbar/Dashboard/Modals. ### Was schon getan wurde - Spec geschrieben (Master-Design 2026-05-15) - Phase 1 "Setup" laut Claude angeblich abgeschlossen, faktisch aber: - Default-Breeze-Auth-Views statt Custom-Nimuli-UI - Keine Modals gebaut - Layout nur rudimentär - Theme-Switch instabil - 25+ Tasks gemeldet als "complete" obwohl Browser-UI kaputt - Tailwind v4 + @tailwindcss/vite installiert - nginx-Config angepasst für Vite-Proxy via host.docker.internal:5173 - DevUserSeeder existiert (sollte): nexxo@nimuli.com / NimuliDev2026! - Workspace-Routes /w/{ulid}/* definiert mit ResolveWorkspace-Middleware ### Wichtige bisherige Entscheidungen (aus Brainstorm-Spec) | Bereich | Entscheidung | |---------|--------------| | Stack | Laravel 12 + Livewire v3 + Octane (FrankenPHP) + Tailwind v4 + Alpine via Livewire + Wire Elements Modal + Chart.js 4.x | | Frontend-Regel | Alle Views via Livewire-Components, Controller + View getrennt. KEINE klassischen Controller außer RedirectController | | Auth | Breeze Livewire-Stack + 2FA via pragmarx/google2fa | | Multi-Tenancy | Single-DB, Workspace-ULID in URL `/w/{ulid}/...`, Plan-Limits enforced | | DB | MySQL Source-of-Truth, Redis für Cache+Queue+Sessions, 4×/Tag Backup 14d Retention | | Storage | Lokal NAS-Mount (dev), Hetzner Storage Box (prod) | | Billing | Stripe + Stripe Tax + PayPal-Method + SEPA, Cashier, Customer pro Workspace, billing_profiles separate Table | | i18n | DB-Table `translations` + Redis-Hash `lang:{locale}`, Admin-UI | | Theme | Dual-Mode Dark (A-Style, default) + Light (B-Style), Purple-Pink-Akzent (#7c3aed → #ec4899) in beiden Modi | | Tailwind | v4 `@theme {}` Syntax, KEIN tailwind.config.js, KEINE Inline-Styles außer Progressbar-Width | | Mobile | Mobile-First, Sidebar als Drawer <768px | | Modals | Wire Elements Modal — eigener Controller + View, alle via `wire:click="$dispatch('openModal', {component: 'modals.xyz'})"` | | Tests | Pest, TDD-Pflicht pro Modal-Action | | Pricing | Free 0€ / Pro 19€ / Business 79€ / Agency 199€ | | Roadmap | Phase 1 Big-Bang Launch (12-16 Wochen): Core + AI + API + Webhooks. Phase 2 Integrationen. Phase 2.5 React-Native-App. Phase 3 Meta+Google Ads | | Production | Hetzner Cluster: 3 web + 1 worker + DB + Redis + LB + CDN | | Dev | Single-Host Docker Compose, alles auf VM 10.10.90.142 | --- ## Sofort-Aufträge für neue Session ### Phase 0 — Setup-State auditieren (vor jedem Fix) ```bash cd ~/nimuli # 1. Repo-State git status git log --oneline -10 # 2. Container dc ps # 3. Layout-Datei prüfen cat resources/views/components/layouts/app.blade.php # 4. Sidebar-Component ls -la app/Livewire/Components/ resources/views/livewire/components/ # 5. Modals ls -la app/Livewire/Modals/ resources/views/livewire/modals/ # 6. wire-elements-modal eingebunden? grep -r "wire-elements-modal" resources/views/ # 7. Echo-Setup cat resources/js/bootstrap.js cat resources/js/app.js # 8. Tailwind/CSS cat resources/css/app.css # 9. Vite-Config cat vite.config.js # 10. Dev-User art tinker --execute="echo App\Models\User::firstWhere('email','nexxo@nimuli.com')?->email ?? 'MISSING';" # 11. Routes art route:list | grep -E 'ws\.|logout' # 12. Tests art test 2>&1 | tail -20 # 13. Browser-Console-Errors (User berichtet) ``` ### Phase 1 — Theme-Flash fixen (PRIO HOCH) Layout-Header muss exakt so anfangen: ```blade @php $theme = auth()->user()?->theme ?? request()->cookie('theme', 'dark'); if ($theme === 'system') $theme = 'dark'; @endphp {{ $title ?? 'Nimuli' }} @vite(['resources/css/app.css', 'resources/js/app.js']) {{ $slot }} @livewire('wire-elements-modal') @livewireScripts ``` Theme-Toggle MUSS Cookie + localStorage setzen, sonst flash beim Reload. ### Phase 2 — Modals einbinden `@livewire('wire-elements-modal')` MUSS im Layout vor `` stehen. Sonst geht KEIN Modal. Plus Wire-Elements-Modal-Package installiert: ```bash art tinker --execute="echo class_exists('LivewireUI\\Modal\\ModalComponent') ? 'OK' : 'MISSING';" ``` Falls fehlt: ```bash composer require wire-elements/modal ``` ### Phase 3 — Echo definieren `resources/js/bootstrap.js`: ```js import axios from 'axios'; window.axios = axios; window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'reverb', key: import.meta.env.VITE_REVERB_APP_KEY, wsHost: import.meta.env.VITE_REVERB_HOST, wsPort: 443, wssPort: 443, forceTLS: true, enabledTransports: ['ws', 'wss'], }); ``` `resources/js/app.js` — KEIN Alpine-Import: ```js import './bootstrap'; ``` ### Phase 4 — Sidebar mit flex-col Pattern: `flex flex-col` mit `flex-none` Header + `flex-1 overflow-y-auto` Nav + `flex-none` Footer. Siehe vorigen Fix-Prompt. ### Phase 5 — Modals bauen (PFLICHT mit TDD) Reihenfolge: 1. Shared: modals.confirm 2. Links: create-link, edit-link, delete-link 3. QR: create-qr, edit-qr, delete-qr 4. Bio: create-bio-page, edit-bio-page, delete-bio-page, add-bio-block, edit-bio-block 5. Domains: add-domain, verify-domain, delete-domain 6. Team: invite-member, edit-member-role, remove-member 7. Workspace: create-workspace, edit-workspace 8. Settings: create-webhook, edit-webhook, create-api-token, revoke-api-token 9. Profile: setup-2fa, delete-account Pattern pro Modal: - Test ZUERST (TDD) - `app/Livewire/Modals/.php` — extends `LivewireUI\Modal\ModalComponent` - `resources/views/livewire/modals/.blade.php` - Trigger-Button auf Page mit `wire:click="$dispatch('openModal', {component: 'modals.'})"` - Test grün → Browser-Test → Commit ### Phase 6 — Frontend-Build (umfangreich) Layouts, Sidebar, Topbar, Theme-Toggle, Workspace-Switcher, Dashboard mit Hero-Metrics + Chart.js, alle Index-Pages für Links/QR/Bio/Domains/Analytics/Team/Billing/Settings/Profile/Admin. Skills: - /frontend-design für JEDE View - /test-driven-development für jede Action - /using-superpowers immer --- ## Skills-Pflicht in neuer Session ``` - /using-superpowers (BEFORE jeder Action) - systematic-debugging (vor jedem Fix, Root-Cause finden) - verification-before-completion (Beweis-Output bevor "fertig") - /frontend-design (jede View, jedes Modal) - /test-driven-development (jeder Action: Test ZUERST) - security-guidance (Auth, Upload, Token, Webhook) - caveman (Output knapp) ``` --- ## Selbst-Verify-Scripts ### scripts/verify-modals.sh ```bash #!/bin/bash set -e cd ~/nimuli [ -d node_modules/tailwindcss ] || { echo "FAIL: tailwindcss missing"; exit 1; } grep -q 'wire-elements-modal' resources/views/components/layouts/app.blade.php || { echo "FAIL: wire-elements-modal not in layout"; exit 1; } grep -q 'window.Echo' resources/js/bootstrap.js || { echo "FAIL: Echo not defined"; exit 1; } [ -f public/favicon.svg ] || { echo "FAIL: favicon missing"; exit 1; } [ -f public/site.webmanifest ] || { echo "FAIL: manifest missing"; exit 1; } USER=$(docker compose exec -T app php artisan tinker --execute="echo App\Models\User::where('email','nexxo@nimuli.com')->exists() ? '1' : '0';" 2>/dev/null | tr -d '[:space:]') [ "$USER" = "1" ] || { echo "FAIL: nexxo user missing"; exit 1; } ROUTES=$(docker compose exec -T app php artisan route:list 2>/dev/null | grep -E 'ws\.' | wc -l) [ "$ROUTES" -ge 5 ] || { echo "FAIL: workspace routes ($ROUTES)"; exit 1; } docker compose exec -T app php artisan test --parallel 2>&1 | tail -5 echo "✅ Verify GRÜN" ``` Alias: ```bash alias verify='bash scripts/verify-modals.sh' ``` --- ## Workflow neue Session ``` 1. claude-yolo (VS Code: Permission-Mode "Bypass") 2. cd ~/nimuli 3. Diese handoff.md mitgeben oder Pfad referenzieren 4. Spec verfügbar: docs/superpowers/specs/2026-05-15-nimuli-design.md 5. /using-superpowers aktivieren 6. Phase 0 — Audit 7. Phase 1-6 nach Priorität 8. Pro Phase: Test → Build → Verify → Commit → Push 9. NICHT "fertig" ohne verify-Script GRÜN ``` --- ## Git-Push-Pattern ```bash GITEA_TOKEN=$(grep ^GITEA_ACCESS_TOKEN .env | cut -d= -f2 | tr -d '"' | tr -d "'") git remote set-url origin https://oauth2:${GITEA_TOKEN}@git.nexlab.at/boban/nimuli.git git push origin main ``` --- ## Dev-User-Credentials ``` URL: https://app.nimuli.com/login Email: nexxo@nimuli.com Password: NimuliDev2026! ``` Falls fehlt: `art db:seed --class=DevUserSeeder` (idempotent via updateOrCreate) --- ## Lokale Aliase auf VM (~/.bashrc) ```bash alias dc='docker compose' alias dce='docker compose exec' alias dcl='docker compose logs --tail=100 -f' alias dcu='docker compose up -d' alias dcd='docker compose down' alias dcr='docker compose restart' alias dcps='docker compose ps' alias art='docker compose exec app php artisan' alias tinker='docker compose exec app php artisan tinker' alias migrate='docker compose exec app php artisan migrate' alias migratef='docker compose exec app php artisan migrate:fresh --seed' alias reset-db='docker compose exec app php artisan migrate:fresh --seed && echo "DB reset + Dev-User wieder da"' alias seed-dev='docker compose exec app php artisan db:seed --class=DevUserSeeder' alias composer='docker compose exec app composer' alias pest='docker compose exec app vendor/bin/pest' alias pint='docker compose exec app vendor/bin/pint' alias phpstan='docker compose exec app vendor/bin/phpstan analyse' alias dev='npm run dev' alias build='npm run build' alias vite-start='tmux new -d -s vite "cd ~/nimuli && npm run dev 2>&1 | tee /tmp/vite.log"' alias vite-stop='tmux kill-session -t vite 2>/dev/null; pkill -f vite' alias vite-log='tail -f /tmp/vite.log' alias verify='bash scripts/verify-modals.sh' alias smoke='bash scripts/smoke-test.sh' alias clear-cache='docker compose exec app php artisan optimize:clear && rm -rf node_modules/.vite public/hot public/build' alias octane-reload='docker compose exec app php artisan octane:reload' alias nimuli='cd ~/nimuli' alias claude-yolo='claude --dangerously-skip-permissions' # falls CLI installiert ``` --- ## NPM-Konfiguration Proxy-Hosts in NPM-UI: - app.nimuli.com → 10.10.90.142:80, SSL Let's-Encrypt, **Websockets Support ON** - nimu.li → 10.10.90.142:80, SSL Let's-Encrypt - ws.nimuli.com → 10.10.90.142:8080, SSL Let's-Encrypt, **Websockets Support ON** - mail.intern.nimuli.com → 10.10.90.142:8025 Falls vite.nimuli.com noch existiert: **löschen** (Vite läuft jetzt über app.nimuli.com). --- ## Erfolgs-Kriterien (vor "Phase 1 fertig") - [ ] Hard-Reload 5× zeigt KEINEN Theme-Flash - [ ] Manifest-Icon-Error WEG aus Console - [ ] `window.Echo` definiert (Browser-Console-Check) - [ ] Alpine NUR EINMAL geladen (kein "multiple instances") - [ ] Sidebar Header+Footer fix, Mitte scrollable - [ ] Mindestens 25 Modals erstellt, alle funktional, alle getestet - [ ] Frontend hat echtes Nimuli-UI (Dashboard, Links, QR, Bio, Domains, Analytics, Team, Billing, Settings, Profile) - [ ] Login mit nexxo@nimuli.com funktioniert nach migrate:fresh - [ ] verify-Script grün - [ ] smoke-Script grün - [ ] art test grün - [ ] phpstan grün - [ ] alle Commits gepusht zu Gitea --- ## Strikte Regeln 1. **KEINE "fertig"-Behauptung ohne verify-Output** 2. **Bei Console-Error in Browser: NICHT abbrechen, Root-Cause finden** 3. **JEDER Modal = TDD: Test zuerst, dann Build** 4. **Wire-Elements-Modal in JEDEM Layout das Modals nutzt** 5. **Theme-Init Server-Side via Cookie/User-Setting, nicht nur clientseitig** 6. **DevUserSeeder NIE entfernen aus DatabaseSeeder** 7. **`@livewireScripts` NACH `@livewire('wire-elements-modal')`** 8. **Alpine NIE separat importieren** 9. **Echo in bootstrap.js, NICHT in app.js direkt** 10. **Pro Fix: Beweis durch curl + Browser + Test, nicht "ich glaube"**