113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# -*- coding: utf-8 -*-
|
||
|
|
"""
|
||
|
|
patch_layouts_c4.py — Chantier C4 (catalogue : image_split, image_full)
|
||
|
|
=======================================================================
|
||
|
|
Ajoute les deux layouts images à layouts_v2.yaml par APPEND sécurisé
|
||
|
|
(le fichier n'est jamais réécrit ni reformaté — protection des
|
||
|
|
commentaires et de la mise en forme, leçon de l'incident Le Chat) :
|
||
|
|
1. Parse le fichier actuel, vérifie l'absence des deux clés.
|
||
|
|
2. Vérifie que la dernière ligne significative appartient bien au
|
||
|
|
bloc layouts: (ligne indentée) — sinon refus.
|
||
|
|
3. Sauvegarde .bak-c4, append du fragment, re-parse de contrôle.
|
||
|
|
4. En cas d'échec du re-parse : restauration automatique.
|
||
|
|
|
||
|
|
Après ce patch, propager : python3 prompt_injection_v2.py && python3
|
||
|
|
build_gallery.py (le Designer voit les nouveaux layouts, la galerie et
|
||
|
|
le bloc skill sont régénérés).
|
||
|
|
|
||
|
|
Usage (dossier du pipeline, single-line) :
|
||
|
|
python3 patch_layouts_c4.py
|
||
|
|
"""
|
||
|
|
|
||
|
|
import shutil
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import yaml
|
||
|
|
|
||
|
|
TARGET = Path("layouts_v2.yaml")
|
||
|
|
|
||
|
|
FRAGMENT = """
|
||
|
|
# ── Chantier C4 — layouts images ──────────────────────────────────
|
||
|
|
image_split:
|
||
|
|
id: L50
|
||
|
|
famille: Visuel
|
||
|
|
mode: light
|
||
|
|
champs: [titre, image, bullets, side, legende]
|
||
|
|
champs_requis: [titre, image, bullets]
|
||
|
|
agent_hint: >-
|
||
|
|
Image d'appui sur 40 % de la slide (side: left par défaut, right
|
||
|
|
possible) + titre et points clés (max 4 bullets). À utiliser quand
|
||
|
|
une image du dossier assets/ du projet illustre le propos ;
|
||
|
|
image = nom de fichier exact tel que listé par /lire.
|
||
|
|
legende (optionnelle) s'affiche sur un bandeau navy sous l'image.
|
||
|
|
|
||
|
|
image_full:
|
||
|
|
id: L51
|
||
|
|
famille: Visuel
|
||
|
|
mode: dark
|
||
|
|
champs: [titre, image, sous_titre]
|
||
|
|
champs_requis: [titre, image]
|
||
|
|
agent_hint: >-
|
||
|
|
Ouverture de chapitre visuelle : image plein cadre + voile navy +
|
||
|
|
titre display blanc. Alternative à section_divider quand un asset
|
||
|
|
du projet s'y prête. image = nom de fichier exact de assets/.
|
||
|
|
"""
|
||
|
|
|
||
|
|
|
||
|
|
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)
|
||
|
|
raw = TARGET.read_text(encoding="utf-8")
|
||
|
|
try:
|
||
|
|
data = yaml.safe_load(raw)
|
||
|
|
except yaml.YAMLError as e:
|
||
|
|
fail("layouts_v2.yaml actuel illisible : %s" % e)
|
||
|
|
layouts = (data or {}).get("layouts")
|
||
|
|
if not isinstance(layouts, dict):
|
||
|
|
fail("Bloc layouts: introuvable dans le fichier.")
|
||
|
|
for key in ("image_split", "image_full"):
|
||
|
|
if key in layouts:
|
||
|
|
fail("'%s' déjà présent — rien à faire." % key)
|
||
|
|
|
||
|
|
# La dernière ligne significative doit être DANS le bloc layouts:
|
||
|
|
last = next((l for l in reversed(raw.splitlines())
|
||
|
|
if l.strip() and not l.strip().startswith("#")), "")
|
||
|
|
if not last.startswith(" "):
|
||
|
|
fail("Le fichier ne se termine pas dans le bloc layouts: "
|
||
|
|
"(dernière ligne non indentée : %r) — fusion manuelle "
|
||
|
|
"requise." % last[:40])
|
||
|
|
|
||
|
|
shutil.copy2(TARGET, str(TARGET) + ".bak-c4")
|
||
|
|
print(" + Sauvegarde : %s.bak-c4" % TARGET)
|
||
|
|
|
||
|
|
with open(TARGET, "a", encoding="utf-8") as f:
|
||
|
|
if not raw.endswith("\n"):
|
||
|
|
f.write("\n")
|
||
|
|
f.write(FRAGMENT)
|
||
|
|
|
||
|
|
try:
|
||
|
|
check = yaml.safe_load(TARGET.read_text(encoding="utf-8"))
|
||
|
|
nl = check["layouts"]
|
||
|
|
assert "image_split" in nl and "image_full" in nl
|
||
|
|
assert len(nl) == len(layouts) + 2
|
||
|
|
except Exception as e:
|
||
|
|
shutil.copy2(str(TARGET) + ".bak-c4", TARGET)
|
||
|
|
fail("Contrôle post-append échoué (%s) — fichier restauré." % e)
|
||
|
|
|
||
|
|
print(" + image_split (L50) et image_full (L51) ajoutés "
|
||
|
|
"(%d layouts au total)." % (len(layouts) + 2))
|
||
|
|
print("\n Propager : python3 prompt_injection_v2.py && python3 "
|
||
|
|
"build_gallery.py")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|