From e7b4ec571ed3c5cfc23bb7418c28b6b05449bddc Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 15 Jun 2026 12:55:13 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20pers=C3=B6nlichere=20Profilseite=20+=20?= =?UTF-8?q?iOS-App-Setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Profil: Begrüßung in Zielsprache, Kategorie-Punkte-Übersicht, ruhigerer Header (kein rotierender Avatar/Online-Dot), Notch-Fix und kompaktere Aktivitäts-Heatmap. Außerdem Capacitor-iOS-Projekt und diverse Auth/Feed/Audio-Verbesserungen aus dem Premium-Redesign. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 6 + CLAUDE.md | 64 + FUNKTIONSUEBERSICHT.md | 200 ++++ capacitor.config.json | 5 + index.html | 4 +- ios/.gitignore | 13 + ios/App/App.xcodeproj/project.pbxproj | 378 ++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 15 + ios/App/App/AppDelegate.swift | 49 + .../AppIcon.appiconset/AppIcon-512@2x.png | Bin 0 -> 73121 bytes .../AppIcon.appiconset/Contents.json | 14 + ios/App/App/Assets.xcassets/Contents.json | 6 + .../Splash.imageset/Contents.json | 23 + .../Splash.imageset/splash-2732x2732-1.png | Bin 0 -> 41273 bytes .../Splash.imageset/splash-2732x2732-2.png | Bin 0 -> 41273 bytes .../Splash.imageset/splash-2732x2732.png | Bin 0 -> 41273 bytes .../App/Base.lproj/LaunchScreen.storyboard | 32 + ios/App/App/Base.lproj/Main.storyboard | 19 + ios/App/App/Info.plist | 51 + ios/App/CapApp-SPM/.gitignore | 9 + ios/App/CapApp-SPM/Package.swift | 25 + ios/App/CapApp-SPM/README.md | 5 + .../Sources/CapApp-SPM/CapApp-SPM.swift | 1 + ios/debug.xcconfig | 1 + package-lock.json | 1049 ++++++++++++++++- package.json | 2 +- setup_auth.mjs | 776 ++++++++++++ src/App.jsx | 2 +- src/api/directus.js | 6 +- src/assets/logo.svg | 34 + src/components/PairSentenceCard.jsx | 78 +- src/components/PairWordCard.jsx | 63 +- src/components/PairYesNoCard.jsx | 63 +- src/components/SelectionOverlay.jsx | 42 + src/components/auth/AuthScreen.jsx | 8 +- src/components/auth/LoginForm.jsx | 6 +- src/components/auth/RegisterStep1.jsx | 6 +- src/components/auth/RegisterStep2.jsx | 6 +- src/context/AuthContext.jsx | 22 +- src/hooks/usePairAudio.js | 47 +- src/index.css | 13 + src/pages/Feed.css | 14 +- src/pages/Feed.jsx | 64 +- src/pages/Profil.css | 51 +- src/pages/Profil.jsx | 40 +- src/utils/chipTimings.js | 48 + src/utils/secureToken.js | 40 + src/utils/speak.js | 23 + 49 files changed, 3221 insertions(+), 210 deletions(-) create mode 100644 CLAUDE.md create mode 100644 FUNKTIONSUEBERSICHT.md create mode 100644 capacitor.config.json create mode 100644 ios/.gitignore create mode 100644 ios/App/App.xcodeproj/project.pbxproj create mode 100644 ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 ios/App/App/AppDelegate.swift create mode 100644 ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png create mode 100644 ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/App/App/Assets.xcassets/Contents.json create mode 100644 ios/App/App/Assets.xcassets/Splash.imageset/Contents.json create mode 100644 ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png create mode 100644 ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png create mode 100644 ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png create mode 100644 ios/App/App/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/App/App/Base.lproj/Main.storyboard create mode 100644 ios/App/App/Info.plist create mode 100644 ios/App/CapApp-SPM/.gitignore create mode 100644 ios/App/CapApp-SPM/Package.swift create mode 100644 ios/App/CapApp-SPM/README.md create mode 100644 ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift create mode 100644 ios/debug.xcconfig create mode 100644 setup_auth.mjs create mode 100644 src/assets/logo.svg create mode 100644 src/components/SelectionOverlay.jsx create mode 100644 src/utils/chipTimings.js create mode 100644 src/utils/secureToken.js create mode 100644 src/utils/speak.js diff --git a/.gitignore b/.gitignore index 31e9d4b..8ef4db3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,9 @@ dist/ .env.local .DS_Store .claude/ + +# iOS build artifacts (Capacitor) +ios/build/ +ios/App/Pods/ +ios/App/output/ +ios/DerivedData/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c22c0ed --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +```bash +npm run dev # Vite dev server (default http://localhost:5173) +npm run build # Production build → dist/ +npm run preview # Serve the built dist/ locally +``` + +There is **no test runner and no linter** configured — `package.json` only defines `dev`, `build`, `preview`. Don't suggest `npm test`/`npm run lint`. + +Deployment is a two-stage Docker build (`Dockerfile`): Node builds `dist/`, then it's served by nginx (`nginx.conf`) with SPA fallback (`try_files … /index.html`). + +## Environment + +`VITE_API_URL` (in `.env` / `.env.production`) points at the backend. It's the **only** runtime config — every API call in `src/api/directus.js` reads `import.meta.env.VITE_API_URL`. `.env` is gitignored. + +## Architecture + +React 19 + Vite SPA, **no router package**. There are no DB cards/feed entries fetched directly — the app talks to a custom backend, not Directus directly (see below). + +**Routing** is `useState`-based in `src/App.jsx`: a `PAGES` map (`feed`/`game`/`pro`/`profil`) swaps the active page component, driven by `BottomNav`. `Game` and `Pro` are placeholders. + +**Auth gating:** `App.jsx` renders `` unless the user has `username`, `language_native_id`, and `language_target_id`. `AuthContext` (`src/context/AuthContext.jsx`) holds the JWT (localStorage key **`hejyou_token`**), calls `getMe()` on mount, and clears the token on failure. Registration is two steps: `RegisterStep1` (email+password) → `RegisterStep2` (username + native/target language). + +### The API client is misnamed + +`src/api/directus.js` is **not** a Directus client anymore — the module name is kept "aus historischen Gründen". It is a thin fetch wrapper around a **custom backend (snakkimo-API)** exposing `/auth/*` endpoints: + +| Function | Endpoint | Notes | +|---|---|---| +| `login` / `registerUser` | `POST /auth/login`, `/auth/register` | return `{ token, userId, needsProfile }` | +| `getMe` | `GET /auth/me` | basis for auth check + progress (EP/streak/level) | +| `checkUsername` | `GET /auth/check-username` | | +| `createProfile` | `POST /auth/profile` | username + native/target lang | +| `getLanguageOptions` | `GET /auth/languages` | merged with local `LANG_META` (flag + Web Speech code) | +| `getFeedPairs` | `GET /auth/feed?lang=&limit=` | returns "pairs", the feed unit | +| `saveProgress` | `POST /auth/progress` | books EP/streak, returns updated `total_ep` | + +Several content functions (`getWords`, `getQuestions`, `getActiveLearningPair`, `assetUrl`, …) are **stubs** returning empty/null — content endpoints are not built yet. Don't assume they fetch anything. + +### Feed = "pairs" → cards + +`src/pages/Feed.jsx` fetches an array of *pairs* and maps each to one card by its `answer_type`: + +| `answer_type` | Component | Points (`POINTS` map) | +|---|---|---| +| `text` | `PairSentenceCard` | 2 | +| `yes_no` | `PairYesNoCard` | 2 | +| `word` | `PairWordCard` | 3 | +| `question` | `PairWordCard` | 3 | + +On completion, `handleComplete` adds the pair id to a local `done` set (cards are hidden, not removed) and POSTs to `saveProgress`. Target language comes from `user.language_target_short` (fallback `de`). + +The **`Pair*` cards** (`PairSentenceCard`, `PairYesNoCard`, `PairWordCard`) are the live card components. The other card files (`NewWord*`, `ImagePick/ImageQuiz`, `AudioQuiz`, `LetterOrder`, `SentenceFill`, `LanguageParentCard`) are an **earlier card model** not wired into the current feed — check `Feed.jsx` before assuming a component is in use. + +Several cards use the browser **Web Speech API** (`SpeechRecognition` / `speechSynthesis`) for the `speak`/`listen` flow; `src/utils/confetti.js` wraps `canvas-confetti` for correct-answer feedback. + +## `knowledge/directus_struktur.md` + +A detailed reference for the **backend data model** (collections, fields, relations) and the original app concept. Useful for understanding the domain and DB schema, but note it predates the custom-backend migration: its "Frontend API-Funktionen" table describes **direct Directus calls that no longer exist** — trust `src/api/directus.js` for the actual frontend contract. diff --git a/FUNKTIONSUEBERSICHT.md b/FUNKTIONSUEBERSICHT.md new file mode 100644 index 0000000..d814166 --- /dev/null +++ b/FUNKTIONSUEBERSICHT.md @@ -0,0 +1,200 @@ +# Funktionsübersicht — Snakkimo iOS-App + +> **Zweck:** Vollständige Liste aller Funktionen der **iOS-App** (Endnutzer-App) zur Funktionsüberprüfung durch das Team. +> Jede Funktion hat eine **Soll-Beschreibung** (so muss es sich verhalten) und eine abhakbare Checkbox. +> Backend-/Admin-Tools (CMT, Content-Pipeline) sind **nicht** Teil dieses Dokuments. + +**Technischer Rahmen** +- iOS-App = **Capacitor-Wrapper** um eine React-19/Vite-Single-Page-App (`webDir: dist`). +- Bundle-ID: `com.hyggecraftery.hejyou` · App-Name (iOS-Homescreen): **Snakkimo**. +- Alle Inhalte/Logins kommen live vom Backend (`VITE_API_URL`) — **es gibt keinen Offline-Modus**, ohne Netz funktioniert die App nicht. +- Aktiv unterstützte Sprachen: **Deutsch 🇩🇪, Englisch 🇬🇧, Schwedisch 🇸🇪**. + +> ⚠️ **Branding-Inkonsistenz (für QA wichtig):** Auf dem Login-Screen steht **„HejYou"**, der iOS-App-Name ist **„Snakkimo"**, der Browser-/Webview-Titel ist **„Language App"**. Bitte als bekannten Punkt behandeln, nicht als neuen Bug. + +--- + +## 1. Allgemeiner Rahmen + +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 1.1 | App-Start / Ladezustand | Beim Start erscheint ein Spinner, bis Profil geladen ist. | ☐ | +| 1.2 | Auth-Gating | Ohne gültiges Profil (Username + Mutter- + Zielsprache) erscheint immer der Auth-Screen. | ☐ | +| 1.3 | Session-Persistenz | Login bleibt nach App-Neustart erhalten (Token in `localStorage: hejyou_token`). | ☐ | +| 1.4 | Auto-Logout bei ungültigem Token | Ist der Token abgelaufen/ungültig, wird er verworfen und der Auth-Screen erscheint. | ☐ | +| 1.5 | Untere Navigation (BottomNav) | 4 Tabs: **Feed · Game · Pro · Profil**. Aktiver Tab ist hervorgehoben. | ☐ | +| 1.6 | Seitenwechsel-Animation | Beim Tab-Wechsel spielt eine kurze Einblend-Animation (`page-enter`). | ☐ | +| 1.7 | Safe-Area / Notch | Layout respektiert iPhone-Notch/Home-Indicator (`viewport-fit=cover`). | ☐ | +| 1.8 | Design/Theme | Warmes Creme-/Gold-Design, durchgängige Schrift- und Farb-Tokens. | ☐ | + +--- + +## 2. Onboarding & Authentifizierung + +### 2.1 Login (Tab „Anmelden") +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 2.1.1 | Login/Register-Umschalter | Oben umschaltbar zwischen „Anmelden" und „Registrieren"; letzte Wahl wird gemerkt (`hejyou_last_mode`). | ☐ | +| 2.1.2 | Eingabe E-Mail + Passwort | Beide Felder Pflicht; leeres Feld → Fehlermeldung „Bitte E-Mail und Passwort eingeben.". | ☐ | +| 2.1.3 | Erfolgreicher Login | Token wird gespeichert, Profil geladen, App öffnet den Feed. | ☐ | +| 2.1.4 | Falsche Zugangsdaten | Fehlermeldung vom Server wird angezeigt (z. B. „Invalid credentials"). | ☐ | +| 2.1.5 | Login eines Accounts ohne Profil | Springt direkt in **Profil-Schritt (Step 2)** statt in den Feed. | ☐ | +| 2.1.6 | Lade-Status | Button zeigt „Anmelden…" und ist während des Requests deaktiviert. | ☐ | + +### 2.2 Registrierung — Schritt 1 (E-Mail + Passwort) +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 2.2.1 | Schritt-Anzeige | Zeigt Fortschrittspunkte „Schritt 1 von 2". | ☐ | +| 2.2.2 | Passwort-Mindestlänge | Passwort < 8 Zeichen → „Passwort muss mindestens 8 Zeichen haben.". | ☐ | +| 2.2.3 | E-Mail bereits vergeben | Server-Fehler „Email already registered" wird angezeigt. | ☐ | +| 2.2.4 | Erfolg | Weiter zu Schritt 2 (Profil). | ☐ | + +### 2.3 Registrierung — Schritt 2 (Profil) +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 2.3.1 | Username-Regeln | 3–20 Zeichen, nur Buchstaben/Zahlen/Unterstrich; Verstoß → klare Fehlermeldung. | ☐ | +| 2.3.2 | Username-Verfügbarkeit | Vergebener Username → „Dieser Username ist bereits vergeben.". | ☐ | +| 2.3.3 | Muttersprache wählen | Dropdown mit Flaggen + Sprachnamen (vom Server geladen). | ☐ | +| 2.3.4 | Zielsprache wählen | Dropdown; **die gewählte Muttersprache ist hier ausgeblendet**. | ☐ | +| 2.3.5 | Gleiche Sprachen verhindern | Mutter- = Zielsprache → „… dürfen nicht gleich sein.". | ☐ | +| 2.3.6 | Profil anlegen | Bei Erfolg: Token persistiert, Profil geladen, Erfolgs-Screen erscheint. | ☐ | +| 2.3.7 | Sprachen-Ladefehler | Können Sprachen nicht geladen werden, ist der Button deaktiviert + Hinweis. | ☐ | + +### 2.4 Erfolgs-Screen & Logout +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 2.4.1 | Willkommens-Screen | Nach Registrierung: „Willkommen, {Username}!" mit Häkchen-Icon. | ☐ | +| 2.4.2 | Logout | Über das Logout-Icon im Profil; Token wird gelöscht, zurück zum Auth-Screen. | ☐ | + +--- + +## 3. Feed (Kernbereich „Lernen") + +### 3.1 Feed-Grundgerüst +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 3.1.1 | Karten laden | Beim Öffnen werden bis zu 20 Lernkarten in der **Zielsprache** geladen. | ☐ | +| 3.1.2 | EP-Abzeichen oben | Zeigt Fortschritts-Ring (Tagesziel-%), Gesamt-EP und „X/Y heute". | ☐ | +| 3.1.3 | Karte abschließen | Nach Lösen verschwindet die Karte aus der Liste (bis App-Neustart). | ☐ | +| 3.1.4 | Fortschritt speichern | Jede gelöste Karte sendet EP/Richtig-Falsch an den Server (EP/Streak aktualisiert sich). | ☐ | +| 3.1.5 | Ladezustand | „Lade Karten…" während des Ladens. | ☐ | +| 3.1.6 | Leerer Feed | Keine Inhalte → „Noch keine Inhalte verfügbar.". | ☐ | +| 3.1.7 | Alles gelöst | Alle Karten erledigt → „Super! Alle Karten abgeschlossen. 🎉". | ☐ | + +### 3.2 Gemeinsame Karten-Mechaniken (alle Kartentypen) +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 3.2.1 | Bild | Jede Karte zeigt oben ein Bild (Platzhalter 🖼️, falls keins). | ☐ | +| 3.2.2 | Satz vorlesen (Audio) | Tippen auf den Satz **oder** den Lautsprecher-Button spielt echtes Audio. | ☐ | +| 3.2.3 | Verlangsamtes Audio | Audio wird bewusst in **0,7-facher Geschwindigkeit** abgespielt. | ☐ | +| 3.2.4 | Nur ein Audio gleichzeitig | Startet man ein neues Audio, stoppt das vorherige. | ☐ | +| 3.2.5 | Karaoke-Hervorhebung | Während des Vorlesens wird das **aktuell gesprochene Wort** im Satz hervorgehoben. | ☐ | +| 3.2.6 | Wort-Chip antippen | Markiertes Wort antippen → zugehöriges **Label-Abzeichen auf dem Bild**. | ☐ | +| 3.2.7 | Objekt-Chip antippen | Objekt-Wort antippen → **Bildregion umrandet**, Rest abgedunkelt, Label sichtbar. | ☐ | +| 3.2.8 | Objekt-Sync beim Vorlesen | Wird ein Objekt-Wort vorgelesen, wird seine Bildregion (nur Umriss) live hervorgehoben. | ☐ | +| 3.2.9 | Übersetzungs-Hinweis | Unter dem Satz steht die Übersetzung in der Muttersprache (Hint). | ☐ | +| 3.2.10 | Defensive Token-Anzeige | Unaufgelöste Pipeline-Tokens (`⟦PHn:wort⟧`) werden als reines Wort angezeigt, nicht roh. | ☐ | + +### 3.3 Kartentyp „Satz / Lesen" (PairSentenceCard, `text`) — **2 EP** +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 3.3.1 | „Verstanden" gesperrt | Button ist gesperrt, bis man **entweder** vorgelesen **oder** übersetzt hat. | ☐ | +| 3.3.2 | Halten-zum-Übersetzen | Übersetzungs-Button **2 Sekunden halten** → Übersetzung blendet ein, Button entsperrt. | ☐ | +| 3.3.3 | Halte-Fortschrittsring | Während des Haltens läuft ein Ring 2 s lang voll. | ☐ | +| 3.3.4 | TTS-Fallback | Existiert kein Audio-File, wird per Browser-Sprachsynthese vorgelesen (de/en/sv). | ☐ | +| 3.3.5 | Vokabel-Chips | Im Satz markierte Vokabeln werden als separate Chips gelistet. | ☐ | +| 3.3.6 | Abschluss | „Verstanden" → Konfetti, Karte zählt als richtig (2 EP). | ☐ | + +### 3.4 Kartentyp „Ja/Nein" (PairYesNoCard, `yes_no`) — **2 EP** +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 3.4.1 | Zwei Antwort-Buttons | „✗ Nein" und „✓ Ja". | ☐ | +| 3.4.2 | Auswertung | Antwort wird gegen die korrekte Lösung des Satzes geprüft. | ☐ | +| 3.4.3 | Richtig | Konfetti + „✓ Richtig!" (2 EP). | ☐ | +| 3.4.4 | Falsch | „✗ Die Antwort war: Ja/Nein"; richtige/falsche Buttons farblich markiert. | ☐ | +| 3.4.5 | Mehrfachklick-Schutz | Nach Beantwortung sind die Buttons gesperrt. | ☐ | + +### 3.5 Kartentyp „Wort wählen" (PairWordCard, `word` & `question`) — **3 EP** +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 3.5.1 | Frage + Bild + Audio | Zeigt Frage-Satz, Bild und Vorlesen wie alle Karten. | ☐ | +| 3.5.2 | Wort-Optionen | Mischung aus richtigen + falschen Wörtern, zufällig gemischt. | ☐ | +| 3.5.3 | Mehrfachauswahl | Mehrere Optionen an-/abwählbar, dann „Bestätigen". | ☐ | +| 3.5.4 | „Bestätigen" gesperrt | Solange nichts gewählt ist, ist „Bestätigen" gesperrt. | ☐ | +| 3.5.5 | Auswertung | **Richtig**, wenn nur richtige Wörter gewählt sind (Teilauswahl der richtigen erlaubt, **kein** falsches dabei). | ☐ | +| 3.5.6 | Feedback richtig/falsch | Richtig → Konfetti + „✓ Richtig!"; falsch → „✗ Richtig wären: …". | ☐ | +| 3.5.7 | Übersetzungs-Hinweis | Nach Bestätigen werden bei richtigen Optionen die muttersprachlichen Hints gezeigt. | ☐ | + +--- + +## 4. Game (Tab „Game") +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 4.1 | Platzhalter-Screen | Zeigt **„Bald verfügbar"**-Karte „Spielend lernen" mit Teaser-Punkten. | ☐ | +| 4.2 | Keine Funktion | Es gibt **noch keine** spielbare Funktion (bewusst Platzhalter, kein Bug). | ☐ | + +## 5. Pro (Tab „Pro") +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 5.1 | Platzhalter-Screen | Zeigt **„Bald verfügbar"**-Karte „Snakkimo Pro" mit Teaser-Punkten. | ☐ | +| 5.2 | Keine Kauf-/Abo-Funktion | Es gibt **noch keine** Bezahl-/Abo-Funktion (bewusst Platzhalter, kein Bug). | ☐ | + +--- + +## 6. Profil (Tab „Profil") +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 6.1 | Avatar + Initialen | Kreis mit den ersten 2 Buchstaben des Usernamens, Online-Punkt. | ☐ | +| 6.2 | Level-Abzeichen | Sechseck mit Level-Zahl (**Level = Gesamt-EP ÷ 500**, abgerundet). | ☐ | +| 6.3 | Name + Handle | Anzeigename und „@username". | ☐ | +| 6.4 | Streak-Anzeige | „🔥 X Tage Streak", wenn Streak > 0. | ☐ | +| 6.5 | Tagesziel-Karte | Ring mit Tagesziel-%, „heutige EP / Ziel", Restwert-Hinweis bzw. „Geschafft 🎉". | ☐ | +| 6.6 | Fortschritts-Karte | Zielsprache (Flagge+Name), Gesamt-EP, XP-Balken, Level-Pille, „EP bis Level X+1". | ☐ | +| 6.7 | Wochen-Aktivität | Balkendiagramm der letzten 7 Tage (heutiger Tag markiert). | ☐ | +| 6.8 | Aktivitäts-Kalender | Heatmap der letzten 12 Wochen (Farbintensität nach EP/Tag). | ☐ | +| 6.9 | Eckdaten-Kacheln | „Karten geübt", „Genauigkeit %", „Tage Streak". | ☐ | +| 6.10 | Fähigkeiten-Radar | Radar-Chart **Vokabular / Lesen / Verständnis** (Genauigkeit je Skill). | ☐ | +| 6.11 | Leerzustand Skills | Ohne Daten: „Leg los — deine Stärken erscheinen, sobald du Karten löst.". | ☐ | +| 6.12 | Tracking-Hinweis | Ist die Statistik (noch) nicht verfügbar: dezenter Hinweis „… komm morgen wieder! 🌱". | ☐ | +| 6.13 | Logout | Abmelde-Button oben rechts (siehe 2.4.2). | ☐ | + +> **Mapping Skills:** Vokabular = Kartentypen `word` + `question`; Lesen = `text`; Verständnis = `yes_no`. + +--- + +## 7. iOS-spezifische Prüfpunkte +| # | Funktion | Soll-Verhalten | OK | +|---|---|---|---| +| 7.1 | App-Icon & Name | Auf dem Homescreen erscheint **„Snakkimo"**. | ☐ | +| 7.2 | Start ohne Netz | Ohne Internet lädt nichts (Login/Feed schlagen fehl) — Verhalten dokumentieren. | ☐ | +| 7.3 | Audio-Wiedergabe iOS | Vorlesen funktioniert auch im iOS-WebView (Touch löst Audio aus). | ☐ | +| 7.4 | Touch-Halten (Übersetzen) | 2-Sekunden-Halten funktioniert per Touch (nicht nur Maus). | ☐ | +| 7.5 | Scrollen | Auth- und Profil-Screens lassen sich vollständig scrollen, ohne „Gummiband"-Leaks. | ☐ | +| 7.6 | Safe-Area / Statusleiste | Kein Inhalt liegt unter Notch/Statusleiste oder Home-Indicator. | ☐ | +| 7.7 | Hintergrund/Wiederkehr | App nach Hintergrund zurückholen → Session noch aktiv, Feed nutzbar. | ☐ | + +--- + +## 8. Bekannte Einschränkungen / Auffälligkeiten (für QA, kein Bug-Report nötig) +- **Game & Pro** sind reine „Bald verfügbar"-Platzhalter. +- **Kein Offline-Modus** — App braucht durchgehend Verbindung zum Backend. +- **Feed-Wiederholung:** Bereits gelöste Karten verschwinden nur lokal; nach App-Neustart können dieselben Karten erneut erscheinen (keine Spaced-Repetition-Logik in der App). +- **Branding uneinheitlich:** „HejYou" (Login) vs. „Snakkimo" (App-Name) vs. „Language App" (Webview-Titel). +- **Nur 3 Sprachen** (de/en/sv) auswählbar. +- **Inhaltsmenge begrenzt:** Der Feed kann bei intensivem Üben schnell leer sein („Alle Karten abgeschlossen"). + +--- + +## 9. Kompakte Test-Checkliste (Smoke-Test, ~10 Min) +- [ ] Registrierung (neue E-Mail) → Profil anlegen → landet im Feed +- [ ] Logout → erneuter Login → Feed +- [ ] Login bleibt nach App-Neustart bestehen +- [ ] Feed: Satz-Karte vorlesen (Audio + Karaoke-Highlight) +- [ ] Feed: Wort-Chip antippen → Bild-Label; Objekt-Chip → Bildregion umrandet +- [ ] Feed: „Verstanden"-Karte über 2-Sek-Halten entsperren und lösen +- [ ] Feed: Ja/Nein-Karte richtig + falsch testen (Konfetti / korrekte Lösung) +- [ ] Feed: Wort-Wahl-Karte mit Mehrfachauswahl lösen +- [ ] EP-Abzeichen oben steigt nach Lösen einer Karte +- [ ] Profil: EP/Level, Streak, Wochen-Balken, Heatmap, Radar werden angezeigt +- [ ] Game-Tab & Pro-Tab zeigen „Bald verfügbar" +- [ ] iOS: Safe-Area korrekt, Touch-Halten & Audio funktionieren diff --git a/capacitor.config.json b/capacitor.config.json new file mode 100644 index 0000000..721819c --- /dev/null +++ b/capacitor.config.json @@ -0,0 +1,5 @@ +{ + "appId": "com.hyggecraftery.snakkimo", + "appName": "Snakkimo", + "webDir": "dist" +} diff --git a/index.html b/index.html index bdcd71c..6968308 100644 --- a/index.html +++ b/index.html @@ -2,8 +2,8 @@ - - Language App + + Snakkimo
diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..f470299 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,13 @@ +App/build +App/Pods +App/output +App/App/public +DerivedData +xcuserdata + +# Cordova plugins for Capacitor +capacitor-cordova-ios-plugins + +# Generated Config files +App/App/capacitor.config.json +App/App/config.xml diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d52493d --- /dev/null +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,378 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 4D22ABE82AF431CB00220026 /* CapApp-SPM */; }; + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; + 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; + 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; + 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; + 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 504EC3011FED79650016851F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 504EC2FB1FED79650016851F = { + isa = PBXGroup; + children = ( + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */, + 504EC3061FED79650016851F /* App */, + 504EC3051FED79650016851F /* Products */, + ); + sourceTree = ""; + }; + 504EC3051FED79650016851F /* Products */ = { + isa = PBXGroup; + children = ( + 504EC3041FED79650016851F /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + 504EC3061FED79650016851F /* App */ = { + isa = PBXGroup; + children = ( + 50379B222058CBB4000EE86E /* capacitor.config.json */, + 504EC3071FED79650016851F /* AppDelegate.swift */, + 504EC30B1FED79650016851F /* Main.storyboard */, + 504EC30E1FED79650016851F /* Assets.xcassets */, + 504EC3101FED79650016851F /* LaunchScreen.storyboard */, + 504EC3131FED79650016851F /* Info.plist */, + 2FAD9762203C412B000D30F8 /* config.xml */, + 50B271D01FEDC1A000F3C39B /* public */, + ); + path = App; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 504EC3031FED79650016851F /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + 504EC3001FED79650016851F /* Sources */, + 504EC3011FED79650016851F /* Frameworks */, + 504EC3021FED79650016851F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + packageProductDependencies = ( + 4D22ABE82AF431CB00220026 /* CapApp-SPM */, + ); + productName = App; + productReference = 504EC3041FED79650016851F /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 504EC2FC1FED79650016851F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + TargetAttributes = { + 504EC3031FED79650016851F = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 504EC2FB1FED79650016851F; + packageReferences = ( + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */, + ); + productRefGroup = 504EC3051FED79650016851F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 504EC3031FED79650016851F /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 504EC3021FED79650016851F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, + 50B271D11FEDC1A000F3C39B /* public in Resources */, + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, + 504EC30D1FED79650016851F /* Main.storyboard in Resources */, + 2FAD9763203C412B000D30F8 /* config.xml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 504EC3001FED79650016851F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 504EC30B1FED79650016851F /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC30C1FED79650016851F /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC3111FED79650016851F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 504EC3141FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 504EC3151FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 504EC3171FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = LKA5KKQKHY; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; + PRODUCT_BUNDLE_IDENTIFIER = com.hyggecraftery.snakkimo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 504EC3181FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = LKA5KKQKHY; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hyggecraftery.snakkimo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3141FED79650016851F /* Debug */, + 504EC3151FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3171FED79650016851F /* Debug */, + 504EC3181FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "CapApp-SPM"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4D22ABE82AF431CB00220026 /* CapApp-SPM */ = { + isa = XCSwiftPackageProductDependency; + package = D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */; + productName = "CapApp-SPM"; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 504EC2FC1FED79650016851F /* Project object */; +} diff --git a/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..81a371c --- /dev/null +++ b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "ba02264f6d4c613de9cfd9120fa05cc1e61acacc858cb62d0cb6cb68ef052757", + "pins" : [ + { + "identity" : "capacitor-swift-pm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ionic-team/capacitor-swift-pm.git", + "state" : { + "revision" : "44ae2505f54a80c5e559533950886d0644fc2fca", + "version" : "8.4.0" + } + } + ], + "version" : 3 +} diff --git a/ios/App/App/AppDelegate.swift b/ios/App/App/AppDelegate.swift new file mode 100644 index 0000000..c3cd83b --- /dev/null +++ b/ios/App/App/AppDelegate.swift @@ -0,0 +1,49 @@ +import UIKit +import Capacitor + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + // Called when the app was launched with a url. Feel free to add additional processing here, + // but if you want the App API to support tracking app url opens, make sure to keep this call + return ApplicationDelegateProxy.shared.application(app, open: url, options: options) + } + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + // Called when the app was launched with an activity, including Universal Links. + // Feel free to add additional processing here, but if you want the App API to support + // tracking app url opens, make sure to keep this call + return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) + } + +} diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..147db04ddbdd3e111670fac58bd966b2b2770782 GIT binary patch literal 73121 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 |%W!GslV#&}&raS| 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 literal 0 HcmV?d00001 diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9b7d382 --- /dev/null +++ b/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon-512@2x.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/App/App/Assets.xcassets/Contents.json b/ios/App/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/ios/App/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json new file mode 100644 index 0000000..d7d96a6 --- /dev/null +++ b/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "splash-2732x2732-2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png new file mode 100644 index 0000000000000000000000000000000000000000..33ea6c970f2df1db62a624a55e5bbcc4ee07bbdf GIT binary patch literal 41273 zcmeHvcT|&E*ykHSBNh;JqzD9IP*5R&N{28KWdx+DfKoy+A~p0*5(h_Mq|A&6C{o0s z4IrrW790>}s47hX2}MA_5F#akkYwMy(b@0Y^X=~0{cF#j_^9ymWWx9d1}tXhO$0N5J<3{VjPZXQ0^5P5g3r0=1E(An$eEP{IDp zMfHmTCfJ)^KSl*%FGX2i_K5QF(7mpJGLkol&;t$lVME;HBm8{*gY_Z|6(GBMV4M4E zq=G!uCB(;2;Ro&m#AvJsh>WhaZ+AWT|*nGeg>(o zrK+x>r>>)?sUiRMr2sxH==u#kTlD#_&jSBusBkkRBtQ>|3=a=i3qPfX3-Uy2=<4bs z)isftnyO$A)!<0~5Vr_b|6s*$7SPyWk07sr5HFm+JlCR|J1#WDPyrk%Zwh_^|C;p= z{t6Qa7&5{w0I8v-&Ycp}@w&&q{Q^RRe4&S5_dsHOv3^+pkYKQ{#=m_7ZsJ04!8dXL z6Vv~G{GT2GLVNMzzt8wD$KvPr?<0ak&V_-~_zK8>**o}hWB?Xviw(ww26`9jKleUTPW*qLzXu&kypOx=Hcbfor21L_yQK~7J@ZY;I5jghNh~f z?q!WLdg{7*n(C_RT6*g0ysj5<*S&5;{@YatTw1XAWsTE%8oGMwr~Xq{&}FW>g}D81 zJ74$EyMYVxa|0*r<>%&!MF#kLD#-r}qn-)Q7Z(IR7#!WH|M>e0CMMQFxEo%+;0M9B zrf20ZoHNnV(ACmX)l}1fhI{d%-Ua{Q5I27h>;<%;0tk+pm)CVY>~(kSDJ?fmRjh}m zuIgz`4_#Gv53TE}ZaOz^Xlb}>VX>z*zkMH#^9bd-z_;&T|3Cb`Rgf1Lf^NS5*LJvp z$@Lw*AH9OXRgL@_I+w74Ut7Lj@{qgexp{EIz)-=1yZ+eg3SWQi^?!{3&usWjENJw9 z;TODa!MGbC;ch|LA3Q;{{;KogZD z`?U!?a^tct_eI`YLxY^uw)y^dMVjIvVJ{Qf3NPt!XGAAj-Q*8!@N4y@?%YKCa(r}$akrvSez9|1lBd<6Ii@Dbo6z(;_O03U(>ZxCoTne3{L zSm>r@hi}9pOg~L$(dZoK@uq4j6O_Nic(an~Wu_drNe`O6*2I{t3p_2(LO`I#BWqjZltZ*$)!bsmZXBi9N@>B>s(W`by^`W$t5K<&Rq;(12&xucPo z9@P*22i3KmKUj>_oqL7WCDJVci;s|<9GEaYtr%; zMJDm$zyV!u-w0g6a1`|k*di)wC;?l?0h=f1(GN=59~fiXr{<@Bo}WqVOj$gHsA5_H zAqdW62ArVV_sFO!8k{pRKK%mMNPv@5{UHwHtP^5vyc;G=?43Z>9YPvbtpY~I0`fhA zT1}sWQFI1Y=Qk*nTk#L7zQEO3kWWj{P3AV6dC_T-=-#K+ZK~^%rcv zok$;gSx_YfU?q22j}k)h(X$crfLe3n2pMR}GNA;oRgR1K=jyr*%fXUY zq$^DGT=;%J^D=XGD4P&$k$45mT3@2QwNJ4ZnFJ#Osn(J0WfLd8G#`VcOG*9;t;v4K zZ=Y!Q0-49xf`4x8D+SUT2ETNx!}HSWy^Yj%?`MtQaI&843Qu{{06<})0fx!7Cuzk> zX34hHSog9VT%fC_G34Ytr~9!PZ^*ui*^w98Hlo!-A}M>&8qa5&%~fY#wnOG;iw_(L z#Rr377_oN_W^oGeCqIBo4r&SKEst-r8XfVFDWd5ko~vi0w*9vU?go}01Foq=t0iZ| zc~4!MB8_A5ct7)`E)v(0*^_2>MXQMw;L! zVPd4w59l>7_`t5)dZZT8eb{pMKL=yfOg71gFk}#5lBCitiJDKVOO6-mowCzo- zxvI%`Bo@l9gSoxE>6vV6*Jz$-8$S?zk7XT53Q~31{$f+M=k)=g`%#?cxb0O>;Ew0j znu<#TbQ7-I0B3o@>D~p~{r@Y0-xf^*whbdR9f3pDPfBxMrH*kQ==#(ty9V7b-5lhEfUDA0vnWX$jKRBGnt~ z4KVEZWqagPgHJN8KDZ82jH-XFRSAo{a`4UhgJrunDJ*epvFh~x@MHlCE`0#5COiH*ZS^W5>|&;Znr4Kv-f9zlzp16-m{ z;isz8ft%VI`!=ada z;yBG=?z7_eg{Mbvc0tb=2&=x7WdJ_a)GO#4r6WEkRL>AN2q+k;B8#`Hl3w1T** z4s{e_0os{9!Gwg&E$Y&7n0hKa0egfPq>x|rS4@wO(3+S)_0YKXJ^K9cc8gl8ea$M+ zqE{{ua7?SH?*P*y3&_O1AcQd265QVZRILG1msNoHg?bmlBVgQb4mK!VfR+sg20BV4 zP+<)mM-BYR2CK%7OZils4bihtZ51dKYNdn;o(K)4HN5a)mpwOpM&9(7ca1v6vy}na zT48S!$anQkvx><*g38VNNNXIC&!uF!7qK?O<@C0K!bm|#K&DWe4q#$W;1{Yt-38J# zosL|4K$;icS*^6w?7csxZ=$(A;aA8DMBAZQh(NSND&lFV4h-WS?Y&idV)jG&t{*Zmq7l`*;h))3u2RCU4@o$9Z=O+@{^t~pgV$aj6jU?XSh0GtYwiID9=44` zg;qBXUs0I44MjwZJj4+JB(D7hR7bU=m{Jb&6FUg7&rqE?$!a(iL`tP2O|sY<#NiUs zsEcBkw;kut^JMAGjFHd}ohUUm_0JVc<<;n`2;JCea8q36g~lxhp4{zD1M1lRX$i%F zQ{{|l92MtGwELC%Vy#=ffRq+FNRVRZ>a*Zj#>f-kk{2nh;4SG7A+2Mn)H}6j(su}i z{RE@j*qT_#@nI@M?!XAJq%e1a&`cg#={-y{=cqg->DN|SUHN<$cdp&0UobNW5Th+- z`3*E&L)p_DJ2{tgs5=;=-J9k0r1T+^^#WLW#1DtXtw-n4SDjAm=h#`o z#Fl-a-xs)+OxV4<=L~Zl;B3}S1#9P?xv@jbOg|4U;{Lb#BfTS#ry%S~)^BpGxoy$= zxQPS|ySI+rXL~9a|69)Pj{`OHIR=Z7fLP#HMqT|d_!vvDU3yKx%z2EqB;34pXEt)w zmpFB4aj`Qf`>oOGt}j>Gx5gLhXm>|H%UJ2CKu;0Gw+k2Sw0+{=$epJ|>EUX+*8M2ts5*cZzCS&brN+{`W0Af2@%W9Nc z`%eF~(7y2Lo*maV5srqoDv^y25EgQhPwh3Be)@FSRSssx;Ui$z%=UZ#(=!9oXBj!y zEU)6#)%2;WX2_4CxzCf45%&jD zhSga{3WdAFottIx?L{ds8-O> z#Z5OaRu&t%?2wH~uPDZp4o-OwTr}g3sr3kti|=duw^um*vQ#^#7oI%yZoA+&!{LJE zgaqYT$+A8BZMuWTR+Fp8#_kAq9oMdGSSm>;F_Tz&1K+vNXG%ZoJ79sh+@UH{zo41@3VK)T-maPlJ$~>@EFl&rwKR#pDt^&Ef&S{8sjnVCgwP);+ zuhzpA+;2uwsf7ZOOGNTsAi-^?F_>*G-yGq~@$Ds1M14y7)5v4N!=OP1p?mTC$S(n07-#s!gT4Sin=ADd8SSY@ygkSDd;;zu- zeqpW?trSPk+=!X7bs~fhEx26si;)T>7qM_%lW)uxvZObkU6shon9A^G`^UIt2W6)E z-@0=X6k466XbM6154CP(wAU%14ALO+7q~tSriG7yy6V%NsaLk2nd9Yh5au3CTm9UL zl)Jn3z7R?|Cz5wEcC*0nZ`;R+ayx;{+LOY!hNOj6T`Hr0bFdW~Si0x-9d~#-yCIe} zXyDGdPDb0`uY2Zf#S!co}0fI(c;WX7el5=)1U~C--L=y{Cp7 zSe9X{LZJ=<^6ojXl^MvT0~luIrc4R$5Ow4Vr2y@TBw0eV+~Qbu zO*m_Jf$TWaX3l#n*s8-5C3bAt=tqwFcnPhmAYy$ns15sJ9znTL&7 zy51R+oyM08h_hm0H{{S`SFTwRR8_%D`ApZl5rVaVe8)@S(Dy;MJd5d2T9;bfzx`f^ z^P4i(xg2ReOG*;HC7va+HJb8SY29{i=T@=mf!9YVrpz?Mr+Zdn93@;;K8f}l8GZi! zfR4J_>r?x)3|ZoB4FKnZ>GTY04M6Mw5vk*8b2%GNtIqkHp7K|J)A+t_~fnv?uNVao>B=>9anCV;w2XsnG0Ttnla%8=8wHTRw>o+Tbj#v5}Qx`NCG>w+#qyJU(bcTe~TS_V3M)*Wd- zcL%O_I8`kx(&jOlW7J+2WlmyYv}=B5Lq*v-zV;d3S;1)idd$Xw!0Ei_TT}I4g%@6% zQZn`;^{(9D=tPRXnJ*{3a`TstUP`-X->W_Sod;57J5F)KWDgX85rf%=vMA<15nHr^ zj9CBQs2%5Eh2O#4tJRFFoZA9zn3uPsT3w0K(K+zfMXc0ZGF&cgp;TFQ+Pa4P52I|r z-fo24!5A#ag#ObZQ{JX9{ds{g4ra`+D50}09Zfz%hfr!l>W|*lrafOw!K9Q2Uu8{$ z;-bGc6jqZMU$TLjqwOrH5^4?j*c7;T|KR3hg>()xLrXRstP3bZhU0n3uR@f_WRLA? z&RPrS-NJfq%6QT$ruy#@I~|1nLj3bB_B^bLsT`^=@OXGuB|lzjaRqmJcITG+ZolKk zR|!YvxYKTRJHYil;?;iG;BW~hicTn#*yC{6bCMosH_K^gIE(UY7?leK`cc*6o}x!j z*2h0TMRCm(Qb*_xGCN~TdL(>SfzhWZUd5#Cdx&-Kf?AnPOGp?sn}i=?n^sfQ%8%d& zlrd48O;?8IPmQ8kdvLEZHk#F1AK56|bdKFj-d4;;FN!!e6FvrLpK7$GK zIZ6F^1B%1&zdFKDcG_G2h>1g4_A>+LJ{#CX&-Ho?yNz`Ay_(NWJti0ZYd;qV z-nQYmYCudq$=Yhh5+aFL(cUHW6 znQfob|9UsYdc0A~+zY_)O+f@yZTTLAi$)jB# zXDa3{{l>b4FxX@K`) zu^RnoFBXVypIA-C#?HH^3bIiW-Aixv$Q)ALNGNRU)X>FEykzuiEnh!z;7x|Oq{xRD zzY2`DD0kL!l2Bw#$`wWe_BgfjmBtU8P_I;s^fdFO(zQmy`{m?xK}pe^l+Cq!ve4?fX+LXPoRQqkId%u9*wq)euW=4nsHO;;o7znw zg7wyOywx$`txj}S5MV{PUhGYeEpKW*SU>hBEbAd-d>_WnUUF>4zT~Hbgt`^G7_SOl zqUIpm$63w~|F)(eV~map1eUws576p8 zXG%^jP1CC?dXm{5&+xVRC=tO=im;C35tu!$QKwkjT^gBibyT3#Q7MZ8mUsbDxKfRi zk?>YRK(YwRWqHDfY~5qJt{zHTiz8H!L(0 zRw*Z}E?eCh3044daZi%WCD2Lb9Jtn&m3Oz{pj)?&>EpFG0-@|{E~Fd085p1CPGp4* zmbDC64E1@`XzGLvS!AM6XxOacXKK&e3f51Q$~~PP&cw(;&Q@g{?$43+kcnvL^ zZcW^zD6~;b3W{-2AkzJE-~l(hymPCqTi~>JEP+~HRQSsbK(W+BC|Fh-=hE$X{8H`oEV%ugZ?}GpVLf!N zRsE7h{wsQhIah~wK;3@tU(h4&K1*@F-^V`)%9#Ir;Zylodb48F(D36E3{FOilXPh)kQb#RSvT(jS zUmgV0{#vEAd&JYPgXUKPc!xJ~NiT|Kg8Mr*Pn8EbgC)fmC4bAB>$&C>kfX@~xhg_A(dj!7V;de`$7A?)s@Cu925UuEtMAXS(oUq4H=h;#Xcg4`r1e z;=DaJMGxC-!L0``$ECJhH+If7a`Dqqa_4|bEuteb=jH2h)RG~q&7+L1{*3vvZ%*xb zw81L6X~m02Z~&CQw8n*Ogv2c0qYawgF`}0&#ePafn%w2gVR8}Hi$N>fVIK@p@j};b zPX1N9VIV5MbxK5u^R8;09xsh}vR4sS*ok{0kYDD$uiJn~ZNJOgrGMZN0=%LhtVdmY zlCIT8$b3LrAXbQBKPY3gqdlUBV|=qqXR4P8a;+;dtE$6tPQ^AOCVLkH&VD#4q;7ff zU5Zg=#cEG|#8f0SSl3q&%}1<&yT*J!I_V;(ToAt;g!HpN`C+A5u*H7BBLYk=OZ1$I zYL0Kd;tn-$FI^@S-?0!XD_2MkKs9_$5&DE&jCd2~kO-2z63^^Kh=xU8ard@iYRf^& z6Ok*FiD?p^biAOS$_!y;OA{)pM-uih`(Qn_2PqfEn6_j2JF5u?D5lvQ%EWqznN-+G zG8Qyh#;x!HC(@^dG{U*Yw5KtBdGoQ5 zOXoA#z>>7hhMwqT$&ks@xvi1nP<+onL2)yG+%{sn>ecm~*pp;`vQ20Qw2vEmLfg1Z zgO?mA2^8{coBL;1M$f}!7<;0~??kiWVZNhtO94OUh$cjU32PQSAhfBP@*}Y~WmG{R z_V*w5dVcV`ZJc!2+#q<^fICxlC<_)LgDkbssaUwYQBoPJkRQsS|IW6p$o+*yVHpVL zoljDPx$6e1{3>ccHg7;&?WUlUZJ+Gz*Xa;X@BFOF#@-KdupIwGYVrBA%g~C*{{=0y zj^+sNRd>epVsFhKEPriIUwVVzCT3#j(9xSuR>P%d$HuG->#31|MGi z{-IP`yZfJ?bvQ4M??ZjY?4G};cXFR~$ANg8O(sSHbVI4O<2yB|6kLWTAu*wtwMWJ8 z%x$gByko-NRn+o6T_T1h8c%lsjRurKDSE3|{hBH56-T@_+J;oqu-@lQZ$9l(NHA&S zG!>oPM;p@sr3JNoHBo7Lx+NG`Z5v*XYmdq8t|A6TBa&a6$_^emW|JY)y8RT5eiVI@ zy>(2K`hbgr!->#FS9!3tG6@!Z!i%)qsMDbX2M{*igW}a{)s#(6FK#7t`F9NdMMZF1 zEJDtDE76bnH7~Atd8;k-D;ZLbI#TgI+lbwqGmC>P6k2Iw1}QOMa{q11ZRvS7NHGf$ zbK`D?+=!3~=F!aK#deTKFn!JF?Oa8W#C`UdUtO~ z7s%WVg%t|JUyRnCEBC|s&~p!6RK|iihKT4*_*#)Jowq-xeq9KsjENK^E2GXiUXKpu zCrB?cc;4sMh|rRaaJLX{5At7dUKbNv^(AhuOYXmUO3<()@w%2iMq9nRy zcuKL$35#5p>=8_G>my`80fJAu8mc_rxZGa1@`F^EX`1-FWfoVK17c!aWtNNpn{ra% z<%Wgc8-C1DP8Md(k~K=NAYc*XUT1dq<5dNLnU@7M$yin(#qyh!aNRrg`de9|vZTgs z=aPeQGPrlA--2Ny@?WHa=_^!$Fuo1F1^lQ+lErLg*Bvlviz=I2wc?~+TAqK;05x2i zph!xMMQezKs)>9aCdL9lxA!Rshy?~#tO9d4*m|fS1fX6arXJ(feVceFDidLlkA4J2 z67d#fdu7rYuiwJZ5A?k0hZ&_s|J-;oma?j`PQ1f@6K2pl$H*S$M9$2455(7$2!ge* z*TZJTjiN@kCG9T3Kvz@ZX248{j?{T9B3Ids>#T^4U9QYEf9H|dEaKSpykPAPBk1*2 zjquirntl7$y2spR#tp!`-1EEBR8sZ_x>3Pc{XTAC!>qA6(%w1M_U01-v-@0>iGn-_ z5q%5R)twznV-djX)OWG^7F2388~Z7#;u(dC$YOTi@StWQQY*UC^<+L&G;qLza`n@k zhOC^x&7(f(#NgCd92T$A_fD^xcLTxzgp{<=7#RI%gCy05lG+JaR4mnbj*r#ihD985 zUtB%vs#cKd75BISg=x_yk0>jt#hg$`(Sxh^f*YZIh1xwpq))Gk@mEL^OCv2$(B+*T za;=MO_xi#Bw*=mHmy|6h<;RS>L3ewdQ^4X72H& z)u(S$POfsQ(TU1fow}1aW}-$;^?S+SDbi=TN_P8mU;p^^em`NNbRdm;eo8SESL)`NQkC{AS*5A<7pB zFQ!|+TwUs3_LU@jPJ%1b#!XXS84!buW;nldCr@PZitRD`G0d>-QgYS-;LYf#?v=1V zyXY5#9iyr3n5FrROIoXdccW{Gxd}lv_-NkX=HE0uEaQ`&tjtU&x~lYI#f^KqNLiG;V;jyIfna}9tag&JSeG(kf7kBq+Oez{*Vl&wP=}j1ELCizgPxa zoxNN%?ctfa`~;-3NjFdEa!mwbPN;S5hUf1bTMIM&W+!>UIR=Zk8t1WFu?+Oy*-%Y zbolyK>Lu7>gE%6*WXlTW*de)tm;&3Zd6;CHljBV%PfAJ8~GJCbloEcg8dAS4## ziQrCssT=In6FYq4;d^!OV56PIg-cbzW6WYGLkx)F2@gYP>?E;TDtd4A>Xo6Urze&v z3D-m%cH|8XIj~z|W6qQdtUN$Pj|jDX+$xXQv~^-FFFslFpT*PTI;1&^x3$t;kEOkS zB5YF*$>GinUe?&WJzP6Oj#2ctDxhih{DXt{JYg&u{7BkcK=TseIGjL+>iP^X(aKMv z`hhxo0r|pE7+99}bZ;tpo4<1)G~>R&;ujUA>GlBI_XhndVb!JC{?1$|bZU61M&mU0HRn*|OxQm(bP zV4Xlu4~u4*Cy$;#=qqw_mc98?Bi(?a36<1RzJoSr*QHvv4!f6H;R&=mZR9vkcmK&G zooIWhvDQy-?p7zx7DqDUF92vz+OsXv8l-nx=U}aL?lyOY^Lu2NcwU-Bad?%@T~IKDT>;Y)-?E2(e?9F~vfy*IgtgdwxBo zyu|7Ll6c94GyVzn@nDx;y>*wmpzC~15r^jm4BoA(lVCD@W_o#MO$I_EmbD(Iv zt%#R%brPX%uLrzVDE8(qnFZb?%ZUCcnBOn>$#898>Cnl{r>`@_b);w`>>fdLzyhL) zSBJ^6%k|``AMUdk@P+SpmXZ$0NQxAXc~avIk3nw0ya91sLIR^4l|o=L1NGB4IR$7{ z8$~mz!X>%@qhYW)={>UBX672O`UTdO$ucnm+QEwJ3)5Y$oQs`=+(p#=S0kXy1$XHP=5>Ha zwWm-UlLvqg;F-sl*@&5rFKL8O2kpYyG{Y;xd%X?@1k0Kug-qg zwT%m&$G#}ocTV})2bYmHz#B}~9ztM5LsFns5xiU2P!fb1p~k_E^POp74A|RP=Tpat?4`T-SBmGyQoH~Vf zQ8Kld?0*WFYuIquiL1zfu}!>4RU^TU+c&;%RbM5%C_Vq^7skSqi+}|?%6Eerh zMQ`XcnJikGG}P@axXjA)W{u1psZ zd{M&0f4C=Zv>`!XGj?+XiX4Ofe0^trjfFAk>{Ux#(fLH3Xg&NgA&IjA7nu#@I*Z^{ z?g<;wbcF8TnSn>PLX21Mi$R?iX5%E4nX|Uq(uno|n5z+RT8M-4zFsro6kMsOxa)%#sL0 zujcQOY?;=}`bd!7tXO|*fQ|(+5Yj0{8>hC9RSgOaQt}mJ*LxA4`f^f8>lxEtkotU= z`&Cis9Kuh6_4!Uvea+QY*OKTj0c>cDO0aVi+9!b;e;-`<3OTBfF&eCLaL+0id%h%f zE2b~#ZZ%$c``DL$P6bM}0#5QOx4u2Fw30zto1zZ-bMF-LpQR5OB?-A(zcE%(5eMnp zN7iMG%U+0JBl^T`;yX)~yq(W0rb&y@>L8S4M0xeC`_vC9$XG=el7__gJUMIRgh2bs zQ}no3^@{ZBaX|E(KpyXO4rgJ0q-wsrx;&~fa*h3SRWryp2VxJ%fwcWE7!R4rfmA?wveyhr;mPwc#C^T~p<% zE$AwQnCcOART=B)BbERfJe1p>5gS-d?v@*zdR2MoY)POc_fP;xk%GKu(iN;+R_i?# zQZEx&vB!UOy9k7=HUQc+ZGb|)r4-RE6x7)>1cDv!OA`P6OU~Fx8%4{)AHnpQdy1na z>S}A&{(Su9_T)KU-xIvP%`w!ln1#c%t>gqPLDxkNQpp=VXdq1Ve+Um)pWAL4yASi` zF5L;Pd;~gwhx~B74YnjOm>_<}1Js`td(;iT=5#S^6KO(^9gi(yS_a$9_C9NB_OhY%h0HQEdjo=m(jzU!nZY%Rw z?w{P3>shEY1Ge}b`S3t4!FLLNDDXpp9|{lx{7~SB0zVY^p}<9euOGig0$(-rlZtPy zz)z+43FKE_;Pd0Rpx{>(_(hkmzQE^4K0osL5ki0;3j9#uhXOwoKm_;#i7$}8QUQNg z;O`3jU4g$V@OK6N6$E|-=_?cR1=4?6Vc#|ARQe$B*9vbgOc%~sqMx31{pr5}7WQ#E literal 0 HcmV?d00001 diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png new file mode 100644 index 0000000000000000000000000000000000000000..33ea6c970f2df1db62a624a55e5bbcc4ee07bbdf GIT binary patch literal 41273 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 literal 0 HcmV?d00001 diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png new file mode 100644 index 0000000000000000000000000000000000000000..33ea6c970f2df1db62a624a55e5bbcc4ee07bbdf GIT binary patch literal 41273 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 literal 0 HcmV?d00001 diff --git a/ios/App/App/Base.lproj/LaunchScreen.storyboard b/ios/App/App/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..e7ae5d7 --- /dev/null +++ b/ios/App/App/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/App/App/Base.lproj/Main.storyboard b/ios/App/App/Base.lproj/Main.storyboard new file mode 100644 index 0000000..b44df7b --- /dev/null +++ b/ios/App/App/Base.lproj/Main.storyboard @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist new file mode 100644 index 0000000..e830f66 --- /dev/null +++ b/ios/App/App/Info.plist @@ -0,0 +1,51 @@ + + + + + CAPACITOR_DEBUG + $(CAPACITOR_DEBUG) + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Snakkimo + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/App/CapApp-SPM/.gitignore b/ios/App/CapApp-SPM/.gitignore new file mode 100644 index 0000000..3b29812 --- /dev/null +++ b/ios/App/CapApp-SPM/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/ios/App/CapApp-SPM/Package.swift b/ios/App/CapApp-SPM/Package.swift new file mode 100644 index 0000000..a2f6c96 --- /dev/null +++ b/ios/App/CapApp-SPM/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.9 +import PackageDescription + +// DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands +let package = Package( + name: "CapApp-SPM", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "CapApp-SPM", + targets: ["CapApp-SPM"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.4.0") + ], + targets: [ + .target( + name: "CapApp-SPM", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm") + ] + ) + ] +) diff --git a/ios/App/CapApp-SPM/README.md b/ios/App/CapApp-SPM/README.md new file mode 100644 index 0000000..03964db --- /dev/null +++ b/ios/App/CapApp-SPM/README.md @@ -0,0 +1,5 @@ +# CapApp-SPM + +This package is used to host SPM dependencies for your Capacitor project + +Do not modify the contents of it or there may be unintended consequences. diff --git a/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift b/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift new file mode 100644 index 0000000..945afec --- /dev/null +++ b/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift @@ -0,0 +1 @@ +public let isCapacitorApp = true diff --git a/ios/debug.xcconfig b/ios/debug.xcconfig new file mode 100644 index 0000000..53ce18d --- /dev/null +++ b/ios/debug.xcconfig @@ -0,0 +1 @@ +CAPACITOR_DEBUG = true diff --git a/package-lock.json b/package-lock.json index c4e65ed..8781883 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,11 @@ "name": "language-app", "version": "1.0.0", "dependencies": { + "@capacitor/cli": "^8.4.0", + "@capacitor/core": "^8.4.0", + "@capacitor/ios": "^8.4.0", "canvas-confetti": "^1.9.4", + "capacitor-secure-storage-plugin": "^0.13.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -301,6 +305,68 @@ "node": ">=6.9.0" } }, + "node_modules/@capacitor/cli": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-8.4.0.tgz", + "integrity": "sha512-5Z9RKHxiqJYRTLrfMeZmzR4qrlg5B85MxsWZ5goyXsLkO3bgpW9a1qV/6fR1SX9s5gwLza5y7PZVwITl/hDJ7g==", + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.8", + "@ionic/utils-subprocess": "^3.0.1", + "@ionic/utils-terminal": "^2.3.5", + "commander": "^12.1.0", + "debug": "^4.4.0", + "env-paths": "^2.2.0", + "fs-extra": "^11.2.0", + "kleur": "^4.1.5", + "native-run": "^2.0.3", + "open": "^8.4.0", + "plist": "^3.1.0", + "prompts": "^2.4.2", + "rimraf": "^6.0.1", + "semver": "^7.6.3", + "tar": "^7.5.3", + "tslib": "^2.8.1", + "xml2js": "^0.6.2" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/core": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.4.0.tgz", + "integrity": "sha512-LrS1xPIrqLtJABBIPDGXxxKmI9OyesrzWw8DiHbxhSC9JoiLUleUAJlX1a0LWIVLRbuY4Szgf9huFeRqYH2SAQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-8.4.0.tgz", + "integrity": "sha512-tnwstEdbTJ2nHAfoAwnurXgYRscWeLY+IIGdz69o24gN2Crfj9Xc0TWo8L5uFLF1LmpbUywH1IT0U1oHV8c+CA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -743,6 +809,157 @@ "node": ">=18" } }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.12.tgz", + "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", + "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", + "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1202,6 +1419,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", + "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", @@ -1222,6 +1457,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -1243,6 +1484,86 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.10.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", @@ -1256,6 +1577,39 @@ "node": ">=6.0.0" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/browserslist": { "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", @@ -1290,6 +1644,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001788", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", @@ -1321,6 +1684,51 @@ "url": "https://www.paypal.me/kirilvatev" } }, + "node_modules/capacitor-secure-storage-plugin": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/capacitor-secure-storage-plugin/-/capacitor-secure-storage-plugin-0.13.0.tgz", + "integrity": "sha512-+rLC/9Z0LTaRRt6L6HjBwcDh5gqgI3NPmDSwo4hk41XQOy3EBrRo81VleIqFsowsMA3oMT+E59Bl8/HiWk0nhQ==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1328,6 +1736,20 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -1339,7 +1761,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1353,6 +1774,15 @@ } } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.336", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.336.tgz", @@ -1360,6 +1790,33 @@ "dev": true, "license": "ISC" }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -1412,6 +1869,15 @@ "node": ">=6" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1430,6 +1896,20 @@ } } }, + "node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1455,6 +1935,86 @@ "node": ">=6.9.0" } }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1488,6 +2048,27 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -1498,11 +2079,46 @@ "yallist": "^3.0.2" } }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -1524,6 +2140,31 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-run": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.3.tgz", + "integrity": "sha512-U1PllBuzW5d1gfan+88L+Hky2eZx+9gv3Pf6rNBxKbORxi7boHzqiA6QFGSnqMem4j0A9tZ08NMIs5+0m/VS1Q==", + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/node-releases": { "version": "2.0.37", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", @@ -1531,6 +2172,69 @@ "dev": true, "license": "MIT" }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1551,6 +2255,20 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/postcss": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", @@ -1580,6 +2298,28 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/react": { "version": "19.2.5", "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", @@ -1611,6 +2351,39 @@ "node": ">=0.10.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", @@ -1656,6 +2429,32 @@ "fsevents": "~2.3.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "license": "ISC" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -1672,6 +2471,56 @@ "semver": "bin/semver.js" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1682,6 +2531,84 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.16", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", + "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -1699,6 +2626,45 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -1730,6 +2696,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vite": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", @@ -1805,12 +2777,85 @@ } } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } } diff --git a/package.json b/package.json index b60e97b..33dd44b 100644 --- a/package.json +++ b/package.json @@ -1 +1 @@ -{"name":"language-app","version":"1.0.0","type":"module","scripts":{"dev":"vite","build":"vite build","preview":"vite preview"},"dependencies":{"canvas-confetti":"^1.9.4","react":"^19.0.0","react-dom":"^19.0.0"},"devDependencies":{"@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4","vite":"^6.3.1"}} \ No newline at end of file +{"name":"snakkimo","version":"1.0.0","type":"module","scripts":{"dev":"vite","build":"vite build","preview":"vite preview"},"dependencies":{"@capacitor/cli":"^8.4.0","@capacitor/core":"^8.4.0","@capacitor/ios":"^8.4.0","canvas-confetti":"^1.9.4","capacitor-secure-storage-plugin":"^0.13.0","react":"^19.0.0","react-dom":"^19.0.0"},"devDependencies":{"@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4","vite":"^6.3.1"}} \ No newline at end of file diff --git a/setup_auth.mjs b/setup_auth.mjs new file mode 100644 index 0000000..0d6e34b --- /dev/null +++ b/setup_auth.mjs @@ -0,0 +1,776 @@ +#!/usr/bin/env node +// Führe aus: node setup_auth.js +// Im Verzeichnis: /Users/tim/Documents/GitTea/Language + +import { writeFileSync, mkdirSync } from 'fs' +import { join } from 'path' + +const ROOT = '/Users/tim/Documents/GitTea/Language' + +const files = { + // ─── .env (falls noch nicht vorhanden) ─────────────────── + '.env': `VITE_DIRECTUS_URL=https://db.hejyou.com +VITE_DIRECTUS_TOKEN=j6YyjhoFidnU3cI3MSrcgTXqO3t2wbZG +`, + + // ─── API Service ───────────────────────────────────────── + 'src/api/directus.js': `const BASE = import.meta.env.VITE_DIRECTUS_URL +const TOKEN = import.meta.env.VITE_DIRECTUS_TOKEN + +const headers = { + 'Content-Type': 'application/json', + 'Authorization': \`Bearer \${TOKEN}\`, +} + +export async function login(email, password) { + const res = await fetch(\`\${BASE}/auth/login\`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }) + const data = await res.json() + if (!res.ok) throw new Error(data.errors?.[0]?.message || 'Login fehlgeschlagen.') + return data.data +} + +export async function getMe(userToken) { + const res = await fetch( + \`\${BASE}/users/me?fields=id,username,language_native,language_target\`, + { headers: { 'Authorization': \`Bearer \${userToken}\` } } + ) + const data = await res.json() + if (!res.ok) throw new Error('Profil konnte nicht geladen werden.') + return data.data +} + +export async function registerUser(email, password) { + const res = await fetch(\`\${BASE}/users\`, { + method: 'POST', + headers, + body: JSON.stringify({ email, password }), + }) + const data = await res.json() + if (!res.ok) throw new Error(data.errors?.[0]?.message || 'Registrierung fehlgeschlagen.') + return data.data +} + +export async function checkUsername(username) { + const clean = username.toLowerCase().replace(/[^a-z0-9_]/g, '') + const res = await fetch( + \`\${BASE}/items/users_language?filter[username_lowercases][_eq]=\${encodeURIComponent(clean)}&fields=id&limit=1\`, + { headers } + ) + const data = await res.json() + return data.data?.length === 0 +} + +export async function createProfile({ userId, username, nativeLang, targetLang, userToken }) { + const clean = username.toLowerCase().replace(/[^a-z0-9_]/g, '') + const authHeaders = userToken + ? { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${userToken}\` } + : headers + + const profileRes = await fetch(\`\${BASE}/items/users_language\`, { + method: 'POST', + headers, + body: JSON.stringify({ username_public: username, username_lowercases: clean, status: 'published' }), + }) + const profileData = await profileRes.json() + if (!profileRes.ok) throw new Error(profileData.errors?.[0]?.message || 'Profil konnte nicht erstellt werden.') + const profileId = profileData.data.id + + await fetch(\`\${BASE}/users/\${userId}\`, { + method: 'PATCH', + headers: authHeaders, + body: JSON.stringify({ username: profileId, language_native: nativeLang, language_target: targetLang }), + }) + + await fetch(\`\${BASE}/items/users_language/\${profileId}\`, { + method: 'PATCH', + headers, + body: JSON.stringify({ user: userId }), + }) + + await fetch(\`\${BASE}/items/learning_pairs\`, { + method: 'POST', + headers, + body: JSON.stringify({ + user: profileId, + language_from: nativeLang, + language_to: targetLang, + active: true, + current_level: 1, + points: 0, + }), + }) + + return profileId +} + +export const LANGUAGE_OPTIONS = [ + { id: '88053026-3d7e-4799-b10d-67187f7c1709', label: 'Deutsch', flag: '🇩🇪' }, + { id: '99fbaa9d-3cac-48cb-a5e2-dcb320e913e4', label: 'Englisch', flag: '🇬🇧' }, + { id: '25350b32-e9ab-4fec-946e-c0f11eff70dd', label: 'Schwedisch', flag: '🇸🇪' }, +] +`, + + // ─── Auth Context ───────────────────────────────────────── + 'src/context/AuthContext.jsx': `import { createContext, useContext, useState, useEffect } from 'react' +import { getMe } from '../api/directus' + +const AuthContext = createContext(null) + +export function AuthProvider({ children }) { + const [token, setToken] = useState(() => localStorage.getItem('hejyou_token')) + const [user, setUser] = useState(null) + const [loading, setLoading] = useState(true) + + useEffect(() => { + if (!token) { setLoading(false); return } + getMe(token) + .then(setUser) + .catch(() => { localStorage.removeItem('hejyou_token'); setToken(null) }) + .finally(() => setLoading(false)) + }, [token]) + + const saveToken = (t) => { + localStorage.setItem('hejyou_token', t) + setToken(t) + } + + const logout = () => { + localStorage.removeItem('hejyou_token') + setToken(null) + setUser(null) + } + + return ( + + {children} + + ) +} + +export const useAuth = () => useContext(AuthContext) +`, + + // ─── UI Components ──────────────────────────────────────── + 'src/components/auth/ui.jsx': `import { useState } from 'react' +import styles from './auth.module.css' + +export function FormGroup({ label, children }) { + return ( +
+ {label && } + {children} +
+ ) +} + +export function Input({ className, ...props }) { + return +} + +export function Select({ children, ...props }) { + return ( +
+ +
+
+ ) +} + +export function Button({ loading, children, ...props }) { + return ( + + ) +} + +export function Alert({ message }) { + if (!message) return null + return
{message}
+} + +export function StepDots({ current, total }) { + return ( +
+ {Array.from({ length: total }).map((_, i) => ( +
+ ))} + Schritt {current + 1} von {total} +
+ ) +} +`, + + // ─── Auth CSS Module ────────────────────────────────────── + 'src/components/auth/auth.module.css': `:root { + --bg: #F5F0E8; + --surface: #FFFCF7; + --border: #E2DAD0; + --text: #2C2520; + --muted: #9A8F85; + --accent: #5C7A5E; + --accent-lt: #EAF0EA; + --danger: #C0544A; + --danger-lt: #FBF0EF; + --radius: 14px; +} + +.formGroup { margin-bottom: 16px; } + +.label { + display: block; + font-size: 11px; + font-weight: 500; + color: var(--muted); + letter-spacing: 0.06em; + text-transform: uppercase; + margin-bottom: 6px; +} + +.input { + width: 100%; + padding: 12px 14px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg); + font-family: 'DM Sans', sans-serif; + font-size: 15px; + color: var(--text); + outline: none; + transition: border-color 0.2s, box-shadow 0.2s; + appearance: none; + -webkit-appearance: none; +} +.input:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(92,122,94,0.12); + background: var(--surface); +} + +.selectWrap { position: relative; } +.selectArrow { + position: absolute; + right: 14px; top: 50%; + transform: translateY(-50%); + width: 0; height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 6px solid var(--muted); + pointer-events: none; +} +.selectWrap .input { padding-right: 36px; cursor: pointer; } + +.btn { + width: 100%; + padding: 13px; + margin-top: 8px; + background: var(--accent); + color: #fff; + border: none; + border-radius: var(--radius); + font-family: 'DM Sans', sans-serif; + font-size: 15px; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: background 0.2s, transform 0.1s; +} +.btn:hover { background: #4a6650; } +.btn:active { transform: scale(0.98); } +.btn:disabled { background: var(--border); color: var(--muted); cursor: not-allowed; } + +.spinner { + width: 14px; height: 14px; + border: 2px solid rgba(255,255,255,0.35); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.7s linear infinite; +} + +.alert { + background: var(--danger-lt); + border: 1px solid #EBCBC8; + border-radius: var(--radius); + padding: 10px 14px; + font-size: 13px; + color: var(--danger); + margin-bottom: 16px; +} + +.stepDots { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 24px; +} +.stepDot { + height: 6px; + border-radius: 3px; + transition: all 0.25s ease; +} +.stepLabel { + font-size: 11px; + color: var(--muted); + margin-left: 4px; +} + +@keyframes spin { to { transform: rotate(360deg); } } +`, + + // ─── Login Form ─────────────────────────────────────────── + 'src/components/auth/LoginForm.jsx': `import { useState } from 'react' +import { login, getMe } from '../../api/directus' +import { useAuth } from '../../context/AuthContext' +import { FormGroup, Input, Button, Alert } from './ui' + +export default function LoginForm({ onNeedsProfile }) { + const { saveToken, setUser } = useAuth() + const [email, setEmail] = useState('') + const [pw, setPw] = useState('') + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + + const handleSubmit = async (e) => { + e?.preventDefault() + if (!email || !pw) { setError('Bitte E-Mail und Passwort eingeben.'); return } + setError('') + setLoading(true) + try { + const { access_token } = await login(email, pw) + saveToken(access_token) + const me = await getMe(access_token) + setUser(me) + if (!me.username || !me.language_native || !me.language_target) { + onNeedsProfile(me.id, access_token) + } + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + + return ( +
+ + + setEmail(e.target.value)} autoComplete="email" autoFocus /> + + + setPw(e.target.value)} autoComplete="current-password" /> + + + + ) +} +`, + + // ─── Register Step 1 ────────────────────────────────────── + 'src/components/auth/RegisterStep1.jsx': `import { useState } from 'react' +import { registerUser, login } from '../../api/directus' +import { useAuth } from '../../context/AuthContext' +import { FormGroup, Input, Button, Alert, StepDots } from './ui' + +export default function RegisterStep1({ onSuccess }) { + const { saveToken } = useAuth() + const [email, setEmail] = useState('') + const [pw, setPw] = useState('') + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + + const handleSubmit = async (e) => { + e?.preventDefault() + if (!email || !pw) { setError('Bitte alle Felder ausfüllen.'); return } + if (pw.length < 8) { setError('Passwort muss mindestens 8 Zeichen haben.'); return } + setError('') + setLoading(true) + try { + const newUser = await registerUser(email, pw) + const { access_token } = await login(email, pw) + saveToken(access_token) + onSuccess(newUser.id, access_token) + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + + return ( +
+ + + + setEmail(e.target.value)} autoComplete="email" autoFocus /> + + + setPw(e.target.value)} autoComplete="new-password" /> + + + + ) +} +`, + + // ─── Register Step 2 ────────────────────────────────────── + 'src/components/auth/RegisterStep2.jsx': `import { useState, useRef, useCallback } from 'react' +import { checkUsername, createProfile, LANGUAGE_OPTIONS } from '../../api/directus' +import { useAuth } from '../../context/AuthContext' +import { FormGroup, Input, Select, Button, Alert, StepDots } from './ui' +import styles from './auth.module.css' + +export default function RegisterStep2({ userId, userToken, onSuccess }) { + const { setUser } = useAuth() + const [username, setUsername] = useState('') + const [nativeLang, setNativeLang] = useState('') + const [targetLang, setTargetLang] = useState('') + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + const [usernameState, setUsernameState] = useState('idle') + const debounceRef = useRef(null) + + const handleUsernameChange = useCallback((val) => { + setUsername(val) + setUsernameState('idle') + clearTimeout(debounceRef.current) + if (val.length < 3) return + setUsernameState('checking') + debounceRef.current = setTimeout(async () => { + try { + const available = await checkUsername(val) + setUsernameState(available ? 'available' : 'taken') + } catch { setUsernameState('idle') } + }, 450) + }, []) + + const handleSubmit = async (e) => { + e?.preventDefault() + if (!username) { setError('Bitte einen Username wählen.'); return } + if (usernameState !== 'available') { setError('Bitte einen verfügbaren Username wählen.'); return } + if (!nativeLang) { setError('Bitte Muttersprache wählen.'); return } + if (!targetLang) { setError('Bitte Zielsprache wählen.'); return } + if (nativeLang === targetLang) { setError('Mutter- und Zielsprache dürfen nicht gleich sein.'); return } + setError('') + setLoading(true) + try { + await createProfile({ userId, username, nativeLang, targetLang, userToken }) + setUser(prev => ({ ...prev, username: true, language_native: nativeLang, language_target: targetLang })) + onSuccess(username) + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + + const statusColor = { idle: 'var(--muted)', checking: 'var(--muted)', available: 'var(--accent)', taken: 'var(--danger)' } + const statusText = { idle: '', checking: '…', available: '✓ verfügbar', taken: '✕ vergeben' } + + return ( +
+ + + +
+ handleUsernameChange(e.target.value)} + autoComplete="off" autoFocus style={{ paddingRight: '110px' }} /> + {usernameState !== 'idle' && ( + + {statusText[usernameState]} + + )} +
+
+ + + + + + + + + ) +} +`, + + // ─── Auth Screen ────────────────────────────────────────── + 'src/components/auth/AuthScreen.jsx': `import { useState } from 'react' +import LoginForm from './LoginForm' +import RegisterStep1 from './RegisterStep1' +import RegisterStep2 from './RegisterStep2' +import styles from './AuthScreen.module.css' + +function Brand() { + return ( +
+
+ + + + +
+

HejYou

+

Sprachen lernen wie ein Kind

+
+ ) +} + +function ModeToggle({ mode, onChange }) { + return ( +
+ {['login', 'register'].map(m => ( + + ))} +
+ ) +} + +function SuccessScreen({ username }) { + return ( +
+
+ + + +
+ Willkommen{username ? \`, \${username}\` : ''}! +

Dein Abenteuer beginnt jetzt.

+
+ ) +} + +export default function AuthScreen() { + const [mode, setMode] = useState(() => localStorage.getItem('hejyou_last_mode') || 'login') + const [step, setStep] = useState('main') + const [pendingUserId, setPendingUserId] = useState(null) + const [pendingToken, setPendingToken] = useState(null) + const [successName, setSuccessName] = useState('') + const [showToggle, setShowToggle] = useState(true) + + const handleModeChange = (m) => { + setMode(m) + localStorage.setItem('hejyou_last_mode', m) + setStep('main') + setShowToggle(true) + } + + const handleNeedsProfile = (userId, token) => { + setPendingUserId(userId); setPendingToken(token) + setShowToggle(false); setStep('profile') + } + + return ( +
+
+ + {showToggle && step === 'main' && } + {step === 'main' && mode === 'login' && } + {step === 'main' && mode === 'register' && handleNeedsProfile(id, t)} />} + {step === 'profile' && { setSuccessName(name); setStep('success') }} />} + {step === 'success' && } +
+
+ ) +} +`, + + // ─── AuthScreen CSS Module ──────────────────────────────── + 'src/components/auth/AuthScreen.module.css': `.page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + background: var(--bg, #F5F0E8); +} + +.card { + background: var(--surface, #FFFCF7); + border: 1px solid var(--border, #E2DAD0); + border-radius: 24px; + padding: 48px 44px; + width: 100%; + max-width: 420px; + box-shadow: 0 2px 40px rgba(44,37,32,0.06); + animation: fadeUp 0.3s ease; +} + +.brand { text-align: center; margin-bottom: 36px; } +.brandMark { + width: 48px; height: 48px; + background: var(--accent, #5C7A5E); + border-radius: 50%; + margin: 0 auto 14px; + display: flex; align-items: center; justify-content: center; +} +.brandTitle { + font-family: 'Lora', serif; + font-size: 22px; font-weight: 500; + letter-spacing: -0.3px; + color: var(--text, #2C2520); +} +.brandSub { font-size: 13px; color: var(--muted, #9A8F85); margin-top: 4px; } + +.toggle { + display: flex; + background: var(--bg, #F5F0E8); + border: 1px solid var(--border, #E2DAD0); + border-radius: 10px; + padding: 3px; gap: 3px; + margin-bottom: 32px; +} +.toggleBtn { + flex: 1; padding: 8px; + border: none; border-radius: 8px; + background: transparent; + font-family: 'DM Sans', sans-serif; + font-size: 13px; font-weight: 500; + color: var(--muted, #9A8F85); + cursor: pointer; + transition: all 0.2s; +} +.toggleBtnActive { + background: var(--surface, #FFFCF7); + color: var(--text, #2C2520); + box-shadow: 0 1px 4px rgba(44,37,32,0.08); +} + +.success { text-align: center; padding: 24px 0; } +.successCheck { + width: 52px; height: 52px; + background: var(--accent-lt, #EAF0EA); + border-radius: 50%; + display: flex; align-items: center; justify-content: center; + margin: 0 auto 16px; +} +.successTitle { + font-family: 'Lora', serif; + font-size: 18px; + display: block; margin-bottom: 8px; +} +.successSub { font-size: 14px; color: var(--muted, #9A8F85); } + +@keyframes fadeUp { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: none; } +} +`, + + // ─── App.jsx (ersetzt bestehende) ───────────────────────── + 'src/App.jsx': `import { AuthProvider, useAuth } from './context/AuthContext' +import AuthScreen from './components/auth/AuthScreen' + +function AppContent() { + const { user, loading, logout } = useAuth() + + if (loading) { + return ( +
+
+ +
+ ) + } + + // Nicht eingeloggt oder Profil unvollständig → Auth Screen + if (!user || !user.username || !user.language_native || !user.language_target) { + return + } + + // ─── Eingeloggt → hier kommt dein bestehender App-Content ── + return ( +
+ {/* TODO: Deine bestehenden Pages/Routes hier einbauen */} +

+ Eingeloggt ✓ — hier kommt der Feed. + +

+
+ ) +} + +export default function App() { + return ( + + + + ) +} +`, +} + +// Erstelle alle Verzeichnisse und schreibe Dateien +const dirs = new Set() +for (const path of Object.keys(files)) { + const parts = path.split('/') + for (let i = 1; i < parts.length; i++) { + dirs.add(join(ROOT, ...parts.slice(0, i))) + } +} +for (const dir of dirs) { + try { mkdirSync(dir, { recursive: true }) } catch {} +} + +let written = 0 +for (const [path, content] of Object.entries(files)) { + const fullPath = join(ROOT, path) + // .env nur schreiben wenn noch nicht vorhanden + if (path === '.env') { + try { + const existing = await import('fs').then(m => m.readFileSync(fullPath, 'utf8')) + console.log('⏭ .env bereits vorhanden – übersprungen') + continue + } catch {} + } + writeFileSync(fullPath, content, 'utf8') + console.log(`✓ ${path}`) + written++ +} + +console.log(\`\nFertig! \${written} Dateien geschrieben.\`) +console.log('\nJetzt starten:') +console.log(' npm install (falls neue Abhängigkeiten fehlen)') +console.log(' npm run dev') diff --git a/src/App.jsx b/src/App.jsx index 5c2e3f6..ae079ff 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,7 +15,7 @@ function AppContent() { if (loading) { return ( -
+
) diff --git a/src/api/directus.js b/src/api/directus.js index a8689e2..b1488c0 100644 --- a/src/api/directus.js +++ b/src/api/directus.js @@ -80,9 +80,11 @@ export function langById(id, options) { // ── Feed ────────────────────────────────────────────────────────────────────── -export async function getFeedPairs(userToken, lang = 'de', limit = 20) { +export async function getFeedPairs(userToken, lang = 'de', limit = 20, exclude = []) { + const params = new URLSearchParams({ lang, limit: String(limit) }) + if (exclude.length) params.set('exclude', exclude.join(',')) const res = await fetch( - `${BASE}/auth/feed?lang=${encodeURIComponent(lang)}&limit=${limit}`, + `${BASE}/auth/feed?${params}`, { headers: auth(userToken) } ) const data = await res.json() diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..b0a3792 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1,34 @@ + + Snakkimo Logo + Offenes Buch mit wachsendem Blatt und Sonne, in den App-Farben + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/PairSentenceCard.jsx b/src/components/PairSentenceCard.jsx index 4331a72..7c8238f 100644 --- a/src/components/PairSentenceCard.jsx +++ b/src/components/PairSentenceCard.jsx @@ -1,6 +1,9 @@ -import { useState, useRef } from 'react' +import { useState, useRef, useMemo } from 'react' import confetti from 'canvas-confetti' import usePairAudio from '../hooks/usePairAudio' +import SelectionOverlay from './SelectionOverlay' +import { buildChipTimings, activeChipIdAt } from '../utils/chipTimings' +import { speak } from '../utils/speak' import './PairCards.css' function triggerConfetti() { @@ -14,42 +17,6 @@ function triggerConfetti() { }) } -function SelectionOverlay({ chip }) { - const sels = chip?.selections - if (!sels?.length) return null - const maskId = `selmask-${chip.id.slice(0, 8)}` - const label = chip.label || '' - const toPoints = pts => pts.map(p => `${p.x * 100},${p.y * 100}`).join(' ') - const firstPts = sels[0].points - const xs = firstPts.map(p => p.x * 100) - const ys = firstPts.map(p => p.y * 100) - const cx = (Math.min(...xs) + Math.max(...xs)) / 2 - const labelY = Math.min(Math.max(...ys) + 6, 94) - return ( - - - - - {sels.map((s, i) => )} - - - - {sels.map((s, i) => ( - - ))} - {label && ( - - {label} - - )} - - ) -} - // Sentence format: {{label.w:uuid}} or {{label.o:uuid}} function resolveSentence(sentence, placeholders, onChipClick, activeId) { if (!sentence) return null @@ -87,16 +54,7 @@ function extractVocab(sentence) { .map(m => ({ label: m[1], id: m[2] })) } -// Strip placeholders to plain text for TTS -function toPlainText(sentence) { - if (!sentence) return '' - return sentence - .replace(/\{\{([^.]+)\.[wo]:[0-9a-f-]{36}\}\}/g, '$1') - .replace(/[⟦〚]PH\d+:([^⟧〛]*)[⟧〛]/g, '$1') -} - const LANG_LABELS = { sv: 'Svenska', en: 'English', de: 'Deutsch' } -const LANG_TTS = { sv: 'sv-SE', en: 'en-US', de: 'de-DE' } // Circumference of r=16 circle ≈ 100.53 const RING_C = 2 * Math.PI * 16 @@ -117,7 +75,17 @@ export default function PairSentenceCard({ card, onComplete }) { const hint = stmt?.[`sentence_${native}`] || null const pic = card.picture?.url - const { play, playing } = usePairAudio(stmt?.audio_url) + const { play, playing, currentTime } = usePairAudio(stmt?.audio_url) + + // Karaoke: aktives Chip aus den Audio-Timestamps ableiten, solange vorgelesen wird. + const timings = useMemo( + () => buildChipTimings(sentence, stmt?.audio_alignment), + [sentence, stmt?.audio_alignment], + ) + const audioChipId = playing ? activeChipIdAt(timings, currentTime) : null + const audioObject = audioChipId && card.placeholders?.[audioChipId]?.type === 'object' + ? { id: audioChipId, ...card.placeholders[audioChipId] } + : null const isWord = activeChip && activeChip.type === 'word' const isObject = activeChip && activeChip.type === 'object' && activeChip.selections?.length @@ -129,7 +97,7 @@ export default function PairSentenceCard({ card, onComplete }) { } function handleConfirm() { - if (!unlocked) return + if (done || !unlocked) return setDone(true) setActiveChip(null) triggerConfetti() @@ -137,15 +105,10 @@ export default function PairSentenceCard({ card, onComplete }) { } function handlePlay() { + // play() liefert false, wenn kein Audio-File da ist ODER es nicht geladen + // werden konnte → stiller TTS-Fallback. if (play()) { setUnlocked(true); return } - // Fallback: Browser-TTS, falls kein Audio-File vorhanden - if (!window.speechSynthesis || !sentence) return - window.speechSynthesis.cancel() - const utt = new SpeechSynthesisUtterance(toPlainText(sentence)) - utt.lang = LANG_TTS[lang] || 'de-DE' - utt.rate = 0.9 - window.speechSynthesis.speak(utt) - setUnlocked(true) + if (speak(sentence, lang)) setUnlocked(true) } function startHold() { @@ -179,6 +142,7 @@ export default function PairSentenceCard({ card, onComplete }) {
)} {isObject && } + {!isObject && audioObject && }
e.stopPropagation()} style={{ paddingTop: 18 }}> @@ -189,7 +153,7 @@ export default function PairSentenceCard({ card, onComplete }) {

- {resolveSentence(sentence, card.placeholders, handleChipClick, activeChip?.id)} + {resolveSentence(sentence, card.placeholders, handleChipClick, audioChipId ?? activeChip?.id)}

{hint && (

pts.map(p => `${p.x * 100},${p.y * 100}`).join(' ') - const firstPts = sels[0].points - const xs = firstPts.map(p => p.x * 100) - const ys = firstPts.map(p => p.y * 100) - const cx = (Math.min(...xs) + Math.max(...xs)) / 2 - const labelY = Math.min(Math.max(...ys) + 6, 94) - return ( - - - - - {sels.map((s, i) => )} - - - - {sels.map((s, i) => ( - - ))} - {label && ( - - {label} - - )} - - ) -} - // Sentence format: {{label.w:uuid}} or {{label.o:uuid}} function resolveSentence(sentence, placeholders, onChipClick, activeId) { if (!sentence) return null @@ -106,7 +73,17 @@ export default function PairWordCard({ card, onComplete }) { const hint = q?.[`sentence_${native}`] || null const pic = card.picture?.url - const { play, playing } = usePairAudio(q?.audio_url) + const { play, playing, currentTime } = usePairAudio(q?.audio_url) + + // Karaoke: aktives Chip aus den Audio-Timestamps der Frage ableiten. + const timings = useMemo( + () => buildChipTimings(sentence, q?.audio_alignment), + [sentence, q?.audio_alignment], + ) + const audioChipId = playing ? activeChipIdAt(timings, currentTime) : null + const audioObject = audioChipId && card.placeholders?.[audioChipId]?.type === 'object' + ? { id: audioChipId, ...card.placeholders[audioChipId] } + : null const options = useMemo(() => { const pos = (stmt?.positive_words || []).map(w => ({ ...w, correct: true })) @@ -118,6 +95,11 @@ export default function PairWordCard({ card, onComplete }) { setActiveChip(prev => prev?.id === id ? null : { id, ...entry }) } + // Audio-File abspielen; bei fehlendem/nicht ladbarem File still auf TTS zurückfallen. + function handlePlay() { + if (!play()) speak(sentence, lang) + } + function handleSelect(opt) { if (confirmed) return setSelectedIds(prev => { @@ -156,14 +138,15 @@ export default function PairWordCard({ card, onComplete }) {

)} {isObject && } + {!isObject && audioObject && }
e.stopPropagation()} style={{ paddingTop: 18 }}>
-

play()}> - {resolveSentence(sentence, card.placeholders, handleChipClick, activeChip?.id)} +

+ {resolveSentence(sentence, card.placeholders, handleChipClick, audioChipId ?? activeChip?.id)}

{hint && !confirmed && (

@@ -171,7 +154,7 @@ export default function PairWordCard({ card, onComplete }) {

)}
-
)} {isObject && } + {!isObject && audioObject && }
e.stopPropagation()} style={{ paddingTop: 18 }}> @@ -134,7 +117,7 @@ export default function PairYesNoCard({ card, onComplete }) {

play()}> - {resolveSentence(sentence, card.placeholders, handleChipClick, activeChip?.id)} + {resolveSentence(sentence, card.placeholders, handleChipClick, audioChipId ?? activeChip?.id)}

{hint && !result && (

@@ -142,7 +125,7 @@ export default function PairYesNoCard({ card, onComplete }) {

)}
-