feat(search): panneau de filtres unifié, clavier/focus du typeahead
CI / build-and-test (push) Successful in 14m10s

Barre de recherche
- Panneau de filtres unifié (remplace le ProviderPicker) : sources, type
  (vidéos/shorts/direct/chaînes), période (heure/jour/semaine/mois/année),
  durée (<4 / 4-20 / >20 min) et tri. Pastilles + roving tabindex
  (←/→ dans un groupe, ↑/↓ entre groupes), focus trap, Échap, Ctrl/⌘+Maj+F,
  recherches récentes, « mémoriser par défaut », « Réinitialiser ».
- Pastilles des filtres actifs sous la barre (retrait en un clic) + compteur.
- Typeahead : un seul keydown (fin du double Enter), ↑/↓ bouclants,
  Home/End, PageUp/PageDown, Entrée valide la ligne surlignée (sinon
  recherche brute), Tab complète sans chercher, Échap ferme puis vide,
  ligne « Rechercher <q> », défilement de l'option active, aria à jour.
- Focus : le panneau se referme dès que le focus quitte la barre
  (focusout + relatedTarget + activeElement), pointerdown neutralisé sur les
  lignes pour garder le focus dans l'input (plus de scintillement).
- Opérateurs en clair : `linux live:`, `tuto today: long:` deviennent des
  filtres et sont retirés de la requête (autocomplétion après le `:`).

Filtres côté serveur
- Modèle partagé : src/app/search/filters.ts + server/search-filters.mjs.
- /api/search?type=&duration=&period=&sort= : InnerTube (upload_date, type,
  duration, features), Data API v3 (type, videoDuration, publishedAfter),
  yt-dlp --dateafter ; post-filtrage pour les providers sans filtre natif.
  Un champ manquant ne fait jamais disparaître un résultat.
- Filtres dans l'URL (partageables) + adapters + SearchService (clé de cache).
- Correction : la recherche Shorts renvoyait 0 (ShortsLockupView/GridShelfView
  non mappés, id/titre dans l'endpoint) -> mappage + shelf + vignette/vues.

Tests / docs
- 23 tests unitaires (clavier, focus, opérateurs, panneau de filtres),
  server/tests/search-filters.test.mjs (npm run test:filters) + CI,
  scénarios e2e des filtres, README/api/README/todo/MCP à jour.
This commit is contained in:
2026-09-28 17:42:19 -04:00
parent 9fb0ce7549
commit f1f673040e
40 changed files with 3111 additions and 938 deletions
+4 -2
View File
@@ -19,6 +19,7 @@
- [x] Step 14: Update README with search UX section, keyboard shortcuts and usage notes
- [x] Step 15: Auto-complétion de la **requête** (typeahead) dans la barre de recherche
- [x] Step 16: **Transcripts** de vidéos sur la page Watch (API + UI, Phase 1 du doc)
- [x] Step 19: Panneau de filtres unifié (remplace le ProviderPicker) + filtres serveur + clavier/focus du panneau de suggestions
---
@@ -154,7 +155,7 @@ valeur / coût pour l'existant.
| # | Fonction | Description | Effort |
|---|---|---|---|
| 1 | **Filtres de recherche** | Durée (< 4 min / 4-20 / 20+), type (vidéo/chaîne/playlist), date de mise en ligne, langue. `SearchService.sort$` existe déjà, il manque les params + l'UI en chips + le mapping par adapter | + |
| 1 | **Filtres de recherche** | ✅ DONE : modèle partagé (`src/app/search/filters.ts` + `server/search-filters.mjs`), panneau unique (sources / type / période / durée / tri) au clavier, filtres dans l'URL + `/api/search`, appliqués nativement (InnerTube, Data API, `--dateafter`) ou en post-traitement, opérateurs `live:`/`today:`/`long:`, langue encore locale | ~ |
| 2 | **Pagination / infinite scroll** | ✅ DONE (Step 18) : backend pages illimitées via continuations InnerTube (`page=2,3…`, `pageSize` ≤ 50) + front `loadNextPage()` + `<app-infinite-anchor>` + `mergeGroups` + `endReached` (`search.component.ts`) | ~ |
| 3 | **Dédup multi-provider** | La même vidéo existe souvent sur Rumble/Odysee/PeerTube : regrouper par titre+durée+chaîne et afficher « aussi disponible sur… » | + |
| 4 | **Recherche dans les résultats** (« search within results ») | Relancer la requête en restreignant au provider/à la chaîne déjà affichée | ~ |
@@ -193,7 +194,8 @@ valeur / coût pour l'existant.
## Test commands
```bash
npm run test:search # Step 11 — unit tests: SearchService + @ parsing + ProviderPicker
npm run test:search # Step 11/19 — unit tests: SearchService, @ parsing, clavier/focus, panneau de filtres
npm run test:filters # Step 19 — filtres serveur (offline)
npm run test:search-e2e # Step 12 — e2e scenarios against a real isolated server
npm run test:preferences # Step 9 — defaultProviders persistence
npm run test:telemetry # Step 13 — telemetry events (insert/list/count)