diff --git a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md index 1b94020..91703b5 100644 --- a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md +++ b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md @@ -11,13 +11,22 @@ Stand 2026-09-28, Brainstorming läuft (Weg: architektonisch → Spec → Plan). ## Antworten der Auftraggeberin/des Auftraggebers - G1 Echte Designdaten: (b) selbst über Divis Funktionen anlegen → erledigt, Commit 65bd86f, Formate in `2026-09-28-etappe-4-divi-formate.md`, Fixtures in `tests/Fixtures/divi/stage4/`, Seite 3182 „[Fixture] Designsystem“ (Entwurf). - G2 Site-weite Änderungen (Presets, Farben, Variablen): (a) wie globale Module – Probelauf zeigt Änderung und betroffene Seiten, erst `confirm: true` schreibt; Snapshot immer. -- G3 Theme-Optionen-Umfang: **offen** – Vorschlag (a) ohne SEO; Alternativen (b) mit SEO wie Spec §7, (c) vorerst ganz ohne Theme-Optionen. +- G4 Zuschnitt (2026-10-16): (b) ganz Etappe 4 in einer Spec; Presets lesen (inkl. Einstellungen), anlegen, ändern und zuweisen (Modul- und Gruppen-Presets) sind ausdrücklicher Teil. +- G3 Theme-Optionen-Umfang (2026-10-16): (a) Design-Optionen ohne SEO (Theme-Schriften/-Größen, Akzent-/Linkfarben, Layout-Breiten). Annahme: Button-Standards (`all_buttons_*`) und Logo (`divi_logo`, ePanel) bleiben draußen. +- G5 Löschen (2026-10-16): (b) Preset/Variable nur löschen, wenn nirgends verwendet; sonst Fehler mit Fundstellen. Dazu eigene site-weite Funktion „Verweise überall entfernen“ (Seiten, Bibliothek, Theme Builder): (a) nur den Verweis entfernen, Module fallen auf Standard zurück; Probelauf je Seite, `confirm: true`, Revision je Seite, Snapshot vorher. +- G6 Snapshots (2026-10-16): (a) je Bereich (Farben, Variablen, Presets, Theme-Optionen) getrennt, je 20; Liste per Tool; Wiederherstellen mit Probelauf (Diff) und `confirm: true`, vorher selbst ein Snapshot; Seiteninhalte nicht (WordPress-Revisionen). +- Farben löschen: nur „inaktiv“ wie Divi (Annahme, nicht widersprochen). +- G7 Preset-Inhalt (2026-10-16): (a) `set` (Kurzform) + `attrs` (Vollform) wie lit-divi-update, gleiche Prüfung/Sperren/Platzhalter; zusätzlich `from_node` {post_id, id} übernimmt das Design eines Moduls (ohne Texte/Inhalte). Gruppen-Presets analog mit `group`, nur Attribute dieser Gruppe. +- G8 Zuweisen (2026-10-16): insert/update um Stapel (`preset` als Liste) und `group_presets` {Gruppe: Name|null} erweitern; zusätzlich site-weite Massenzuweisung (a) – gleiches Gerüst wie „Verweise entfernen“ (G5), Filter Modultyp, Seiten, nur ohne Preset / mit bestimmtem bisherigem Preset. +- G9 Lokale Werte vs. Preset (2026-10-16): (a) Probelauf/Antwort meldet je Modul lokale Werte, die das Preset überdecken; `clear_overrides: true` entfernt genau diese; sonst nur Warnung. Gilt für Einzel- und Massenzuweisung. +- G10 Schriften (2026-10-16): (b) Google-Fonts aus Divis Liste (Namensprüfung mit Vorschlägen) und eigene Schriftdateien hochladen (Divi „Custom Fonts“, Option `et_uploaded_fonts`). Annahme: Quelle per URL wie lit-media-upload-from-url. Code: `FontsUtility::font_add()` (B5 `VisualBuilder/Fonts/FontsUtility.php:102`) nutzt `wp_handle_upload` (prüft `is_uploaded_file`) → für geladene Dateien eigener Sideload mit Divis Filtern `upload_dir_font`/`check_filetype_and_ext_font` nötig; DSGVO-Lokal-Laden bleibt Theme-Einstellung. +- G11 Ablauf „Corporate Design übernehmen“ (2026-10-16): (c) kein eigener Ablauf/Prompt, nur Tools; Claude stellt den Ablauf selbst zusammen. +- G12 Schleifen-Inhalte (2026-10-16): in Etappe 4 aufnehmen. Befund: `DynamicContentRules::fieldsFor()` (`src/Integrations/Divi/Compose/DynamicContentRules.php:117`) lehnt `loop_*` immer ab (Ruling S36, E12) – kein Erkennungsfehler; Meldung „nur innerhalb von Divi-Schleifen“ irreführend. Praxisfall: Datumsformat in Karten (`loop_post_date`), Pills (Kategorien/Tags) in Modulen unter einer Schleifen-Spalte. Ziel: Schleifen-Kontext aus dem nächsten Vorfahren mit `module.advanced.loop.desktop.value.enable` (+ `queryType`) bestimmen und passende `loop_*`-Optionen zulassen (S36 lockern, Sicherheitsblick). +- Ansatz (2026-10-16): A – ein Tool je Bereich, gemeinsame Änderungs-Pipeline; gemeinsamer Baustein für Seiten-Umschreiben (Zuweisen/Entfernen); `lit/divi-preset-get` zum Lesen. +- Entwurf Abschnitt 1 (Pipeline + Snapshots) freigegeben (2026-10-16); übrige Abschnitte direkt in die Spec `docs/superpowers/specs/2026-10-16-etappe-4-designsystem-design.md` (Annahmen A1–A11), Freigabe der Spec ausstehend. ## Noch zu klären (je eine Frage) -- Löschen: Farben nur „inaktiv“ (Divi-Verhalten)? Presets/Variablen löschen, wenn noch verwendet? -- Snapshots: Umfang je Bereich, Anzahl, Wiederherstellen mit Probelauf? -- Google-Schriften laden (Kann-Kriterium) – in Etappe 4 oder später? -- Prompt „Corporate Design übernehmen“ (§9.3) – Umfang. +- (alle Fragen beantwortet) ## Danach Ansätze vorschlagen → Entwurf abschnittsweise freigeben → Spec schreiben → Plan (writing-plans) → Umsetzung subagent-driven mit Reviews, Security-Audit am Ende. diff --git a/docs/superpowers/specs/2026-10-16-etappe-4-designsystem-design.md b/docs/superpowers/specs/2026-10-16-etappe-4-designsystem-design.md new file mode 100644 index 0000000..edf342c --- /dev/null +++ b/docs/superpowers/specs/2026-10-16-etappe-4-designsystem-design.md @@ -0,0 +1,246 @@ +# Etappe 4 – Designsystem schreiben – Design-Spezifikation + +- **Datum:** 2026-10-16 +- **Status:** Entwurf zur Freigabe +- **Autor:** Jan-Niclas Loosen (Loosen-IT), erarbeitet mit Claude +- **Einordnung:** Etappe 4 der Gesamt-Spec (`2026-09-25-wp-lit-mcp-design.md` §7, §13); baut auf der Divi-Absicherung (`2026-09-29-divi-absicherung-design.md`) auf. Entscheidungen G1–G12: `docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md`. Datenformate: `docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md` (Divi 5.2.1). +- **Annahmen** sind mit **(A…)** markiert: Punkte, die im Brainstorming nicht einzeln gefragt wurden; bei der Freigabe der Spec bestätigen oder ändern. + +## 1. Ziel und Kontext + +### 1.1 Problem + +Das Plugin liest das Designsystem (`lit/divi-design-system`: Farben, Variablen, Theme-Schriften/-Größen, Presets mit Name und ID) und kann einem Modul beim Einfügen/Ändern **ein** Modul-Preset per Name geben (`"preset": ""`). Es fehlt: + +- Einstellungen eines Presets lesen; Presets anlegen, ändern, löschen; Gruppen-Presets und Preset-Stapel zuweisen; Presets auf viele Seiten auf einmal ausrollen oder überall entfernen. +- Globale Farben, Variablen, Design-Theme-Optionen und Schriften schreiben. +- Eine Wiederherstellung: Divi 5 führt für Farben, Variablen und Presets keine serverseitige Historie (Formate §7.6). +- Schleifen-Inhalte (`loop_*`, z. B. Datum oder Kategorien in Karten einer Loop-Spalte) werden immer abgelehnt (Ruling S36), mit irreführender Meldung „nur innerhalb von Divi-Schleifen“ (G12). + +### 1.2 Ziele + +1. Ein Corporate Design lässt sich über die Tools vollständig übernehmen: Farben, Variablen, Schriften, Theme-Optionen, Presets – und auf bestehende Seiten ausrollen. +2. Jede site-weite Änderung: Probelauf mit Diff und betroffenen Seiten, erst `confirm: true` schreibt, vorher immer ein Snapshot (G2, G6). +3. Schreiben nur über Divis eigene Funktionen, abgesichert über Manifest und Freigabe (Divi-Absicherung). +4. Schleifen-Inhalte in Modulen innerhalb einer Divi-Schleife setzen können (G12). + +### 1.3 Erfolgskriterien + +1. Ein Preset lässt sich lesen (Kurz- und Vollform), anlegen (aus `set`/`attrs` oder aus einem Modul), ändern, als Standard setzen und – wenn unbenutzt – löschen; die Frontend-Ausgabe entspricht einem im Builder angelegten Preset (Integrationstest gegen echtes Divi). +2. Modul- und Gruppen-Presets, auch gestapelt, lassen sich einzelnen Modulen und per Massenzuweisung vielen Seiten zuweisen; Verweise lassen sich überall entfernen. +3. Farben, Variablen, Design-Theme-Optionen und Schriften lassen sich schreiben; der Builder zeigt sie danach unverändert an. +4. Jede Änderung eines Bereichs erzeugt vorher einen Snapshot; `lit/divi-design-restore` stellt ihn mit Probelauf wieder her. +5. `{{…}}`/Vollform mit `loop_post_date` in einem Modul unter einer Spalte mit aktiver Schleife wird gespeichert; ohne Schleifen-Kontext kommt eine verständliche Ablehnung. +6. Architekturtest (Divi-Grenze) grün; alle bestehenden Tests grün; Security-Audit ohne offene kritische/hohe Punkte. + +### 1.4 Nicht Teil dieser Etappe + +- SEO-Optionen, Logo (`divi_logo`), Button-Standards (`all_buttons_*`) (G3, A1). +- MCP-Prompts und eigener Guide „Corporate Design übernehmen“ (G11) – nur Tools; Tool-Beschreibungen tragen die Reihenfolge. +- Echtes Löschen von Farben (nur `inactive`, wie Divi); Löschen hochgeladener Schriften (A2). +- Loop Builder **anlegen** (Schleife an einem Container einschalten/konfigurieren) – nur Inhalte innerhalb bestehender Schleifen (A3). +- Snapshots von Seiteninhalten (dafür WordPress-Revisionen). + +## 2. Begriffe + +- **Bereich:** `colors`, `variables`, `presets`, `theme_options` – Einheit für Snapshot, `design_rev` und Rechte. +- **`design_rev`:** Hash über den gelesenen Rohbestand eines Bereichs; Sperre gegen gleichzeitige Änderungen (wie `tree_rev` bei Seiten). +- **Änderungs-Pipeline:** gemeinsamer Ablauf aller Schreib-Tools des Designsystems (§4). +- **Seiten-Umschreiber:** gemeinsamer Baustein, der Verweise in vielen Seiten ändert (§6.6). +- **Fundstellen:** Seiten, Bibliothekseinträge und Theme-Builder-Layouts, die ein Preset, eine Farbe oder Variable verwenden. + +## 3. Architektur + +``` +Tools (Abilities) Pipeline / Domäne (reines PHP) Adapter (Divi-Grenze) +lit/divi-presets-set ─┐ DesignChange (Rechte→Prüfung→Diff→ Designsystem-Adapter +lit/divi-colors-set ─┤ confirm→Snapshot→Schreiben→Protokoll) ├ Reader (vorhanden) +lit/divi-variables-set┤──────────▶ PresetStore / ColorStore / … ──────▶ └ Writer (neu) +lit/divi-theme-options-* SnapshotStore Katalog-Adapter +lit/divi-design-restore DesignUsages (Fundstellen) (Gruppen-Namen, Preset- +lit/divi-presets-assign ─┐ SiteRewrite (Seiten-Umschreiber) ─────▶ Attr-Mapping, Google-Fonts) +lit/divi-design-detach ─┘ Seiten-Adapter (DiviSaver) +``` + +- Neue Divi-Aufrufe liegen nur im **Designsystem-Adapter** (Schreiben: `GlobalData::set_global_colors`, `set_global_variables`, `GlobalPreset::prepare_data`, `validate_preset_count`, `save_data`, `et_update_option`, `FontsUtility`/Divis Upload-Filter) und im **Katalog-Adapter** (`GlobalPreset::get_group_preset_default_attr`, `get_default_group_name`, `Conversion::get_preset_attrs_mapping`, `get_preset_attrs`, `et_builder_get_google_fonts`, Loop-Optionen). Jeder Eintrag steht im Manifest; nachgebaute Logik bekommt einen Fingerabdruck. Divi-Funktionen werden aufgerufen, wo möglich, statt nachgebaut. +- Domänenklassen (Bestände berechnen, Diff, Prüfregeln, Überdeckungen) sind reines PHP und unit-testbar; sie arbeiten auf den Rohdaten des Adapters. +- Seitenänderungen laufen über die vorhandenen Bausteine: `Parser`/`TreeEditor`/`Composer`/`Serializer` und `DiviSaver` (Revision je Seite, Cache), Rechte und Grenzen wie `GlobalUsages`/`VisibleEdits`. +- Formate stammen aus Divi 5.2.1; freigegeben ist inzwischen 5.14.0. **Erster Umsetzungsschritt:** Formate und Funktionssignaturen gegen 5.14.0 prüfen und die Formate-Notiz nachführen (Spec §14 Gesamt-Spec: „nur aus Code abgeleitet“). + +## 4. Änderungs-Pipeline (freigegeben) + +Jedes Schreib-Tool des Designsystems: + +1. **Rechte** des Bereichs (§10). +2. **Eingabe prüfen:** JSON-Schema, dann fachlich (Namen, IDs, Platzhalter `{{color:…}}`/`{{var:…}}`/`{{font:…}}`, Werte über `ValueCleaner`/Composer mit denselben Sperren wie bei Modulen: Lottie, Interactions, rohes HTML/CSS ohne `unfiltered_html`). Fehler gesammelt als `validation_failed` mit `fields`. +3. **Neuen Bestand berechnen** aus aktuellem Rohbestand (live gelesen) + Änderung. +4. **Probelauf** (ohne `confirm`): `diff` (je Eintrag vorher/nachher, Eintragsart `added|changed|removed|deactivated`), `affected` (Fundstellen über `DesignUsages`, Regeln wie `UsageReport`: lesbare Seiten mit Titel, sonst Anzahl; `blocked_ids`), `design_rev`. Nichts gespeichert. +5. **Mit `confirm: true`:** `design_rev` vergleichen (sonst `conflict`, nichts geschrieben) → Snapshot → Schreiben über Divi → nach dem Schreiben zurücklesen und mit dem erwarteten Bestand vergleichen (fängt stille Fehlschläge wie bei `set_global_variables`) → Caches → Protokoll. +6. **Antwort:** `ToolResult` mit `diff`, `snapshot_id`, `log_id`, neuem `design_rev`, `warnings`. +7. **Fehler beim Schreiben oder Zurücklesen:** Snapshot zurückschreiben (`Rollback`), `WriteFailed` (`rolled_back`/`partial`). + +Jedes Tool nimmt **mehrere Einträge** eines Bereichs je Aufruf (ein Snapshot, ein Diff) (A4). Keine Ausnahme für „harmlose“ Änderungen: auch ein neues, unbenutztes Preset braucht Probelauf + `confirm`. + +## 5. Snapshots (freigegeben) + +- **Speicher:** je Bereich eine Option `lit_mcp_snapshots_` (autoload aus), höchstens 20 Einträge (FIFO). Eintrag: `id`, `created` (UTC), `user_id`, `ability`, `log_id`, `size`, `data` (Rohbestand wie Divi ihn speichert; Farben inkl. der fünf Customizer-Farben aus `et_divi`). +- **Größe:** Snapshot über 2 MB → Änderung abgelehnt (`snapshot_too_large`), statt ohne Sicherung zu schreiben. +- **`lit/divi-design-snapshots`** (lesen): je Bereich die Liste ohne `data`, mit `cursor`/`limit`. +- **`lit/divi-design-restore`** (`snapshot_id`): Probelauf = Diff Snapshot ↔ aktueller Stand; mit `confirm` Snapshot des aktuellen Stands, dann Zurückschreiben über dieselben Divi-Funktionen wie beim Schreiben. Presets: `save_data()` mit dem Snapshot-Bestand (ohne `validate_preset_count`, weil das Wiederherstellen Presets entfernen darf); verwaiste Verweise in Seiten meldet der Probelauf als Warnung. +- Deinstallation entfernt die Snapshot-Optionen. + +## 6. Presets + +### 6.1 Identifikation + +- Modul-Presets über `module` (`button` oder `divi/button`), Gruppen-Presets über `group` (`font-body` oder `divi/font-body`). +- Ein Preset wird über `id` oder `name` angesprochen. Namen sind in Divi nicht eindeutig: Ein mehrdeutiger Name ist ein Fehler mit den passenden IDs (wie Ruling S13a). Neue Presets mit einem schon vergebenen Namen desselben Moduls/derselben Gruppe werden abgelehnt (A5) – strenger als Divi, damit Namen beim Zuweisen eindeutig bleiben. + +### 6.2 Lesen: `lit/divi-preset-get` + +- Eingabe: `module` oder `group`, `id` oder `name`. +- Antwort: `id`, `name`, `kind`, `module`/`group`, `default` (Standard-Preset?), `set` (Einstellungen in Kurzform, soweit abbildbar), `attrs` (Vollform, nur was nicht in `set` steht), verschachtelte `group_presets` mit Namen, Platzhalter statt Roh-Referenzen (`{{color:…}}`, `{{var:…}}`), `usage` (Fundstellen wie §4 Schritt 4, gekappt), `design_rev`. +- `lit-divi-design-system` bleibt die Übersicht (Name, ID, Standard). + +### 6.3 Anlegen und ändern: `lit/divi-presets-set` + +`items` = Liste von Einträgen: + +```json +{ "module": "button", "name": "CTA primär", // oder "group": "font-body"; "id" zum Ändern + "rename": "CTA Primär", // optional, nur beim Ändern + "set": { "background_color": "{{color:primary}}", "text_color": "#ffffff" }, + "attrs": { "button.decoration.border.desktop.value.radius": "{{var:Eckenradius}}" }, + "from_node": { "post_id": 12, "id": "s0.r0.c1.m2" }, + "group_presets": { "font": "Button-Schrift" }, // verschachtelte Gruppen-Presets (nur Modul-Presets) + "make_default": false } +``` + +- Ohne `id` und ohne gleichnamiges Preset → anlegen; mit `id` (oder eindeutigem `name`) → ändern. Ändern **mischt**: genannte Pfade werden gesetzt, `null` entfernt einen Pfad/Breakpoint, Nicht-Genanntes bleibt (wie `lit-divi-update`). +- `set`/`attrs` laufen durch den Composer des Moduls (`module`) bzw. des Quellmoduls der Gruppe: dieselbe Kurzform, Pfadprüfung, Platzhalter, Bereinigung und Sperren wie bei Modulen. Nur Pfade, die Divis Preset-Attribut-Mapping des Moduls kennt (`Conversion::get_preset_attrs_mapping`), sind erlaubt; Inhalte (Texte, URLs, Bilder-IDs …) werden abgelehnt (`validation_failed` mit Pfad). +- **Gruppen-Presets:** nur Pfade unterhalb der Gruppe (`groupId`, z. B. `content.decoration.bodyFont`); Speicherung mit `groupName`, `groupId`, Quell-`moduleName` wie der Builder (Formate §3). +- **`from_node`:** liest den Knoten (Leserecht auf die Seite), übernimmt alle Attribute, die das Preset-Mapping des Moduls als Preset-Attribute führt; Inhalte nicht. Kombinierbar mit `set`/`attrs` (gehen vor). Gruppen: nur die Pfade der Gruppe. +- **`styleAttrs`/`renderAttrs`** werden serverseitig mit Divis `Conversion::get_preset_attrs(…)` berechnet, `version` = installierte Builder-Version, `created`/`updated` in Millisekunden, IDs 10 Zeichen `[0-9a-z]` (Formate §3). +- **`make_default: true`:** wird Standard-Preset des Moduls/der Gruppe; Probelauf nennt alle Seiten mit Modulen dieses Typs ohne eigenes Preset als betroffen. +- Schreiben: vollständiger Bestand → `prepare_data()` (Sanitize wie der Controller) → `validate_preset_count()` → `save_data()`. + +### 6.4 Löschen: `lit/divi-presets-delete` + +- `items` = Presets (`module`/`group` + `id`/`name`). +- Nur unbenutzte Presets (G5): Fundstellen in Seiten, Bibliothek, Theme Builder **und** als verschachteltes Gruppen-Preset in anderen Presets. Sonst Fehler `in_use` mit Fundstellen und dem Hinweis auf `lit/divi-design-detach`. +- Ein Standard-Preset wird erst gelöscht, nachdem `default` auf `""` gesetzt ist (im selben Aufruf automatisch, im Diff sichtbar). +- Schreiben wie §6.3 mit `actionType`-Semantik `DELETE_*` (kein `preset_count_decreased`); Divi-4-Altoption wie der Controller bereinigen (`remove_preset_from_legacy_option`). + +### 6.5 Zuweisen an einzelne Module (`lit-divi-insert`, `lit-divi-update`, `lit-content-create`) + +- `"preset"`: Name **oder Liste von Namen** (Stapel, letzter gewinnt wie in Divi); `null` entfernt. Speicherung `modulePreset: [ids]`. +- `"group_presets"`: `{ "": "" | ["", …] | null }`, Gruppe als Kurzname (`font-body`) oder `divi/font-body`; Zuordnung zum Attributpfad über Divis Gruppenliste des Moduls (`get_group_preset_default_attr`). Speicherung `groupPreset: { "": { presetId: [ids], groupName } }`. +- **Überdeckungen (G9):** Lokale Werte des Moduls auf denselben Pfaden (Pfad + Breakpoint + Zustand) wie das Preset werden gemeldet (`warnings`: „s0.r0.c1.m2: Hintergrundfarbe, Schriftgröße überdecken ‚CTA primär‘“). `clear_overrides: true` entfernt genau diese lokalen Werte. +- `lit-divi-outline` zeigt Gruppen-Presets mit Namen zusätzlich zu Modul-Presets. + +### 6.6 Seiten-Umschreiber (gemeinsam für §6.7 und §6.8) + +- Findet Fundstellen (`DesignUsages`: lesbare Seiten, Bibliothekseinträge, Theme-Builder-Layouts; Rechte- und Theme-Builder-Regeln wie `GlobalUsages`), ändert je Seite den Baum über `TreeEditor`/Composer und speichert über `DiviSaver` (Revision je Seite, Cache, Protokoll). +- Probelauf: je Seite die betroffenen Knoten (Pfad-ID, Modul, Label) und Überdeckungen; `blocked_ids` für Live-Seiten ohne `edit_post`; keine Änderung, wenn eine Seite blockiert ist (wie `GlobalEdits`). +- **Grenze je Aufruf** (A6): höchstens 50 Seiten werden mit `confirm` geschrieben; die Antwort nennt `remaining`. Beide Operationen sind wiederholbar (Filter treffen bereits geänderte Module nicht mehr), der nächste Aufruf macht weiter. Über `GlobalUsages::HARD_CAP` Live-Seiten: Ablehnung wie bisher. +- Seiten mit globalen Modulen: Instanzen werden nicht umgeschrieben, sondern der Bibliothekseintrag (eigene Fundstelle, F7-Regeln); nicht bearbeitbare/gesperrte Knoten (StoredNodeGuard) bleiben unverändert und werden gemeldet. +- Pro Seite gilt ein frisch gelesener Baum (kein `tree_rev` nötig); schlägt eine Seite fehl, sind die bereits gespeicherten Seiten gespeichert (Revisionen in der Antwort), die Antwort meldet `partial`. + +### 6.7 Massenzuweisung: `lit/divi-presets-assign` + +- Eingabe: `module` (Pflicht), `preset` (Name/Liste) und/oder `group_presets`, Filter: `post_ids` (optional, sonst alle Fundstellen mit diesem Modultyp), `only_without_preset` (bool) oder `only_with_preset` (Name), `clear_overrides` (bool, G9). +- Probelauf → `confirm` wie §6.6. + +### 6.8 Verweise überall entfernen: `lit/divi-design-detach` + +- Eingabe: `preset` (`module`/`group` + Name/ID) **oder** `variable` (Name/ID). Optional `post_ids`. +- Preset: entfernt die ID aus `modulePreset`/`groupPreset`-Stapeln; Module fallen auf das nächste Preset im Stapel bzw. das Standard-Preset zurück (G5 a). Verschachtelte Verwendung in anderen Presets entfernt der Aufruf im selben Schritt über die Pipeline (Snapshot `presets`). +- Variable: ersetzt die Referenz durch ihren aktuellen Wert als festen Wert (A7) – „Verweis entfernen“ ohne Wert würde Inhalte leeren; bei Farben entfällt detach (sie werden nur deaktiviert). +- Danach ist das Löschen (§6.4, §7.2) möglich. + +## 7. Farben und Variablen + +### 7.1 `lit/divi-colors-set` + +- `items`: `{ id? | name?, "label": "Primär", "color": "#5b3a29" | "{{color:Primär}}" + "relative": {hue, saturation, lightness, opacity}, "folder"?, "status": "active"|"inactive" }`. Die fünf Hauptfarben über ihre festen Namen (`accent_color` …). +- Schreiben über `set_global_colors($data, true)` (mischen), Hauptfarben nur, wenn sie geändert werden (Standardwert-Abweichung Formate §4). Neue IDs `gcid-` + 10 Zeichen. +- „Löschen“ = `status: inactive`; der Probelauf nennt Fundstellen als Warnung. +- Zyklen bei relativen Farben (Tiefe max. 10 wie Divi) → `validation_failed`. + +### 7.2 `lit/divi-variables-set` + +- `items`: `{ id? | name?, "type": "numbers|strings|images|links|fonts", "label", "value", "status": "active"|"archived" }`, `delete: true` für echtes Löschen. +- Prüfung je Typ (Zahlen: CSS-Ausdruck ohne `;{}`, Links: URL, Bilder: Anhang-ID/URL der Mediathek, Schriften: §8.2, Strings: Text). +- Schreiben: vollständig lesen, `stdClass` → Array, `allowedActions` und Customizer-Schriften entfernen, `set_global_variables()` (ersetzt), zurücklesen (stiller Fehlschlag bei fehlendem `variables_manager`). +- Löschen nur unbenutzt (G5; Fundstellen auch in Presets und relativen Farben), sonst `in_use` mit Hinweis auf detach. „Archivieren“ geht immer (Divi-Verhalten). + +## 8. Theme-Optionen und Schriften + +### 8.1 `lit/divi-theme-options-get` / `lit/divi-theme-options-update` + +- Whitelist (G3 a): Schriften (`heading_font`, `body_font`, `primary_nav_font`, `secondary_nav_font`, `heading_font_weight`, `body_font_weight`), Größen (`body_font_size`, `tablet_…`, `phone_…`, `body_header_size`, `tablet_header_font_size`, `phone_header_font_size`, `body_font_height`, `body_header_height`, `body_header_spacing`, `body_header_style`), Layout (`content_width`, `gutter_width`, `section_padding`, `row_padding`). +- Die fünf Farben stehen in `get` (lesend), geschrieben werden sie nur über `lit/divi-colors-set` (eine Quelle, Snapshot `colors`) (A8). +- `get` liefert je Feld Typ, Beschreibung, aktuellen Wert und Divi-Standard; `update` bereinigt mit Divis Customizer-Sanitize-Callbacks (Katalog-Adapter), schreibt mit `et_update_option()` und leert die Caches wie der Customizer (`remove_static_resources`, `et_pb_force_regenerate_templates`). + +### 8.2 Schriften + +- Schriftnamen (Theme-Optionen, Presets, Schrift-Variablen) werden gegen Divis Google-Fonts-Liste, hochgeladene Schriften (`et_uploaded_fonts`) und Web-Safe-Schriften geprüft; Unbekanntes → Fehler mit ähnlichen Namen. +- **`lit/divi-fonts-upload`** (G10 b): `name`, `files` (`{ "woff2": "", "woff": …, "ttf": …, "otf": … }`), `weights` (`all` oder Liste), `generic_family`. Laden mit `wp_safe_remote_get` (Größenlimit wie Medien, Grenze je Datei 5 MB (A9)), Dateityp über Inhalt prüfen (Magic Bytes), Ablage mit Divis Upload-Verzeichnis- und Dateityp-Filtern (`upload_dir_font`, `check_filetype_and_ext_font`) über einen eigenen Sideload, weil `FontsUtility::font_add()` nur echte Browser-Uploads annimmt; Eintrag in `et_uploaded_fonts` im Format von `font_add()` (Nachbau mit Fingerabdruck). Namensregeln wie Divi (Sonderzeichen, Länge, kein Google-/vorhandener Name). +- Keine Snapshots für Schriften: ein Upload legt nur neu an, überschreibt nie (A2). Probelauf + `confirm` gelten trotzdem. +- DSGVO: lokales Laden der Google-Fonts bleibt Theme-Einstellung; Tool-Beschreibung weist auf hochgeladene Schriften als datensparsame Variante hin. + +## 9. Schleifen-Inhalte (G12) + +- **Kontext:** Für den Zielknoten wird der nächste Knoten auf dem Weg Wurzel → Knoten (einschließlich des Knotens selbst) gesucht, dessen `module.advanced.loop.desktop.value.enable` an ist; daraus `queryType` (`post_types`, `post_taxonomies`/`terms`, `user_roles`/Benutzer, Menü …). Genaue Regeln (Selbst vs. Vorfahre, verschachtelte Schleifen, Werte von `queryType`) werden im ersten Umsetzungsschritt aus Divi 5.14.0 abgeleitet (`LoopUtils`, `LoopContext`, `DynamicContentLoopOptions`, Visual-Builder-Logik) und in der Formate-Notiz festgehalten. +- **Erlaubte Optionen:** `DynamicContentMap` liefert `loop_*`-Optionen nicht mehr verworfen, sondern getrennt je Query-Typ (Optionen aus Divis Registry, Zuordnung zum Query-Typ aus Divis Code). `DynamicContentRules::fieldsFor()` bekommt den Schleifen-Kontext; `loop_*` nur, wenn eine Schleife vorhanden ist und die Option zum Query-Typ passt. Einstellungen werden wie alle Dynamic-Content-Einstellungen je Feldtyp bereinigt, Meta-Schlüssel-Regeln S29/S30 inklusive `loop_*_meta_key_`. +- **Meldungen:** ohne Schleife: „Schleifen-Inhalt "%s" braucht eine Divi-Schleife in diesem Modul oder einem übergeordneten Element (Abschnitt, Zeile, Spalte, Gruppe).“; falscher Typ: „… passt nicht zur Schleife dieses Elements (Typ: Terme).“ +- **Kurzform:** Platzhalter `{{loop: