diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5908553 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# LyRemember — environment variables + +# JWT signing secret for user authentication. +# Required in production. If unset, an ephemeral random secret is generated +# at startup and a warning is logged — JWT tokens then become invalid after +# every process restart. +# +# Generate a strong value, for example: +# openssl rand -hex 32 +LYREMEMBER_JWT_SECRET= diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 23a07d8..7845e82 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -36,5 +36,8 @@ jobs: - name: TypeScript check run: npx vue-tsc --noEmit + - name: Unit tests (Vitest) + run: npm run test:unit + - name: Build run: npx vite build diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml index 000fe18..72243e7 100644 --- a/.github/workflows/ci-python.yml +++ b/.github/workflows/ci-python.yml @@ -1,25 +1,24 @@ -name: CI Python +name: CI Python (legacy POC) on: push: branches: [main] paths: - - 'lyremember/**' - - 'tests/**' - - 'requirements.txt' + - 'legacy/python-cli/**' - '.github/workflows/ci-python.yml' pull_request: branches: [main] paths: - - 'lyremember/**' - - 'tests/**' - - 'requirements.txt' + - 'legacy/python-cli/**' - '.github/workflows/ci-python.yml' jobs: test: - name: Python Tests + name: Python Tests (legacy) runs-on: ubuntu-latest + defaults: + run: + working-directory: legacy/python-cli steps: - uses: actions/checkout@v4 @@ -28,6 +27,7 @@ jobs: with: python-version: '3.11' cache: pip + cache-dependency-path: legacy/python-cli/requirements.txt - name: Install dependencies run: pip install -r requirements.txt pytest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56442a5..573022f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,12 @@ jobs: working-directory: lyremember-app env: NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.0.12077973 - run: npx tauri android build --apk --debug + # PyO3 (feature `python`) n'est pas cross-compilable vers Android. + # On passe --no-default-features pour exclure la feature `python` + # activee par defaut sur desktop. La phonetique JP/KR/FR/EN + # renverra alors le stub d'erreur jusqu'a ce qu'une alternative + # Rust pure soit fournie (post-MVP). + run: npx tauri android build --apk --debug -- --no-default-features - name: Find and rename APK id: find-apk diff --git a/.gitignore b/.gitignore index 5d5e29c..be15e93 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,8 @@ eggs/ # Ignore Python lib directories but not TypeScript/JavaScript lib directories /lib/ /lib64/ -lyremember/lib/ -lyremember/lib64/ +legacy/python-cli/lyremember/lib/ +legacy/python-cli/lyremember/lib64/ parts/ sdist/ var/ @@ -37,9 +37,9 @@ ENV/ *~ # Data files (user data should not be committed) -data/songs.json -data/progress.json -data/config.json +legacy/python-cli/data/songs.json +legacy/python-cli/data/progress.json +legacy/python-cli/data/config.json *.db *.db-shm *.db-wal diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0617939 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,77 @@ +# Changelog + +Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), versionnage suivant [SemVer](https://semver.org/lang/fr/). + +## [Unreleased] + +### Added +- Index documentaire `docs/INDEX.md` regroupant les 16 fichiers de design par thème. +- Mode invité : utiliser l'application sans créer de compte. +- Tests e2e WebDriverIO avec le driver Tauri. +- Suite de tests Rust complète sur le backend (77 tests). +- Workflows CI/CD pour frontend, Python et Rust (lint, build, release multi-plateforme). +- Build Android (APK debug installable) et release Windows + Android. +- Internationalisation (i18n) sur les vues principales, page Settings, intégration UI Genius API. +- Refonte UI : thème dark gold aligné sur le site, navigation mobile en bas. +- Modes de pratique : Karaoke, Fill-blank, MCQ, Oral (UI Phase 4). + +### Removed +- `docs/TECH_STACK_FINAL.md` (#7) : doublait `docs/FINAL_DECISIONS.md` et décrivait une stack (PWA/React/FastAPI) abandonnée. La source canonique est désormais `docs/FINAL_DECISIONS.md`. + +### Changed +- Réorganisation de la documentation : les 15 fichiers `.md` de design sont passés de la racine à `docs/` ; seul `README.md` reste à la racine. +- Corrections doc : références à `deep-translator` remplacées par `LibreTranslate` (service réellement utilisé dans `rust-backend/src/services/translation.rs`) dans `docs/TECH_CHOICES.md` et `docs/RUST_OPTION.md` (#8). +- `docs/FINAL_DECISIONS.md` aligné sur la réalité du code : UI library = composants maison `lyremember-app/src/components/ui/` (Button, Card, Input, Alert, Spinner). Note ajoutée en tête de `docs/UI_LIBRARIES.md` pour marquer ce comparatif comme historique (#9). +- Commandes Tauri : doc corrigée de 16 → 19 (5 auth + 7 songs + 4 practice + 3 util), avec liste exhaustive synchronisée sur `commands.rs`. Mentions "16" alignées dans `README.md` et `docs/TAURI_INTEGRATION_COMPLETE.md` (#10). +- `docs/ARCHITECTURE.md` clarifie en tête : ce doc décrit le POC Python (legacy, storage JSON), la prod canonique est Rust+Tauri+Vue avec storage SQLite (`rust-backend/src/db/sqlite.rs`). Référence à `legacy/python-cli/` et à `FINAL_DECISIONS.md` (#11). +- `docs/USER_STORIES_V2.md` : Epic 5 (Mode oral) descendu en CouldHave, scope MVP = self-assessment manuel, mode micro live → post-MVP avec pointeur vers #28 (#12). +- `docs/USER_STORIES_V2.md` : Epics 4-7 préfacés par une note de navigation expliquant les rôles distincts `PracticeView` (apprendre) vs `SongDetailView` (lyrics), pointeur vers #19 (#13). +- `docs/USER_STORIES_V2.md` : US-1.3 (Genius) refondue — import de lyrics interdit (ToS + API), remplacée par champ `genius_url` optionnel (#17) + spike API métadonnées (#26) + cleanup UI (#18). US-2.1 ajustée en conséquence (#14). +- Build Tauri : feature `python` (PyO3 → phonétique JP/KR/FR/EN) **activée par défaut** sur desktop dans `lyremember-app/src-tauri/Cargo.toml`. Le build Android passe `--no-default-features` (PyO3 non cross-compilable vers Android). `docs/ARCHITECTURE.md` documente le flag (#15). + +### Security +- JWT signing secret n'est plus hardcoded (`rust-backend/src/services/auth.rs`). Le secret est désormais lu depuis la variable d'environnement `LYREMEMBER_JWT_SECRET`. Fallback dev : secret éphémère aléatoire (32 bytes via `uuid::Uuid::new_v4`) + warning sur stderr — les tokens deviennent invalides à chaque redémarrage. `.env.example`, `README.md`, `docs/ARCHITECTURE.md` mis à jour (#16). + +### Added +- Champ optionnel `genius_url` sur les chansons : exposé via `CreateSongData` / `UpdateSongData` côté Rust, transmis par `cmd_create_song` et `cmd_update_song`, saisi dans `AddSongView` et affiché en lien sortant (`target="_blank" rel="noopener noreferrer"`) sur `SongDetailView`. La colonne SQL existait déjà — seul le wiring service/commands/UI manquait. Sémantique update : `None` = ne pas toucher, `Some("")` = clear, `Some(url)` = écrase. i18n FR/EN ajoutés. **Aucune extraction de paroles** depuis Genius (interdit par ToS) — c'est un simple lien (#17). + +### Removed +- `SettingsView.vue` : section "Genius API" supprimée (token, sauvegarde localStorage, recherche, import). L'interface laissait croire à un import de paroles alors que ce n'est pas légalement faisable. Clés i18n `settings.integrations`, `settings.geniusApi`, `settings.geniusToken*`, `settings.geniusDesc`, `settings.geniusHelp`, `settings.tokenSaved`, `settings.searchSongs`, `settings.searchPlaceholder`, `settings.search`, `settings.import`, `settings.noResults` retirées de FR/EN (#18). + +### Improved +- `PracticeView.vue` accepte désormais un query param `?songId=` qui pré-sélectionne (déplie) la chanson dans la liste. `SongDetailView.vue` expose un raccourci "Ouvrir dans Practice →" à côté de la section Modes qui route vers `/practice?songId=...`. Confirme la séparation des rôles : Practice = hub d'apprentissage, SongDetail = gestion lyrics + raccourcis directs vers les modes (#19). +- i18n : locales `ja.json` (日本語) et `ko.json` (한국어) ajoutées ; sélecteur de langue dans Settings (4 langues). Couverture 100% des 151 clés UI. Détection automatique de `navigator.language` étendue aux 4 codes (#20). +- `SongDetailView.vue` : vue lyrics enrichie en 3 niveaux **VO + Phonétique + Traduction**. La phonétique (générée par PyO3 → pykakasi/hangul-romanize/epitran, voir #15) s'affiche en italique mono sous chaque ligne VO quand `song.phonetic_lyrics` est présent. La traduction reste en italique dorée sous la phonétique quand sélectionnée. Pas de toggle horizontal pour éviter la friction mobile ; l'ordre vertical respecte l'esprit "3 colonnes" promis (#21). +- `McqMode.vue` : génération de distractors améliorée. Les distractors sont désormais classés par proximité de longueur avec la bonne réponse (∆ characters) avant d'être mélangés — plus difficile à éliminer visuellement que des lignes aléatoires. Fallback "word-scrambled" puise dans les autres lignes plutôt que de répéter la bonne réponse. Pas de doublons garanti (#22). +- `services/phonetic.rs` court-circuite désormais l'input vide sans démarrer le runtime Python (perf + simplicité de test). + +### Docs / Spikes +- `docs/spikes/2026-05-genius-api.md` : rapport R&D sur l'API métadonnées Genius — endpoints utilisables (search, songs, artists), valeur produit limitée (auto-complétion à la création), recommandation **NO-GO court terme**. Lien sortant `genius_url` (#17) suffit pour le cas d'usage légal (#26). +- `docs/spikes/2026-05-song-detection.md` : rapport R&D sur la détection de chanson en cours d'écoute (Shazam-like) — comparatif AudD / ACRCloud / Shazam / Chromaprint+MusicBrainz, faisabilité capture audio Tauri desktop+Android, aspects légaux RGPD, coûts estimés 100/1000 utilisateurs. Recommandation **NO-GO MVP** (effort 1-2 semaines + coûts API récurrents) (#27). +- `docs/INDEX.md` : nouvelle section "Spikes R&D" référençant les deux rapports. + +### Testing +- Tests `services::phonetic` étoffés (#23) : empty input passes through tous langs ; ajout de tests pour la branche stub (`#[cfg(not(feature = "python"))]`) couvrant erreur sur langs supportés + passthrough sur lang non-supportée ; ajout test FR (`fra-Latn`) ignored ; ajout dispatcher test ; documentation des prérequis Python dans le module. Suite globale : 92 passed, 6 ignored, 0 failed avec feature `python` ; 91 passed, 0 ignored, 0 failed sans (couvre les deux branches CI). +- Vitest infrastructure côté frontend (#24) : `vitest.config.ts` (jsdom + alias `@/*`), scripts npm `test:unit` (run-once) et `test:unit:watch`, devDeps `vitest` + `@vue/test-utils` + `jsdom`. Trois tests pilotes : `Button.spec.ts` (4 cas : rendering slot, click, loading, variant), `Alert.spec.ts` (3 cas : hidden, message+variant, close emit), `songs.spec.ts` (4 cas : filter search, filter lang, group by lang, count). Workflow CI Frontend exécute `npm run test:unit` entre le typecheck et le build. README + CONTRIBUTING.md mis à jour. +- Tests `ui.spec.ts` couvrant dark mode (#31) : `toggleDarkMode` flip + DOM class + localStorage, `initializeDarkMode` saved value priorité, fallback `prefers-color-scheme`. +- Mode oral **live (expérimental)** dans `OralMode.vue` (#28). Utilise la Web Speech API native du webview (Chrome/WebView2/WRY-WebKit selon plateforme), avec **fallback gracieux** sur le self-assessment manuel existant si l'API n'est pas exposée. Bouton "Speak" qui démarre l'écoute, mappe la langue de la chanson (`fr`→`fr-FR`, `jp`→`ja-JP`, `kr`→`ko-KR`, `en`→`en-US`), calcule un score via `scoreSpoken` (token-set recall, seuil 70% pour auto-validation). Module pur `lib/oral-scoring.ts` (`normalize`, `tokenize`, `scoreSpoken`, `hasSpeechRecognition`) + 12 tests Vitest. Pas de capture audio Tauri native — donc fonctionnel sur desktop si le webview expose `SpeechRecognition`, sur Android dépend de la version de WebView. Spike de #27 (capture native + STT serveur) reste pertinent pour le mode oral live définitif. +- Stats : streak quotidien + recommandations de chansons (#29). Côté Rust : `compute_streak_from_dates` (pure, testable avec NaiveDate contrôlée — gère cas vide, sessions multiples par jour dédupliquées, gap > 1 jour reset à 0, anchor today/yesterday accepté), `get_user_streak` (DB) et `get_recommendations` (chansons les moins maîtrisées, ORDER BY AVG(score) ASC, limit configurable). 2 commandes Tauri : `cmd_get_user_streak`, `cmd_get_recommendations`. Frontend : `getUserStreak` / `getRecommendations` dans tauri-api, `useUserStats` composable étendu, badge "🔥 N jours" sur `DashboardView` quand streak > 0. i18n FR/EN/JP/KR : `dashboard.streakDays` (pluriel) + `dashboard.streakTooltip`. 9 tests Rust ajoutés. +- Import de fichier `.txt` / `.json` / `.lrc` dans `AddSongView` (#30). Parsers purs dans `lib/file-parsers.ts` (sans dépendance Tauri, lit `File.text()` côté navigateur) : `parseTxt` (1 ligne = 1 lyric, ignore `#comment` et lignes vides), `parseJson` (`{title?, artist?, language?, lyrics: string[]}`), `parseLrc` (timestamps `[mm:ss.xx]` stripés, tags `[ti:]` `[ar:]` `[la:]` extraits en métadonnées). Input `` dans le formulaire, mappe sur les champs existants. 16 tests Vitest dans `file-parsers.spec.ts`. i18n FR/EN/JP/KR mis à jour (`addSong.importFile`, `addSong.importFileHint`). +- Couverture de tests étendue côté `legacy/python-cli/` (#25) : `test_models.py` (User/Song/PracticeSession/SongProgress, round-trip + verify_password), `test_practice_engine.py` (fill-blank, flashcard, line-by-line, session accounting), `test_user_manager.py` (register, login, repertoire, genius token — via Storage mocké car la couche storage legacy n'expose pas les méthodes user), `test_genius_api.py` (init + degradation gracieuse sans token). 68 tests passent, **coverage moyenne 81% sur les 4 modules ciblés** (practice_engine 100%, models 100%, user_manager 96%, genius_api 30%). +- Le CLI Python (proof of concept) est archivé dans `legacy/python-cli/` (#6) : `lyremember/`, `tests/`, `data/`, `demo.py`, `setup.py`, `requirements.txt` y vivent désormais. La stack canonique est Rust + Tauri + Vue 3. Le workflow `ci-python` cible ce nouveau chemin. +- Polish `SongDetailView` : layout des paroles et états hover affinés. +- PyO3 rendu optionnel pour faciliter les builds cross-platform. +- `reqwest` basculé d'OpenSSL vers rustls pour la compatibilité Android. + +### Fixed +- Lien cassé vers `IMPLEMENTATION_SUMMARY.md` dans le `README.md` (pointe désormais vers `rust-backend/`). +- Erreurs de lint TypeScript : imports et paramètres inutilisés retirés. +- Warnings clippy : closures redondantes remplacées par des références de fonction. +- Couleurs grises résiduelles dans les vues et le mode karaoké. +- Signatures de Tauri commands alignées sur l'API backend réelle. +- Compilation cross-Android des commandes Tauri. +- Installation APK via `termux-open` et création de chanson (#5). + +## Notes + +Ce changelog est introduit en cours de route ; l'historique complet est accessible via `git log`. Les prochaines releases déclencheront des sections versionnées dédiées. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8d483ff --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,68 @@ +# Contributing to LyRemember + +Merci de votre intérêt ! Ce document explique comment proposer une contribution. + +## Avant de commencer + +- Lire le [README](README.md) et l'index docs ([docs/INDEX.md](docs/INDEX.md)). +- Vérifier les [issues ouvertes](https://github.com/RebelliousSmile/lyremember/issues) et la roadmap dans [docs/USER_STORIES_V2.md](docs/USER_STORIES_V2.md). +- Pour une feature non triviale, ouvrir une issue de discussion avant d'écrire du code. + +## Stack + +Trois bases de code coexistent dans le repo — choisir la bonne selon le sujet : + +| Dossier | Stack | Domaine | +|---|---|---| +| `lyremember/` + `tests/` | Python 3.8+ | CLI / proof of concept | +| `rust-backend/` | Rust + SQLite + PyO3 | Backend Tauri | +| `lyremember-app/` | Vue 3 + TypeScript + Tauri | Desktop / Mobile UI | + +## Workflow + +1. Forker le repo et créer une branche depuis `main` : `feat/`, `fix/`, `docs/`, `refactor/`. +2. Écrire les tests **avant** le code (TDD red → green → refactor). +3. Lancer la suite de tests locale (voir ci-dessous) et s'assurer qu'elle est verte. +4. Commit en [Conventional Commits](https://www.conventionalcommits.org/) : `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`. Inclure `(#NN)` si une issue est liée. +5. Pousser la branche et ouvrir une Pull Request claire : contexte, changements, sortie observable, tests joués. + +## Lancer les tests + +```bash +# Python CLI +pip install -r requirements.txt pytest +python -m pytest tests/ -q + +# Rust backend +cd rust-backend && cargo test + +# Frontend Vue + Tauri +cd lyremember-app && npm install +npm run test:unit # Vitest (composants, stores) +npm run test:e2e # WebDriverIO (parcours utilisateur) +``` + +## Style & conventions + +- **Python** : respecter les patterns existants (Click, pyyaml, type hints quand utile). Pas d'`except Exception` nu. +- **Rust** : `cargo fmt`, `cargo clippy --all-targets -- -D warnings` avant push. +- **TypeScript/Vue** : ESLint et conventions Vue 3 ` diff --git a/lyremember-app/src/views/SongDetailView.vue b/lyremember-app/src/views/SongDetailView.vue index 7068d67..60d2431 100644 --- a/lyremember-app/src/views/SongDetailView.vue +++ b/lyremember-app/src/views/SongDetailView.vue @@ -34,6 +34,15 @@

{{ song.artist }}

+ + {{ $t('songDetail.openOnGenius') }} + +