Files
gilles 2129da4f55 fix(notes): audio overflow + volume 50%, grille 3col, bouton laptop, sidebar BDD
- Audio : minWidth:0 + onLoadedMetadata volume=0.5 (plus de débordement)
- Grille : repeat(3,1fr) sur laptop, 1fr sur mobile (était auto-fill)
- Header laptop : bouton "Nouvelle note" (fa-plus + accent) visible lg:flex
- SideNav : DbStatusBar en bas — LED verte/rouge + taille BDD, polling 30s
- docs/plan.md : Phase 4c documentée

v0.5.7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:51:44 +02:00

17 KiB
Raw Permalink Blame History

HomeHub — Plan de développement

Approche itérative : chaque phase livre quelque chose d'utilisable.
Stack : FastAPI · React/Vite/TS · PostgreSQL 16 · Docker Compose


Phase 1 — Socle technique

Objectif : environnement opérationnel, rien de métier, mais tout tourne.

Backend

  • Structure projet FastAPI (app/api/, app/core/, app/models/, app/schemas/, app/services/)
  • Configuration SQLAlchemy 2.0 async + pool de connexions
  • Création des schémas PostgreSQL (todos, shopping, notes) via Alembic
  • Migration initiale : toutes les tables (voir spec section 3)
  • Endpoint santé : GET /api/health
  • Module media (app/services/media.py) :
    • Validation des formats acceptés (JPG, PNG, SVG, WebP, WebM, M4A)
    • Génération miniature Pillow : 150×150 (shopping), 300×300 (notes), 400×300 (inline)
    • Sauvegarde originals/ + thumbnails/ sur le volume
    • Endpoint POST /api/media/upload → retourne { file_path, thumbnail_path }
    • Endpoint DELETE /api/media/{uuid} → supprime original + miniature
  • Middleware CORS pour réseau local 10.0.0.0/22
  • Configuration via variables d'environnement (.env)
  • Dockerfile backend (Python 3.12 slim)

Frontend

  • Scaffold Vite + React 18 + TypeScript
  • Tailwind CSS configuré avec les tokens Gruvbox (via tokens.json)
  • Intégration design_system/components/ui-kit.jsx
  • @vite-pwa/plugin configuré (Service Worker + manifest)
  • manifest.json avec icônes iOS + Android
  • Routage React Router v6
  • Layout de base : navigation mobile (bottom bar) + navigation laptop (sidebar)
  • Dockerfile frontend (Nginx + proxy /api/ → backend)
  • Favicon maison (SVG Gruvbox orange)

Infra

  • docker-compose.yml avec 3 services (frontend, backend, db)
  • Volume PostgreSQL persistant
  • Volume uploads persistant (/uploads/)
  • Fichier .env.example
  • Script de seed (backend/app/data/seed.py) : 113 produits + 9 magasins

Phase 2 — Module Todos

Objectif : créer, lister, modifier, terminer et reporter des tâches depuis mobile et laptop.

Backend

  • GET /api/todos — liste avec filtres (domaine, statut, priorité, tags, période)
  • POST /api/todos — création
  • PATCH /api/todos/{id} — mise à jour partielle
  • DELETE /api/todos/{id} — suppression
  • POST /api/todos/{id}/postpone — incrémente postponed_count + décale due_date
  • Schémas Pydantic : TodoCreate, TodoUpdate, TodoResponse
  • Tests d'intégration (9 tests)

Frontend Mobile

  • Page Todos : liste des tâches en cours, groupées par domaine
  • Bouton "+ Tâche" flottant → Modal création (titre + domaine + date + priorité + tags)
  • Double-tap → Modal édition pré-rempli
  • Swipe droite → marquer done
  • Swipe gauche → actions (reporter 1j / reporter 1 semaine / supprimer)
  • Badge compteur par domaine

Frontend Laptop

  • Vue tableau avec filtres : domaine, statut, priorité, période
  • Formulaire complet (tous les champs) via Modal
  • Double-clic sur titre → Modal édition
  • Actions inline : ✓ done / +1j / +1S / ✕ supprimer

Phase 3 — Module Shopping (liste de courses)

Objectif : liste de courses fonctionnelle en magasin depuis smartphone, avec génération automatique.

