#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ patch_facilitator_c4.py — Chantier C4 (Assets & images) ======================================================= Patch strict de facilitator_v9.py : P1. validate_freeform accepte le nouveau type de bloc « image ». P2. /lire inventorie aussi projets//assets/ (images + dimensions) et injecte la liste dans le contexte Narrator — le Narrator peut alors référencer les fichiers par nom dans les layouts image_*. Le dossier assets/ est créé au premier /lire s'il n'existe pas. P3. Insère list_assets() (inventaire, dimensions via Pillow si disponible, sinon noms seuls). Usage (dossier du pipeline, single-line) : python3 patch_facilitator_c4.py Compatible tout ordre avec C1/C2/C3. Vérifie chaque ancre, écrit .bak-fc4, compile, idempotent. """ import py_compile import shutil import sys from pathlib import Path TARGET = Path("facilitator_v9.py") FUNC = ''' def list_assets(proj: "Project"): """Inventaire des images de projets//assets/ (chantier C4). Crée le dossier au premier appel. Retourne un bloc texte destiné au contexte Narrator, ou une chaîne vide si aucune image.""" assets = proj.root / "assets" assets.mkdir(exist_ok=True) exts = {".png", ".jpg", ".jpeg", ".webp"} files = sorted(p for p in assets.iterdir() if p.suffix.lower() in exts and p.is_file()) if not files: return "" lines = ["IMAGES DISPONIBLES DANS assets/ (utilisables dans les " "layouts image_split / image_full et le bloc freeform " "image, par leur nom de fichier) :"] for p in files: dims = "" try: from PIL import Image with Image.open(p) as im: dims = f" ({im.size[0]}×{im.size[1]})" except Exception: pass lines.append(f"- {p.name}{dims}") return "\\n".join(lines) ''' PATCHES = [ # P1 — type de bloc freeform « image » ( 'VALID_BLOCK_TYPES = {"title", "heading", "text", "stat", ' '"circle",\n' ' "badge", "card", "rect", "line"}\n', 'VALID_BLOCK_TYPES = {"title", "heading", "text", "stat", ' '"circle",\n' ' "badge", "card", "rect", "line", "image"}\n', ), # P2 — /lire : inventaire des assets ( ' elif cmd == "/lire":\n' ' context, new_files = load_documents(proj, ' 'loaded_docs)\n' ' if not new_files:\n' ' info("Aucun nouveau document dans inputs/.")\n' ' continue\n', ' elif cmd == "/lire":\n' ' context, new_files = load_documents(proj, ' 'loaded_docs)\n' ' assets_info = list_assets(proj)\n' ' if assets_info:\n' ' context = ((context or "").strip()\n' ' + "\\n\\n" + assets_info).strip()\n' ' ok(f"{len(assets_info.splitlines()) - 1} ' 'image(s) dans assets/.")\n' ' if not new_files and not assets_info:\n' ' info("Aucun nouveau document dans inputs/ ni ' 'image dans assets/.")\n' ' continue\n', ), ] MARKER = "def list_assets" ANCHOR_SECTION = "# FLUX LIBRE — THE FREE DESIGNER" def fail(msg): print(" ! %s" % msg) sys.exit(1) def main(): if not TARGET.exists(): fail("%s introuvable — lancer depuis le dossier du pipeline." % TARGET) content = TARGET.read_text(encoding="utf-8") if MARKER in content: fail("Déjà patché (list_assets présent) — rien à faire.") for i, (old, _) in enumerate(PATCHES, 1): n = content.count(old) if n == 0: fail("Ancre du patch %d introuvable — facilitator inattendu." % i) if n > 1: fail("Ancre du patch %d non unique (%d occurrences)." % (i, n)) if content.count(ANCHOR_SECTION) != 1: fail("Ancre de section FREE DESIGNER introuvable ou non unique.") shutil.copy2(TARGET, str(TARGET) + ".bak-fc4") print(" + Sauvegarde : %s.bak-fc4" % TARGET) for old, new in PATCHES: content = content.replace(old, new) lines = content.split("\n") idx = next(i for i, l in enumerate(lines) if ANCHOR_SECTION in l) ins = idx - 1 if lines[idx - 1].startswith("# ───") else idx lines[ins:ins] = FUNC.split("\n") content = "\n".join(lines) TARGET.write_text(content, encoding="utf-8") print(" + 2 patchs + list_assets appliqués.") try: py_compile.compile(str(TARGET), doraise=True) print(" + Compilation OK.") except py_compile.PyCompileError as e: shutil.copy2(str(TARGET) + ".bak-fc4", TARGET) fail("Erreur de compilation — fichier restauré :\n%s" % e) print("\n Dépose tes images dans projets//assets/ puis /lire " "dans la session.") if __name__ == "__main__": main()