Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
864d4bf
docs: move design docs to docs/ and add an index
claude May 17, 2026
e9e6b34
chore: add LICENSE (MIT), CONTRIBUTING and CHANGELOG
claude May 17, 2026
8b02c11
chore: archive Python CLI proof of concept under legacy/python-cli/ (#6)
claude May 17, 2026
dae33ba
docs: remove obsolete TECH_STACK_FINAL.md (#7)
claude May 17, 2026
263bee4
docs: corriger les references deep-translator vers LibreTranslate (#8)
claude May 17, 2026
041a348
docs: aligner FINAL_DECISIONS.md sur composants UI maison (#9)
claude May 17, 2026
4e4bb3d
docs: corriger 16 -> 19 commandes Tauri et detailler la ventilation (…
claude May 17, 2026
8e86ff1
docs: clarifier storage POC = JSON et prod = SQLite dans ARCHITECTURE…
claude May 17, 2026
4b0c888
docs: USER_STORIES_V2 — Epic 5 CouldHave, roles PracticeView/SongDeta…
claude May 17, 2026
8122d2a
build: activer feature python par defaut dans Tauri desktop (#15)
claude May 17, 2026
a877b79
fix(security): load JWT secret from LYREMEMBER_JWT_SECRET env var (#16)
claude May 17, 2026
9875f00
feat(songs): expose optional genius_url field across stack (#17)
claude May 17, 2026
cdf1cab
refactor(ui): retirer la section Genius API de SettingsView (#18)
claude May 17, 2026
bf306d9
feat(practice): PracticeView accepte ?songId= et SongDetailView point…
claude May 17, 2026
f618fb1
feat(i18n): add Japanese and Korean locales (#20)
claude May 17, 2026
5242e83
feat(songs): afficher VO + Phonetique + Traduction sur SongDetailView…
claude May 17, 2026
dd67de7
feat(mcq): smarter distractor generation in McqMode (#22)
claude May 17, 2026
bf35546
test(rust): cover phonetic stub branch and short-circuit empty input …
claude May 17, 2026
f7f0b2b
test(frontend): bootstrap Vitest with 3 pilot specs and wire it in CI…
claude May 17, 2026
e4a478f
docs: deux spikes R&D Genius API + song detection (#26 #27)
claude May 17, 2026
31ff42a
test(ui): cover existing dark mode store behavior (#31)
claude May 17, 2026
1070c8e
test(python-cli): cover models, practice_engine, user_manager, genius…
claude May 17, 2026
805ba3f
feat(import): .txt / .json / .lrc file import in AddSongView (#30)
claude May 17, 2026
062ee52
feat(stats): daily streak + recommendations end-to-end (#29)
claude May 17, 2026
8e36055
feat(practice): experimental live oral mode via Web Speech API (#28)
claude May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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=
3 changes: 3 additions & 0 deletions .github/workflows/ci-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 8 additions & 8 deletions .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand All @@ -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
Expand Down
77 changes: 77 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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=<id>` 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 `<input type="file" accept=".txt,.json,.lrc">` 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.
68 changes: 68 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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/<sujet>`, `fix/<sujet>`, `docs/<sujet>`, `refactor/<sujet>`.
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 `<script setup>`.
- **Markdown** : pas de duplication entre docs ; un sujet, un fichier canonique, on référence depuis les autres.
- **Pas de secrets** : jamais de clé API en clair ; utiliser les variables d'environnement / store Tauri.

## Code review

Toute PR passe en revue. Critères :
- Tests qui décrivent le comportement (pas le type).
- DRY : pas de logique dupliquée 3+ fois.
- Logs sur les chemins de production critiques.
- Aucune référence à des chemins, URLs ou secrets hard-codés.
- Documentation mise à jour si la PR touche une convention publique.

## Reporting de bug

Ouvrir une issue avec : environnement (OS, version Node/Rust/Python), étapes de reproduction, comportement observé vs attendu, logs.

## Licence

En contribuant, vous acceptez que votre travail soit publié sous la même licence que le projet ([MIT](LICENSE)).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 LyRemember Team

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading