# LoreMind — feature "Illustrations & images"
# Plan d'execution en 6 etapes. Mets a jour apres chaque etape terminee.
# ==========================================================================

## Etape 1 — Shared Kernel images + MinIO        [x] TERMINEE (2026-04-20 sess.5)
    Backend Java pur. Aucune integration metier. Tout est testable via curl.
    Fichiers crees/modifies :
      - docker-compose.yml (nouveau, service minio + minio-init)
      - core/pom.xml (+ dep io.minio:minio:8.5.11)
      - core/src/main/resources/application.properties (+ config minio.*, multipart 10MB)
      - core/src/main/java/com/loremind/domain/images/Image.java
      - core/src/main/java/com/loremind/domain/images/ports/ImageRepository.java
      - core/src/main/java/com/loremind/domain/images/ports/ImageStorage.java
      - core/src/main/java/com/loremind/application/images/ImageService.java
      - core/src/main/java/com/loremind/infrastructure/persistence/entity/ImageJpaEntity.java
      - core/src/main/java/com/loremind/infrastructure/persistence/jpa/ImageJpaRepository.java
      - core/src/main/java/com/loremind/infrastructure/persistence/postgres/PostgresImageRepository.java
      - core/src/main/java/com/loremind/infrastructure/storage/MinioConfig.java
      - core/src/main/java/com/loremind/infrastructure/storage/MinioImageStorageAdapter.java
      - core/src/main/java/com/loremind/infrastructure/web/dto/images/ImageDTO.java
      - core/src/main/java/com/loremind/infrastructure/web/controller/ImageController.java
      - docs/academy/object-storage.md
      - docs/academy/shared-kernel.md
    Validation : `mvn compile` OK (exit 0).
    Tests manuels a faire par l'utilisateur :
      1. docker-compose up -d
      2. mvn spring-boot:run (dans /core)
      3. curl -F file=@test.jpg http://localhost:8080/api/images
      4. Verifier la reponse JSON avec id et url
      5. Ouvrir http://localhost:8080/api/images/<id>/content dans le navigateur

## Etape 2 — Composants Angular partages         [x] TERMINEE (2026-04-20 sess.5)
    Fichiers crees :
      - web/src/app/services/image.model.ts
      - web/src/app/services/image.service.ts (upload, getById, delete, contentUrl)
      - web/src/app/shared/image-uploader/ (ts + html + scss)
          * Mode drop-zone standard OU compact (bouton "+ ajouter" pour galerie)
          * Validation client MIME/taille alignee avec le backend
          * Spinner + gestion erreur 413 et erreur generique
      - web/src/app/shared/image-gallery/ (ts + html + scss)
          * Grille de vignettes 120x120, lazy-loading
          * Mode editable=true : bouton "+ ajouter" via app-image-uploader compact
          * Bouton X par vignette, supprime cote serveur + emet nouvelle liste
          * Lightbox plein ecran au clic (clic hors image pour fermer)
    Validation : npx tsc --noEmit OK (exit 0).

## Etape 3 — Illustrations sur Scene / Chapter / Arc   [x] TERMINEE (2026-04-20 sess.5)
    Backend :
      - domain/campaigncontext/{Arc,Chapter,Scene}.java : + List<String> illustrationImageIds
      - persistence/entity/{Arc,Chapter,Scene}JpaEntity.java : colonne JSON illustration_image_ids
      - persistence/postgres/Postgres{Arc,Chapter,Scene}Repository.java : mapping 2 sens
      - web/dto/campaigncontext/{Arc,Chapter,Scene}DTO.java : + champ illustrationImageIds
      - web/mapper/{Arc,Chapter,Scene}Mapper.java : propage dans les 2 sens
      - application/campaigncontext/{Arc,Chapter,Scene}Service.java : update inclut le champ
    Frontend :
      - services/campaign.model.ts : + champ sur Arc/Chapter/Scene + leurs Create
      - arc-view/chapter-view/scene-view : import + section "Illustrations" en haut (lecture)
      - arc-edit/chapter-edit/scene-edit : import + propriete illustrationImageIds,
        section galerie editable en tete de form, propagation dans submit()
    Validation : mvn compile OK, npx tsc --noEmit OK.

## Etape 4 — Refactor Template.fields             [x] TERMINEE (2026-04-21 sess.6)
    Backend :
      - domain/lorecontext/FieldType.java (enum TEXT | IMAGE)
      - domain/lorecontext/TemplateField.java (VO name + type)
      - domain/lorecontext/Template.java : fields devient List<TemplateField>,
        helper textFieldNames() pour ne garder que les noms TEXT (use cases IA)
      - persistence/converter/TemplateFieldListJsonConverter.java : lit le legacy
        ["name",...] ET le nouveau [{name,type}], ecrit toujours au nouveau format.
        Migration automatique a la premiere sauvegarde.
      - persistence/entity/TemplateJpaEntity.java : converter swap
      - persistence/postgres/PostgresTemplateRepository.java : mapping typé
      - web/dto/lorecontext/TemplateFieldDTO.java (nouveau)
      - web/dto/lorecontext/TemplateDTO.java : fields -> List<TemplateFieldDTO>
      - web/mapper/TemplateFieldMapper.java (nouveau, tolerance type inconnu -> TEXT)
      - web/mapper/TemplateMapper.java : delegue au fieldMapper
      - application/lorecontext/TemplateService.java : signature createTemplate
      - web/controller/TemplateController.java : conversion DTO -> domain
      - application/generationcontext/GeneratePageValuesUseCase.java : n'envoie
        a l'IA que les champs TEXT (via textFieldNames()), erreur claire si aucun
      - application/generationcontext/StreamChatForLoreUseCase.java : idem
    Frontend :
      - services/template.model.ts : FieldType + TemplateField
      - template-create : liste TemplateField[], selecteur de type, toggle
      - template-edit : idem + normalisation legacy en TEXT cote client
      - page-view : rendu TEXT vs placeholder IMAGE (complete etape 5)
      - page-edit : hydrate TEXT only, mergeSuggestions filtree, placeholder IMAGE
      - page-create wizard prompt : ne liste que les TEXT fields
      - Styles : chip verte (TEXT) vs indigo (IMAGE), bouton toggle inline
    Validation : mvn compile OK, npx tsc --noEmit OK.

## Etape 5 — Support champs IMAGE dans Pages      [x] TERMINEE (2026-04-21 sess.6)
    Backend :
      - persistence/converter/StringListMapJsonConverter.java (nouveau,
        convertit Map<String, List<String>> <-> JSON pour Page.imageValues)
      - domain/lorecontext/Page.java : + champ imageValues + helpers
        setImageFieldValue/getImageFieldValue
      - persistence/entity/PageJpaEntity.java : colonne image_values_json
      - persistence/postgres/PostgresPageRepository.java : mapping 2 sens
      - web/dto/lorecontext/PageDTO.java : + champ imageValues
      - web/mapper/PageMapper.java : propage le champ
      - application/lorecontext/PageService.java : update inclut imageValues
    Frontend :
      - services/page.model.ts : + imageValues?: Record<string, string[]>
      - page-view : import ImageGalleryComponent + helper imageIdsOf()
        + rendu galerie (readonly) pour chaque champ IMAGE
      - page-edit : import ImageGalleryComponent + propriete imageValues
        + hydrate separe TEXT/IMAGE + save propage imageValues + UI galerie editable
    Validation : mvn compile OK, npx tsc --noEmit OK.

## Etape 6 — Brain Python : synchro DTOs          [x] TERMINEE (2026-04-21 sess.6)
    Backend Java :
      - domain/generationcontext/CampaignStructuralContext.java : +illustrationCount
        sur ArcSummary, ChapterSummary, SceneSummary
      - application/generationcontext/CampaignStructuralContextBuilder.java : populate
        depuis Arc/Chapter/Scene.getIllustrationImageIds() (null-safe)
      - infrastructure/ai/BrainAiChatClient.java : serialise illustration_count dans
        le JSON envoye au Brain (UNIQUEMENT si > 0, pour payload leger)
    Brain Python :
      - domain/models.py : +illustration_count: int = 0 sur les 3 summaries
      - main.py : +illustration_count sur les 3 DTOs Pydantic + propagation dans
        _to_campaign_context. Les champs inconnus (ex: illustrationImageIds envoyes
        par le Core pour les pages) sont ignores par defaut par Pydantic v2.
      - application/chat.py : nouveau helper _illustration_hint() ; les lignes
        arcs/chapters/scenes du prompt affichent " [N illustrations]" si presentes.
    Validation : mvn compile OK, python ast-parse OK, demo runtime prompt OK
    (" - A (arc) [2 illustrations]", " - S (scène) [1 illustration]").

    NON couvert (scope residuel evident, si besoin) :
      - PageSummary ne porte pas encore de signal sur les imageValues des pages.
        Les pages Lore exposent deja des values text via LoreStructuralContext ;
        on pourrait ajouter le nombre d'images par champ IMAGE la aussi.

## Notes transverses
  - Docker-compose pour l'instant ne couvre QUE MinIO. Postgres/brain/web restent lances a la main.
  - Les ports `ImageRepository` et `ImageStorage` sont volontairement separes (SRP).
  - Le binaire est stocke dans MinIO bucket `loremind-images` (cree auto par minio-init).
  - L'URL publique d'une image est `/api/images/{id}/content` (proxy via backend Java).
  - Validation MIME cote ImageService : jpeg/png/webp/gif uniquement. Max 10 Mo.