Backend

  • GET /api/shopping/stores — liste magasins
  • GET /api/shopping/products — catalogue avec recherche (param q)
  • GET /api/shopping/lists — listes de courses avec compteurs
  • POST /api/shopping/lists — création liste
  • GET /api/shopping/lists/{id} — détail liste avec articles
  • PATCH /api/shopping/lists/{id} — mise à jour liste
  • DELETE /api/shopping/lists/{id} — suppression
  • POST /api/shopping/lists/{id}/items — ajout article (produit catalogue ou custom)
  • PATCH /api/shopping/lists/{id}/items/{item_id} — cocher / modifier
  • DELETE /api/shopping/lists/{id}/items/{item_id} — suppression article
  • POST /api/shopping/lists/{id}/finish — terminer les courses (report articles non cochés → nouvelle liste draft avec carried_over=True)
  • POST /api/shopping/lists/generate — liste magique V1 (score = retard / intervalle moyen, articles cochés comme historique)
  • Schémas Pydantic complets (listes, articles, produits, magasins)
  • Tests d'intégration (10 tests)
  • Champ tags TEXT[] sur les produits du catalogue (migration 006)

Frontend

  • Page Shopping avec 3 vues : liste des listes / détail / mode magasin
  • Vue "listes" : cartes par liste (statut, compteur articles), FAB + bouton "Liste magique"
  • Bouton "Liste magique" : désactivé si une liste draft/active existe
  • Vue "détail" : articles avec swipe-to-delete, FAB ajout article, bouton ✏️ modal gestion
  • Modal ✏️ : ajout rapide d'article + bouton rouge "Supprimer la liste en cours"
  • Vue "Mode magasin" : plein écran, grands boutons (48px+), section cochés/non cochés
  • Wake Lock API activée automatiquement en mode magasin
  • Composant Modal générique réutilisable (overlay + Escape + stopPropagation)
  • Composant ItemRow : checkbox circulaire + swipe-to-delete + mode magasin
  • Hook useWakeLock avec fallback gracieux (mode économie d'énergie)
  • Client API TypeScript typé (frontend/src/api/shopping.ts)
  • Migration TodoForm vers Modal (plus de panneau inline)
  • Bottom sheet multi-select pour l'ajout d'articles depuis le catalogue
  • Stats d'achat dans le catalogue : dernier achat + intervalle moyen par produit
  • Swipe gauche → modal édition (TodoPage + ShoppingPage)
  • Tags sur les produits : chip-input dans CatalogueModal, recherche étendue aux tags
  • Recherche article : type="search" supprime la suggestion URL iOS, autoFocus automatique
  • Upload photo produit : support HEIC/HEIF, redimensionnement 500×500 max (ratio conservé), miniature auto
  • Collage Ctrl+V photo dans CatalogueModal et TodoForm

Phase 4 — Module Notes

Objectif : saisie rapide de notes avec photo, audio et GPS depuis smartphone.

Backend

  • GET /api/notes — liste avec search full-text + filtres tags/catégorie
  • POST /api/notes — création
  • PATCH /api/notes/{id} — mise à jour
  • DELETE /api/notes/{id} — suppression
  • POST /api/notes/{id}/attachments — upload fichier (image/audio)
  • DELETE /api/notes/{id}/attachments/{att_id} — suppression pièce jointe
  • Compression image Pillow → WebP (service media partagé)
  • Recherche FTS PostgreSQL français

Frontend Mobile

  • Page Notes : liste chronologique avec aperçu
  • Bouton "+ Note" → formulaire rapide (contenu + tags) via Modal
  • Bouton 📷 → Camera API (capture directe ou import galerie)
  • Bouton 🎤 → MediaRecorder (enregistrement audio inline)
  • Bouton 📍 → Geolocation API → affichage coordonnées
  • Visionneuse photo inline + lecteur audio inline
  • Compression WebP côté client (Canvas API) — différé Phase 5+
  • Waveform visuel enregistrement — différé Phase 5+

Frontend Laptop

  • Grille notes avec vignettes photo
  • Recherche full-text
  • Filtres rapides : avec photo, avec audio, avec GPS
  • Vue carte pour les notes avec GPS (Leaflet.js) — différé Phase 5+

Phase 4c — Notes v2 : états de tuile + médias

Objectif : refonte de l'interface Notes avec tuiles à 3 états, support vidéo, transcodage audio universel.

Backend

  • ffmpeg dans le Dockerfile backend — transcodage audio et vidéo
  • save_audio() : transcode toute entrée (webm/ogg/m4a) → AAC .m4a — lecture Safari iOS garantie
  • save_video() : stockage mp4/quicktime direct, webm → H.264/mp4 via ffmpeg
  • ALLOWED_VIDEO_TYPES : mp4, webm, quicktime, m4v, 3gpp
  • GET /api/notes : filtre has_video ajouté
  • POST /api/notes/{id}/attachments : gère file_type = "video"
  • GET /api/admin/stats : section media.video (count + size_bytes)
  • Schémas Pydantic notes : gps_lat/gps_lon passés en float | None (fix Decimal sérialisé en string → TypeError JS)

Frontend

  • NoteCard à 3 états : semi (défaut, 3 lignes + actions) / expanded (markdown complet + médias) / collapsed (titre + date)
  • Bouton toggle fa-chevron-down / fa-minus / fa-chevron-right dans le coin haut-droit de chaque tuile
  • Renderer pseudo-markdown : # ## ###, - * 1. listes, > citations, ---, **gras** *italique* `code`, ``` blocs
  • Icônes méta sur la tuile : fa-image / fa-microphone / fa-video / fa-location-dot
  • Bouton vidéo fa-video dans les actions de chaque note
  • Lecteur <video playsInline> inline dans l'état expanded
  • Audio : onLoadedMetadata règle le volume à 50% + fix overflow (minWidth: 0)
  • Filtres rapides : Photo / Audio / Vidéo / GPS (avec icônes Font Awesome)
  • Grille : 3 colonnes max sur laptop (repeat(3, 1fr)), 1 colonne sur mobile
  • Bouton "Nouvelle note" dans le header, visible sur laptop uniquement
  • Sidebar laptop : indicateur de statut BDD (LED verte/rouge + taille) avec polling 30s sur /api/health
  • ConfigPage : grille médias 3 colonnes (Photos / Audio / Vidéos)
  • client_max_body_size nginx : 15m → 200m pour les vidéos
  • docker-compose.yml : user: "1000:1000" sur backend et backend-worker

Phase 4b — UX transversale

Objectif : améliorations d'expérience applicables à tous les modules.

Thème et apparence

  • ThemeContext : gestion dark / light / system avec persistance localStorage
  • Script anti-flash dans index.html : thème et zoom appliqués avant le premier rendu
  • Page /config : boutons thème + slider police (0.81.4, pas de 0.05)
  • Aperçu temps réel du texte sur le slider
  • Zoom CSS sur <html> : scale global compatible avec les tailles en pixels fixes
  • TopBar : en-tête fixe 44px avec bouton de thème cyclique (lune / soleil / demi-cercle)
  • Tuile "Paramètres" sur la page d'accueil (/config, icône fa-sliders)

Mobile — clavier iOS

  • BottomSheet : visualViewport API pour remonter le panneau au-dessus du clavier virtuel
  • Transition fluide (0.15s) sur bottom, max-height, border-radius à l'ouverture du clavier

Todos

  • Case "Pas de date" pré-cochée par défaut dans TodoForm (champ date masqué)
  • Collage Ctrl+V pour coller une image directement dans le formulaire Todo

Infra / Media

  • nginx : location ^~ /media/ pour éviter que la règle regex WebP prenne la priorité
  • nginx : client_max_body_size 15m pour les photos de smartphone (38 Mo)

Phase 5 — Export Markdown notes + Backup BDD

Objectif : persistance double des notes (BDD source de vérité + fichiers Markdown partagés), infrastructure Redis, backup/restore depuis l'interface.

Architecture

  • Volume Docker ./data/ (bind mount) remplace le volume nommé uploads
    • data/notes/ — fichiers .md auto-générés
    • data/uploads/ — médias (photos, audio)
    • data/backup/ — dumps PostgreSQL

Backend

  • Service Redis : redis:7-alpine dans docker-compose
  • Worker ARQ (backend-worker) : consomme la queue notes:markdown, même image que backend
  • app/core/redis.py — pool ARQ, enqueue() best-effort (silence si Redis down)
  • app/workers/notes_worker.py — tâches export_note_markdown + remove_note_markdown
  • app/services/notes_markdown.py — génère le frontmatter YAML + contenu + pièces jointes (liens relatifs ../uploads/…)
    • Nom de fichier : YYYY-MM-DD_slug-du-titre_shortid.md
    • Rotation automatique si titre modifié (recherche par *_{shortid}.md)
  • app/api/notes.py — publie sur Redis après create / update / delete / add_attachment / delete_attachment
  • app/api/admin.pyPOST /api/admin/backup, GET /api/admin/backups, POST /api/admin/restore/{filename}
  • backend/Dockerfile — ajout postgresql-client pour pg_dump / pg_restore
  • requirements.txt — ajout arq==0.26.1

Frontend

  • frontend/src/api/admin.ts — client TypeScript backup/restore
  • Page Config — section "Base de données" : bouton sauvegarde + liste des dumps + bouton Restaurer par fichier

Phase 6 — Scan produits + enrichissement catalogue

Objectif : scan code-barres depuis mobile, auto-remplissage depuis OpenFoodFacts.

Services Docker

  • Dockerfile product-search/ : Python slim + requests + client OpenFoodFacts
  • Ajout service product-search dans docker-compose.yml (port 8002)
  • Ajout service searxng dans docker-compose.yml (images uniquement)

Backend (endpoints proxy)

  • GET /api/products/lookup?barcode= → proxifie vers product-search
  • GET /api/products/search?q= → proxifie vers product-search
  • POST /api/products/import → crée produit depuis données OpenFoodFacts

Frontend Mobile — scan

  • Intégration zxing-js (BarcodeReader via flux caméra)
  • Bouton "Scanner" dans le formulaire d'ajout de produit
  • Sur scan réussi → auto-remplissage formulaire

Frontend Laptop — enrichissement catalogue

  • CRUD complet produits dans catalogue
  • Recherche texte → OpenFoodFacts → import 1 clic
  • Gestion magasins

Phase 7 — Service OCR (conteneur dédié)

Objectif : OCR partagé, prérequis pour shopping avancé et notes avancées.

  • Dockerfile ocr/ : Python slim + Tesseract 5 + langues (fra, eng) + Pillow
  • POST /extract : reçoit image (multipart), retourne { text, confidence }
  • Endpoint backend POST /api/ocr/extract : proxifie vers ocr:8001
  • Ajout du service ocr dans docker-compose.yml

Phase 8 — Shopping avancé (OCR + suivi prix)

Backend

  • POST /api/shopping/ocr/price-tag — photo étiquette → extraction prix
  • POST /api/shopping/ocr/receipt — photo ticket → reconciliation liste
  • GET /api/shopping/products/{id}/price-history — historique prix par produit + magasin

Frontend

  • Bouton 📷 sur chaque article → OCR étiquette → pré-remplissage prix
  • Graphique d'évolution du prix par produit (Recharts)
  • Comparaison prix par magasin

Phase 9 — MCP Server

Objectif : exposer les outils HomeHub aux agents IA (Hermes, Claude, etc.).

  • Intégration SDK MCP Python (mcp package)
  • Endpoint SSE /mcp
  • Implémentation des outils : get_todos(), add_todo(), add_shopping_item(), search_notes()
  • Documentation OpenAPI des outils MCP

Phases futures (hors scope initial)

Phase 9 — Authentification multi-utilisateurs

  • Auth JWT (login / refresh token)
  • Activation de owner_id dans toutes les tables
  • Page connexion React

Phase 10 — Calendrier + intégrations

  • Sync Google Calendar (OAuth2 + worker)
  • Endpoint CalDAV pour iOS
  • Redis pour la queue de synchronisation
  • Webhooks Gitea → Kanban
  • Home Assistant : capteur "tâches en retard"

Phase 11 — Vision IA

  • Endpoint analyse frigo (photo → suggestions liste de courses)
  • Amélioration OCR via modèle Vision local (Ollama)

Phase 12 — Éditeur Markdown pour les Notes

Refonte du module Notes autour d'un vrai éditeur Markdown orienté mobile.

Concept : une seule interface unifiée — champ titre + corps éditeur — avec une barre d'outils flottante au-dessus du clavier.

Barre d'outils (ordre d'affichage) :

  • H — Titre (# )
  • — Liste à puces (- )
  • 1. — Liste numérotée (1. )
  • </> — Bloc code (` inline ou ``` bloc)
  • 📷 — Insérer image (upload direct, syntaxe ![](path))
  • 🎤 — Enregistrement audio (MediaRecorder, lien [enregistrement.m4a](path))
  • 📍 — Position GPS (insère [GPS](geo:lat,lon) ou coordonnées brutes)

Rendu : aperçu Markdown en lecture (pas d'édition WYSIWYG pure — mode split ou toggle édit/aperçu).

Contraintes techniques :

  • Bibliothèque candidate : @uiw/react-md-editor (légère, compatible mobile) ou éditeur custom avec textarea + parsing marked/micromark
  • La barre d'outils doit rester au-dessus du clavier virtuel iOS/Android (même logique que BottomSheet avec visualViewport)
  • Compatibilité avec le format des fichiers .md déjà exportés en Phase 5 (frontmatter YAML conservé)
  • Le backend ne change pas : content reste un champ texte libre (Markdown brut stocké tel quel)

Ordre de développement

Phase 1 ✅ → Phase 2 ✅ → Phase 3 ✅ → Phase 4 ✅ → Phase 4b ✅ → Phase 5 ✅ → Phase 6 (Scan) → Phase 7 (OCR) → Phase 8 (Shopping avancé) → Phase 9 (MCP)