Compare commits

2 Commits

Author SHA1 Message Date
darklithium 8f9dc7f1a6 feat: dynamische Cookie-Bilder je nach Liste
- Füge listenspezifische PNGs hinzu (famous quotes, farmer wisdom, UNfortune, idioms, sandman, unicorn, vegan recipes)
- Implementiere dynamische Bildauswahl basierend auf currentFortuneListName
- Cookie-Bild aktualisiert sich sofort beim Start und Listenwechsel
- Bereinige Cookie-Icon-Größen für einheitliches Aussehen (34x30 / 30x26 GU)
- Entferne Spruchanzahl in Klammern aus Listenbeschreibungen
- Füge Switch für Cookie-Knacksgeräusch (cookieCrackEnabled) in Einstellungen hinzu
- Optimierte updateCookieImage()-Funktion mit expliziten Aufrufen bei allen Zustandsänderungen

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-06 02:37:27 +02:00
darklithium 8586e38bb4 docs: Lektionen und Routinen dokumentiert
- LEKTIONEN_2026-06-05.md: Git-Server-Hinweis hinzugefügt
- ROUTINEN.md: Täglicher Workflow und Git-Branch-Strategie

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-05 23:52:31 +02:00
18 changed files with 282 additions and 13 deletions
+10
View File
@@ -52,3 +52,13 @@
### JSON-Formatierung ### JSON-Formatierung
- Zeilenumbrüche in Strings: `\n\n` für Leerzeile, `\n` für Zeilenumbruch - Zeilenumbrüche in Strings: `\n\n` für Leerzeile, `\n` für Zeilenumbruch
- Bei Konvertierungen: Datei als Text lesen, nicht als JSON, um Newlines korrekt zu verarbeiten - Bei Konvertierungen: Datei als Text lesen, nicht als JSON, um Newlines korrekt zu verarbeiten
## 📚 Siehe auch
- [ROUTINEN.md](./ROUTINEN.md) - Täglicher Workflow und Git-Branch-Strategie
### Git-Server (Gitea) auf YunoHost
- **Installation**: `sudo yunohost app install gitea`
- **Repository-URL**: `https://git.darklithium.de/<username>/<repo>`
- **SSH-URL**: `gitea@<server>:<username>/<repo>.git`
- **SSH-Key**: Benutzer `gitea` (nicht `git`) auf dem Server
- **Hinweis**: Externer SSH-Zugriff (Port 22) oft blockiert → intern oder HTTPS verwenden
+98
View File
@@ -0,0 +1,98 @@
# Lektionen gelehrt am 06.06.2026
## 🎯 Dynamische Cookie-Bilder Implementierung
### Problem
Cookie-Bilder sollten sich je nach ausgewählter Spruchliste ändern (z.B. `famous quotes_open.png` für berühmte Zitate).
### Lösung
1. **Funktion `updateCookieImage()`** erstellt, die den Bildpfad basierend auf `currentFortuneListName` und `fortuneOpened` bestimmt
2. **Explizite Aufrufe** an allen Stellen, wo sich der Zustand ändert:
- Initialisierung (mit `Qt.callLater` für UI-Bereitschaft)
- Beim Listenwechsel in den Einstellungen
- Beim Öffnen/Schließen des Cookies (MouseArea Handler)
- Beim Neuladen des Fortunes
### Wichtige Erkenntnis
**Qt.binding() funktioniert NICHT zuverlässig** für automatische Updates, wenn sich Properties in JavaScript-Funktionen ändern.
**Immer explizite Aufrufe** von Update-Funktionen verwenden, statt auf automatische Bindings zu vertrauen.
### Code-Beispiel
```qml
function updateCookieImage() {
var listName = currentFortuneListName || "fortune";
var availableLists = ["farmer wisdom", "UNfortune", ...];
if (availableLists.indexOf(listName) !== -1) {
var encodedName = listName.replace(/ /g, "%20");
cookieImage.source = Qt.resolvedUrl("../assets/" + encodedName + (fortuneOpened ? "_open.png" : "_close.png"));
} else {
cookieImage.source = fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png");
}
}
```
---
## 📝 Listenbeschreibungen
### Problem
Spruchanzahl wurde in Klammern angezeigt (z.B. "Redensarten aus aller Welt (15)").
### Lösung
`cleanDescription()`-Funktion entfernt alle Klammern mit Zahlen am Ende:
```qml
function cleanDescription(text) {
return text.replace(/\s*\(\d+\)$/, "");
}
```
---
## 🔊 Cookie-Knacksgeräusch Toggle
### Implementierung
- Property `cookieCrackEnabled` (default: true)
- Switch in Einstellungen mit Binding an die Property
- Sound wird nur abgespielt, wenn `cookieCrackEnabled === true`
### Code
```qml
Switch {
checked: cookieCrackEnabled
onCheckedChanged: {
cookieCrackEnabled = checked;
py.call("fortunecookie.set_cookie_crack_enabled", [checked]);
}
}
// In Event-Handlern:
if (cookieCrackEnabled) crackMediaPlayer.play();
```
---
## 📏 Cookie-Icon-Größen
### Anpassung
- Geschlossen: 30x26 GU
- Geöffnet: 34x30 GU
Alle Cookie-Bilder haben jetzt ähnliche Dimensionen für ein einheitliches Aussehen.
---
## 🔧 Debugging-Tipps
1. **console.log() ist essenziell** - Ohne Debug-Ausgaben ist es schwer, den Code-Fluss zu verfolgen
2. **Clean Build** - `clickable clean` vor `clickable build` durchführen, wenn Änderungen nicht sichtbar sind
3. **Git Status prüfen** - `git status` zeigt, welche Dateien geändert sind
4. **Build-Directory prüfen** - Manchmal wird die alte Main.qml verwendet, weil der Build nicht neu gestartet wurde
---
## 📌 Best Practices
1. **Explizit > Implizit** - Bei QML besser explizite Funktionsaufrufe als automatische Bindings verwenden
2. **Fehlerbehandlung** - try-catch in komplexen Funktionen hilft bei der Fehlersuche
3. **Konsistente Größen** - UI-Elemente sollten ähnliche Dimensionen haben für ein professionelles Aussehen
4. **Benutzerfreundlichkeit** - Toggle-Switches für Sound sind intuitiver als Checkboxen
+118
View File
@@ -0,0 +1,118 @@
# Routinen & Workflow - FortuneCookie App
## 📌 Täglicher Entwicklungsworkflow
### 1️⃣ **Arbeiten im `daily`-Branch**
```bash
# Im Projektverzeichnis
cd /home/chrischi/DEV/UT/fortunecookie-neu/testing
# Änderungen commiten
git add -A
git commit -m "Beschreibung der Änderungen"
# Auf Gitea-Server pushen
git push origin daily
```
### 2️⃣ **Testing-Branch aktualisieren**
```bash
# Auf testing wechseln
git checkout testing
# daily in testing mergen
git merge daily
# Auf Gitea-Server pushen
git push origin testing
```
### 3️⃣ **App installieren**
```bash
cd /home/chrischi/DEV/UT/fortunecookie-neu/testing
clickable build && clickable install
```
---
## 🔄 Git-Branch-Strategie
| Branch | Zweck | Wann wird gemerged? |
|--------|-------|-------------------|
| `daily` | Entwicklung, Experimente | → `testing` nach successful Tests |
| `testing` | Getestete Versionen | → `stable` bei Release |
| `stable` | Releases | Nur bei fertigen Versionen |
---
## 📁 Git-Server (Gitea)
- **Repository-URL:** `https://git.darklithium.de/chrischi/fortunecookie`
- **SSH-URL:** `gitea@darklithium:chrischi/fortunecookie.git`
- **Standard-Branch:** `daily`
### SSH-Key einrichten (einmalig)
```bash
# Key erstellen (auf Notebook/PC)
ssh-keygen -t ed25519 -C "chrischi@darklithium.de"
# Key zu Agent hinzufügen
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
# Öffentlichen Key in Gitea hinterlegen
# (https://git.darklithium.de → Einstellungen → SSH Keys)
cat ~/.ssh/id_ed25519.pub
```
---
## 📝 OpenStore-Veröffentlichung
- **Repository-URL:** `https://git.darklithium.de/chrischi/fortunecookie`
- **Verlinkter Branch:** `testing` (oder `daily` für Entwicklerversionen)
---
## 🎯 Quality Gates (vor Merge nach testing)
- [ ] App startet ohne Fehler
- [ ] Alle Listen funktionieren
- [ ] Keine englischen Texte in deutschen Listen
- [ ] Formatierung stimmt (Zeilenumbrüche, Ursprünge etc.)
- [ ] Keine Syntax-Fehler in JSON-Dateien
- [ ] Installation erfolgreich
---
## 📌 Wichtige Dateien & Pfade
```
fortunecookie-neu/
├── testing/ # Aktuelle Entwicklungsversion
│ ├── qml/Main.qml # UI & Logik
│ ├── src/fortunecookie.py # Backend & Spruchlisten-Verwaltung
│ └── assets/fortunes/ # Alle Spruchlisten (*.json)
└── STABLE/ # Stabilisierte Versionen
└── v1.0.1/ # Letztes Release
```
---
## 🔧 Notfall: Remote neu setzen
Falls das Remote verloren geht:
```bash
# Alle Remotes anzeigen
git remote -v
# Remote entfernen
git remote remove origin
# Remote neu setzen (SSH)
git remote add origin gitea@darklithium:chrischi/fortunecookie.git
# ODER (HTTPS)
git remote add origin https://git.darklithium.de/chrischi/fortunecookie.git
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

+56 -13
View File
@@ -19,8 +19,26 @@ MainView {
property bool fortuneOpened: false property bool fortuneOpened: false
property string currentFortune: "" property string currentFortune: ""
property bool musicPlaying: false property bool musicPlaying: false
property bool cookieCrackEnabled: true
property bool appInitialized: false property bool appInitialized: false
property string currentFortuneListDescription: "" property string currentFortuneListDescription: ""
property string currentFortuneListName: ""
function updateCookieImage() {
var listName = currentFortuneListName || "fortune";
var availableLists = ["farmer wisdom", "UNfortune", "sandman", "famous quotes", "idioms", "vegan recipes", "unicorn"];
if (availableLists.indexOf(listName) !== -1) {
var encodedName = listName.replace(/ /g, "%20");
cookieImage.source = Qt.resolvedUrl("../assets/" + encodedName + (fortuneOpened ? "_open.png" : "_close.png"));
} else {
cookieImage.source = fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png");
}
}
// Entfernt Spruchanzahl in Klammern aus Beschreibung (z.B. "Text (15)" → "Text")
function cleanDescription(text) {
return text.replace(/\s*\(\d+\)$/, "");
}
Python { Python {
id: py id: py
@@ -94,20 +112,25 @@ MainView {
py.call("fortunecookie.get_initial_fortune", [], function(result) { py.call("fortunecookie.get_initial_fortune", [], function(result) {
currentFortune = result; currentFortune = result;
currentFortuneLabel.text = currentFortune; currentFortuneLabel.text = currentFortune;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_closed2.png");
}); });
// Musik-Status laden // Musik-Status laden
py.call("fortunecookie.get_music_enabled", [], function(result) { py.call("fortunecookie.get_music_enabled", [], function(result) {
musicPlaying = result; musicPlaying = result;
console.log("DEBUG QML: musicPlaying geladen: " + musicPlaying);
if (musicPlaying) { if (musicPlaying) {
mediaPlayer.play(); mediaPlayer.play();
} }
}); });
// Cookie-Crack-Sound-Status laden (lokal gespeichert)
py.call("fortunecookie.get_cookie_crack_enabled", [], function(result) {
root.cookieCrackEnabled = (result !== false && result !== undefined); // Default: true
});
// Aktuelle Liste laden und Beschreibung setzen // Aktuelle Liste laden und Beschreibung setzen
py.call("fortunecookie.get_current_fortune_list", [], function(listName) { py.call("fortunecookie.get_current_fortune_list", [], function(listName) {
root.currentFortuneListName = listName;
Qt.callLater(function() { updateCookieImage(); });
var descriptions = { var descriptions = {
"fortune": "klassische Glückskeks-Sprüche", "fortune": "klassische Glückskeks-Sprüche",
"farmer wisdom": "Bauernweisheiten", "farmer wisdom": "Bauernweisheiten",
@@ -118,7 +141,7 @@ MainView {
"vegan recipes": "Vegane Rezeptideen", "vegan recipes": "Vegane Rezeptideen",
"unicorn": "Einhorn Glückssprüche (die fast schon wehtun)" "unicorn": "Einhorn Glückssprüche (die fast schon wehtun)"
}; };
root.currentFortuneListDescription = descriptions[listName] || listName; root.currentFortuneListDescription = cleanDescription(descriptions[listName] || listName);
}); });
appInitialized = true; appInitialized = true;
@@ -132,9 +155,9 @@ MainView {
Image { Image {
id: cookieImage id: cookieImage
anchors.centerIn: parent anchors.centerIn: parent
width: fortuneOpened ? units.gu(36) : units.gu(32) width: fortuneOpened ? units.gu(34) : units.gu(30)
height: fortuneOpened ? units.gu(28) : units.gu(24) height: fortuneOpened ? units.gu(30) : units.gu(26)
source: fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png") source: ""
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
MouseArea { MouseArea {
@@ -146,13 +169,13 @@ MainView {
onReleased: { onReleased: {
if (mouseY < startY - units.gu(2)) { if (mouseY < startY - units.gu(2)) {
py.call("fortunecookie.open_fortune", [], function() { py.call("fortunecookie.open_fortune", [], function() {
crackMediaPlayer.play(); if (cookieCrackEnabled) crackMediaPlayer.play();
fortuneOpened = true; fortuneOpened = true;
updateCookieImage();
py.call("fortunecookie.get_current_fortune", [], function(result) { py.call("fortunecookie.get_current_fortune", [], function(result) {
currentFortune = result; currentFortune = result;
currentFortuneLabel.text = currentFortune; currentFortuneLabel.text = currentFortune;
currentFortuneLabel.visible = true; currentFortuneLabel.visible = true;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_open2.png");
}); });
}); });
} }
@@ -166,20 +189,20 @@ MainView {
onClicked: { onClicked: {
if (!fortuneOpened) { if (!fortuneOpened) {
py.call("fortunecookie.open_fortune", [], function() { py.call("fortunecookie.open_fortune", [], function() {
crackMediaPlayer.play(); if (cookieCrackEnabled) crackMediaPlayer.play();
fortuneOpened = true; fortuneOpened = true;
updateCookieImage();
py.call("fortunecookie.get_current_fortune", [], function(result) { py.call("fortunecookie.get_current_fortune", [], function(result) {
currentFortune = result; currentFortune = result;
currentFortuneLabel.text = currentFortune; currentFortuneLabel.text = currentFortune;
currentFortuneLabel.visible = true; currentFortuneLabel.visible = true;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_open2.png");
}); });
}); });
} else { } else {
fortuneOpened = false; fortuneOpened = false;
updateCookieImage();
currentFortuneLabel.text = ""; currentFortuneLabel.text = "";
currentFortuneLabel.visible = false; currentFortuneLabel.visible = false;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_closed2.png");
} }
} }
} }
@@ -207,10 +230,10 @@ MainView {
onClicked: { onClicked: {
py.call("fortunecookie.get_new_fortune", [], function() { py.call("fortunecookie.get_new_fortune", [], function() {
fortuneOpened = false; fortuneOpened = false;
updateCookieImage();
py.call("fortunecookie.get_current_fortune", [], function(result) { py.call("fortunecookie.get_current_fortune", [], function(result) {
currentFortune = result; currentFortune = result;
currentFortuneLabel.text = currentFortune; currentFortuneLabel.text = currentFortune;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_closed2.png");
}); });
}); });
} }
@@ -332,13 +355,33 @@ MainView {
var newList = newListObj ? newListObj.list_name : "fortune"; var newList = newListObj ? newListObj.list_name : "fortune";
py.call("fortunecookie.set_fortune_list", [newList], function() { py.call("fortunecookie.set_fortune_list", [newList], function() {
console.log("Spruchliste gewaehlt: " + newList); console.log("Spruchliste gewaehlt: " + newList);
root.currentFortuneListName = newList;
updateCookieImage();
reloadFortune(); reloadFortune();
// Aktualisiere die Anzeige im Hauptbildschirm // Aktualisiere die Anzeige im Hauptbildschirm
root.currentFortuneListDescription = newListObj ? newListObj.description : ""; root.currentFortuneListDescription = newListObj ? cleanDescription(newListObj.description) : "";
}); });
} }
} }
// COOKIE-CRACK-SOUND TOGGLE
Label {
text: "Cookie-Knacksgeräusch:"
Layout.fillWidth: true
fontSize: "large"
}
Switch {
id: crackSoundSwitch
Layout.fillWidth: false
Layout.alignment: Qt.AlignHCenter
checked: cookieCrackEnabled
onCheckedChanged: {
cookieCrackEnabled = checked;
py.call("fortunecookie.set_cookie_crack_enabled", [checked]);
}
}
// ZURÜCK-BUTTON // ZURÜCK-BUTTON
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true