docs: Divi guides shortform/placeholders, hints name the write tools; test: Divi write end-to-end; version 0.3.0
- Hints (PostPresenter::DIVI_NOTE, outline, catalog) and server instructions (tree_rev rule) name the Divi write tools; guides divi-shortform and divi-placeholders, additions to divi-structure, divi-attrs, overview (tool groups, 'nur lesen'), workflows, content-types. - D7 wording and the S7 exception in readme, guides and spec 1.3/5.2; readme 0.3.0. - ToolNamingTest: every referenced guide topic exists; overview lists exactly the topics. - tests/Integration/divi-write-smoke.sh: all write tools via OAuth, editor without unfiltered_html with benign probes (S6), Divi's own save filters on the new fixture synthetic-divi-filters.html (S7); admin-render.php accepts the new wrap class. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -23,14 +23,16 @@ Werkzeugnamen: `lit-…` sind direkt aufrufbare Tools; `lit/…` sind Abilities,
|
||||
Trefferliste. Geliefert werden nur Einträge, die der verbundene Nutzer selbst bearbeiten darf (fremde
|
||||
Entwürfe/private Inhalte ohne die passende Berechtigung erscheinen nicht).
|
||||
- `lit-content-get`: alle Daten eines Eintrags inkl. Feldern, Begriffen, Beitragsbild und `rev`.
|
||||
Bei Divi-Seiten fehlt `content`; den Aufbau liest `lit-divi-outline` (siehe `divi-structure`), geändert
|
||||
wird er vorerst im Divi-Builder (Titel, Auszug, Felder und Begriffe gehen weiter über
|
||||
`lit-content-update`).
|
||||
Bei Divi-Seiten fehlt `content`; den Aufbau liest `lit-divi-outline`, geändert wird er mit den
|
||||
Divi-Werkzeugen (siehe `divi-structure`); Titel, Auszug, Felder und Begriffe gehen weiter über
|
||||
`lit-content-update`.
|
||||
|
||||
## Anlegen und Ändern
|
||||
|
||||
- `lit-content-create` legt immer einen **Entwurf** an. Felder werden gegen das Schema geprüft; ein Fehler
|
||||
bricht den ganzen Aufruf ab (`fields.<schlüssel>` im Fehler nennt die Ursache).
|
||||
bricht den ganzen Aufruf ab (`fields.<schlüssel>` im Fehler nennt die Ursache). Divi-Seiten gleich mit
|
||||
Inhalt: `divi` = Liste von Abschnitten in Kurzform (siehe `divi-shortform`) statt `content`; die Antwort
|
||||
enthält dann die `tree_rev` für weitere Divi-Änderungen.
|
||||
- `lit-content-update` ändert nur die übergebenen Werte, der Status bleibt. `rev` aus `lit-content-get` ist
|
||||
**Pflicht** – hat jemand parallel gespeichert (Inhalt, Titel, Auszug, Status oder irgendetwas anderes am
|
||||
Eintrag), kommt `rev_mismatch`: neu lesen, Änderung auf dem aktuellen Stand wiederholen.
|
||||
@@ -59,8 +61,8 @@ Werkzeugnamen: `lit-…` sind direkt aufrufbare Tools; `lit/…` sind Abilities,
|
||||
## Sicher überarbeiten
|
||||
|
||||
1. `lit/post-duplicate` → Entwurfskopie; die Antwort enthält die `rev` der Kopie für `lit-content-update`.
|
||||
2. Kopie mit `lit-content-update` (Divi-Inhalte vorerst im Divi-Builder) bearbeiten und dem Nutzer die
|
||||
Vorschau zeigen; `lit-page-render-summary` fasst den gerenderten Inhalt zusammen.
|
||||
2. Kopie mit `lit-content-update` bzw. den Divi-Werkzeugen bearbeiten und dem Nutzer die Vorschau zeigen;
|
||||
`lit-page-render-summary` fasst den gerenderten Inhalt zusammen.
|
||||
3. `lit/post-replace-with-copy` übernimmt die Kopie ins Original (ID, URL, Status und Menüeinträge bleiben).
|
||||
Voraussetzungen: die Kopie muss noch Entwurf oder ausstehend sein, `rev` des Originals aus
|
||||
`lit-content-get` ist Pflicht (sie deckt Titel, Inhalt, Auszug und Status ab – Felder/Meta, die direkt am
|
||||
|
||||
@@ -18,6 +18,31 @@ Der Wert eines Pfads ist eine Zuordnung **Breakpoint → Zustand → Wert**:
|
||||
Breakpoints: `desktop`, `tablet`, `phone` (je nach Website auch `tabletWide`, `phoneWide`, `widescreen`,
|
||||
`ultraWide`); Zustände: `value`, `hover`, `sticky`.
|
||||
|
||||
## Setzen
|
||||
|
||||
In `attrs` (bei `lit-divi-insert` je Knoten, bei `lit-divi-update` je Änderung) steht Pfad → Wert je Breakpoint.
|
||||
Kurz: `{"desktop": "#1a1a1a", "phone": "…"}` bzw. bei Objektwerten `{"desktop": {"size": "28px"}}`; Hover und
|
||||
Sticky: `{"hover": {…}}` bzw. `{"desktop": {"value": {…}, "hover": {…}}}`. Nur die genannten Breakpoints
|
||||
werden ersetzt, der Rest bleibt; `null` entfernt einen Pfad, Breakpoint oder Zustand:
|
||||
|
||||
```json
|
||||
{ "id": "s0.r0.c1.m0",
|
||||
"attrs": { "title.decoration.font.font": { "desktop": { "color": "{{color:heading}}" },
|
||||
"phone": { "size": "28px" } },
|
||||
"module.decoration.spacing": null } }
|
||||
```
|
||||
|
||||
Ein Objektwert ersetzt den bisherigen Wert dieses Breakpoints ganz (Unterschlüssel werden nicht
|
||||
zusammengeführt): z. B. bei `module.decoration.sizing` einer Spalte `flexType` immer mitschicken (siehe
|
||||
`divi-shortform`).
|
||||
|
||||
Unbekannte Pfade → Fehler `invalid_attr_path` mit Vorschlägen unter `fields`. Texte werden bereinigt
|
||||
(HTML nur, wo Divi Rich-Text erwartet; Links ohne `javascript:`). `$variable`-Verweise werden geparst,
|
||||
geprüft und vom Plugin neu erzeugt (siehe `divi-placeholders`). Eigenes HTML, CSS und HTML-Attribute
|
||||
(`…decoration.attributes`, `…advanced.html`, `…css…`) nur mit dem Recht `unfiltered_html`, sonst „nur im
|
||||
Divi-Builder“. Unberührte Teile der Seite bleiben byte-identisch; Ausnahme: Divi selbst normalisiert Seiten
|
||||
mit eigenen HTML-/Attribut-Feldern beim Speichern (Warnung in der Antwort, neue `tree_rev` gilt).
|
||||
|
||||
## Pfade finden
|
||||
|
||||
- `lit-divi-catalog` ohne `module` listet alle Module dieser Website; mit `module` (z. B. `"heading"`) alle
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Platzhalter für Divi-Inhalte
|
||||
|
||||
Platzhalter stehen **allein** im Wert (nicht mitten im Text) und werden beim Speichern in Divis eigene
|
||||
Verweise übersetzt. So bleibt die Seite mit dem Designsystem verbunden: ändert sich die Primärfarbe, ändert
|
||||
sich auch der Button.
|
||||
|
||||
| Platzhalter | Bedeutung |
|
||||
|---|---|
|
||||
| `{{color:primary}}`, `secondary`, `heading`, `body`, `link` | die fünf Theme-Farben |
|
||||
| `{{color:<Name>}}` | eigene globale Farbe (Name wie in `lit-divi-design-system`) |
|
||||
| `{{var:<Name>}}` | globale Variable (Zahl, Text, Bild, Link) |
|
||||
| `{{font:heading}}`, `{{font:body}}` | Theme-Schrift für Überschriften bzw. Fließtext |
|
||||
| `{{post:title}}`, `excerpt`, `date`, `modified`, `author`, `url`, `featured_image`, `categories`, `tags` | dynamischer Inhalt des angezeigten Beitrags |
|
||||
| `{{field:<meta_key>}}` | dynamischer Inhalt aus einem Feld (Pods/Meta), z. B. `{{field:telefon}}` |
|
||||
| `"preset": "<Name>"` | Modul-Preset (kein Platzhalter, sondern eigenes Feld) |
|
||||
|
||||
Beispiele:
|
||||
|
||||
```json
|
||||
{ "type": "button", "text": "Anrufen", "url": "{{field:telefon_link}}",
|
||||
"attrs": { "button.decoration.background": { "desktop": { "color": "{{color:primary}}" },
|
||||
"hover": { "color": "{{color:Nachtblau}}" } } } }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "type": "heading", "text": "{{post:title}}", "level": "h1" }
|
||||
```
|
||||
|
||||
Unbekannte Namen lehnt das Werkzeug ab und nennt die vorhandenen; ist ein Name doppelt vergeben, statt des
|
||||
Namens die ID aus `lit-divi-design-system` angeben (z. B. `{{color:gcid-…}}`). Felder, deren Schlüssel mit `_`
|
||||
beginnt (geschützte Meta-Daten), sind nicht erlaubt.
|
||||
|
||||
In Link-Feldern (`url`, `button_url` …) nimmt das Werkzeug jeden dynamischen Inhalt an, auch `{{post:title}}`;
|
||||
sinnvoll sind dort nur Werte, die eine Adresse ergeben (`{{post:url}}`, ein Feld mit einer URL, eine
|
||||
Link-Variable).
|
||||
|
||||
## `$variable`-Werte
|
||||
|
||||
In `lit-divi-node-get` erscheinen die übersetzten Verweise als `$variable({…})$`. Diese Werte unverändert
|
||||
lassen oder wieder durch einen Platzhalter ersetzen. `$variable`-Verweise werden geparst, geprüft und vom
|
||||
Plugin neu erzeugt: erlaubt sind nur ganze Werte mit bekannten Farben, Variablen, Schriften und
|
||||
dynamischen Quellen; Zusatztexte davor/danach (`before`/`after`) werden bereinigt; alles andere (z. B. ein
|
||||
Verweis mitten im Text, eigenes HTML-Ausgeben von Feldern) lehnt das Werkzeug mit `validation_failed` ab.
|
||||
|
||||
Unberührte Teile der Seite bleiben dabei byte-identisch. Ausnahme: Divi selbst normalisiert Seiten mit
|
||||
eigenen HTML-/Attribut-Feldern beim Speichern; die Antwort warnt dann, dass der Inhalt beim Speichern
|
||||
verändert wurde, und die neue `tree_rev` gilt.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Divi-Kurzform
|
||||
|
||||
Neue Divi-Inhalte beschreibt man als JSON-Knoten. `lit-divi-insert` (in `nodes`) und `lit-content-create` (in
|
||||
`divi`) nehmen dieselbe Form. Jeder Knoten hat einen `type`; dazu Kurzform-Felder, optional `label`
|
||||
(Admin-Label), `preset` (Name eines Modul-Presets aus `lit-divi-design-system`) und `attrs` (Vollform, siehe
|
||||
`divi-attrs`). Kurz- und Vollform lassen sich kombinieren, aber nicht für denselben Pfad (dann Fehler
|
||||
`validation_failed` mit dem Pfad).
|
||||
|
||||
## Aufbau
|
||||
|
||||
```json
|
||||
{ "type": "section", "label": "Hero", "background_color": "{{color:primary}}",
|
||||
"rows": [
|
||||
{ "columns": "1_3,2_3",
|
||||
"modules": [
|
||||
[ { "type": "image", "image_id": 33 } ],
|
||||
[ { "type": "heading", "text": "Willkommen", "level": "h1" },
|
||||
{ "type": "text", "content": "<p>Seit 1987 in Köln.</p>" },
|
||||
{ "type": "button", "text": "Termin buchen", "url": "/kontakt", "preset": "CTA primär" } ]
|
||||
] }
|
||||
] }
|
||||
```
|
||||
|
||||
- **Abschnitt** (`section`): `rows` (Zeilen) oder – für eine einspaltige Zeile – direkt `modules`;
|
||||
`section_type: "fullwidth"` für Vollbreite-Abschnitte (dann nur `fullwidth-…`-Module in `modules`);
|
||||
`background_color`. Spezialabschnitte (specialty) nur im Divi-Builder bauen.
|
||||
- **Zeile** (`row`): `columns` = Breiten als Brüche, die zusammen 1 ergeben (`"4_4"`, `"1_2,1_2"`,
|
||||
`"1_3,2_3"`, `"1_4,1_4,1_2"`, auch Fünftel); `modules` = je Spalte eine Liste (bei einer Spalte reicht eine
|
||||
einfache Liste).
|
||||
- **Module** mit Kind-Modulen (Akkordeon, Tabs, Slider, Kontaktformular, Karte, Social-Media-Follow,
|
||||
Icon-Liste) bekommen ihre Einträge in `items`; der Eintragstyp ergibt sich von selbst.
|
||||
- `label` fehlt → Admin-Label „<Abschnitt> – <Modul>“, z. B. „Hero – Button“. Abschnitten deshalb ein Label
|
||||
geben.
|
||||
- `heading` ohne `level` wird `h2` (eine Seite hat meist schon eine `h1` im Titel).
|
||||
|
||||
## Kurzform-Felder
|
||||
|
||||
| Modul | Felder |
|
||||
|---|---|
|
||||
| `text` | `content` (HTML) |
|
||||
| `heading` | `text`, `level` (`h1`–`h6`) |
|
||||
| `button` | `text`, `url`, `new_window` (true/false) |
|
||||
| `image` | `image_id` (Mediathek, setzt URL/ID/Alt), `src`, `alt`, `title`, `url` |
|
||||
| `blurb` | `title`, `url`, `content`, `image_id`, `src`, `alt`, `level` |
|
||||
| `cta` | `title`, `content`, `button_text`, `button_url` |
|
||||
| `icon` | `url`, `title` |
|
||||
| `divider` | `show`, `color` |
|
||||
| `testimonial` | `author`, `job_title`, `company`, `company_url`, `content`, `portrait_id`, `portrait_url` |
|
||||
| `team-member` | `name`, `position`, `content`, `image_id`, `image_url` |
|
||||
| `accordion` → `accordion-item` | Eintrag: `title`, `content`, `open` |
|
||||
| `tabs` → `tab` | Eintrag: `title`, `content` |
|
||||
| `toggle` | `title`, `content`, `open` |
|
||||
| `gallery` | `image_ids` (Liste von Anhang-IDs) |
|
||||
| `slider` → `slide` | Eintrag: `title`, `content`, `button_text`, `button_url`, `image_id`, `src`, `alt` |
|
||||
| `contact-form` → `contact-field` | Formular: `title`, `email`, `success_message`, `submit_text`; Feld: `title`, `id`, `type` (`input`, `email`, `text`, `checkbox`, `radio`, `select`), `required` |
|
||||
| `map` → `map-pin` | Karte: `address`, `lat`, `lng`, `zoom`; Pin: `title`, `content`, `address`, `lat`, `lng` |
|
||||
| `video` | `src`, `thumbnail` |
|
||||
| `number-counter` | `number`, `title`, `percent` |
|
||||
| `social-media-follow` → `social-media-follow-network` | Eintrag: `network` (z. B. `facebook`), `url`, `label` |
|
||||
| `icon-list` → `icon-list-item` | Eintrag: `text` |
|
||||
| `code` | `content` – nur, wenn kein natives Modul passt (Warnung) |
|
||||
|
||||
Alle anderen Module (siehe `lit-divi-catalog`) gehen über `attrs`. Die Kurzform-Felder eines Moduls samt Pfad
|
||||
zeigt `lit-divi-catalog` mit `module`.
|
||||
|
||||
Zwei Felder heißen wie Knoten-Schlüssel und lassen sich deshalb beim Einfügen **nicht** in Kurzform setzen:
|
||||
`type` des Kontaktformular-Felds (`type` ist dort der Modultyp) und `label` des Social-Media-Netzwerks (`label`
|
||||
ist dort das Admin-Label). Beim Einfügen über `attrs` (`fieldItem.advanced.type` bzw.
|
||||
`socialNetwork.innerContent` mit `label`) setzen, danach auch mit `lit-divi-update` über `set`
|
||||
(`{"set": {"type": "email"}}`).
|
||||
|
||||
## Einfügen, ändern, verschieben, löschen
|
||||
|
||||
- `lit-divi-insert`: `nodes` + Position – `parent` (Pfad-ID, z. B. Spalte `s0.r0.c1`; optional `index`),
|
||||
`before` oder `after`; ohne Position ans Seitenende. Oben auf der Seite sind nur Abschnitte erlaubt, in
|
||||
Abschnitten Zeilen, in Spalten Module.
|
||||
- `lit-divi-update`: `changes` = `[{"id": "s0.r0.c1.m2", "set": {"text": "Jetzt buchen"}, "preset": "CTA
|
||||
primär", "label": "Hero – Button"}]` – mehrere Knoten in einem Aufruf; `null` entfernt einen Wert. Denselben
|
||||
Pfad nicht zugleich über `set` und `attrs` ändern (dann zwei Änderungen senden).
|
||||
- `lit-divi-move`: `id` + Position (auch in andere Zeilen oder Abschnitte).
|
||||
- `lit-divi-delete`: `ids`, erst nach Rückfrage mit `confirm: true`.
|
||||
- `lit/divi-duplicate-node`: Kopie direkt hinter dem Original.
|
||||
- Jede Antwort enthält die neue `tree_rev` (für die nächste Änderung), `changed` mit den neuen Pfad-IDs,
|
||||
den betroffenen Abschnitt als `outline`, `warnings` (Qualität, siehe unten) und `revision_id`.
|
||||
|
||||
Fehler: `validation_failed` bzw. `invalid_attr_path` mit `fields` (Eingabepfad → Problem, z. B.
|
||||
`nodes[0].rows[0].columns`); dann wurde nichts gespeichert.
|
||||
|
||||
## Spaltenbreiten ändern
|
||||
|
||||
Die Breite einer Spalte steht in `module.decoration.sizing` (Desktop: `flexType`, z. B. `8_24` = 1/3). Wer
|
||||
diesen Desktop-Wert über `attrs` ersetzt, ersetzt das ganze Objekt: `flexType` immer mitschicken, sonst fällt
|
||||
die Breite weg. In einer Zeile mit festen Breiten alle Spalten zusammen ändern (Summe 1). Die Angabe
|
||||
`flexColumnStructure` der Zeile (Spaltenvorlage im Divi-Builder) passt das Werkzeug nicht an; nach einer
|
||||
Breitenänderung zeigt der Builder dort evtl. noch die alte Vorlage; gespeichert sind die neuen Breiten (bei
|
||||
Bedarf die Vorlage im Builder neu wählen).
|
||||
|
||||
## Sicherheit und Speichern
|
||||
|
||||
Alle Werte werden vor dem Speichern bereinigt: Texte ohne HTML, Rich-Text ohne `unfiltered_html` nur mit dem
|
||||
HTML, das WordPress in Beiträgen erlaubt, Links ohne `javascript:`.
|
||||
`$variable`-Verweise werden geparst, geprüft und vom Plugin neu erzeugt. Eigenes HTML, CSS und HTML-Attribute eines Moduls (`…decoration.attributes`, `…advanced.html`,
|
||||
`…css…`) ändert nur, wer das Recht `unfiltered_html` hat – sonst „nur im Divi-Builder“. Unberührte Teile der
|
||||
Seite bleiben byte-identisch; Ausnahme: Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern
|
||||
beim Speichern (dann Warnung, dass der Inhalt beim Speichern verändert wurde – mit der neuen `tree_rev`
|
||||
weiterarbeiten).
|
||||
|
||||
## Warnungen
|
||||
|
||||
Neue oder geänderte Knoten werden geprüft: feste Farben/Schriften, obwohl eine passende globale existiert;
|
||||
Bilder ohne Alt-Text; leere Module; Sprünge in der Überschriftenhierarchie; Code-Modul. Warnungen dem Nutzer
|
||||
nennen und möglichst gleich beheben.
|
||||
@@ -22,9 +22,24 @@ Spalten (`column`), Spalten Module (`text`, `heading`, `button`, …). Manche Mo
|
||||
des letzten Lesevorgangs.
|
||||
- `rev` ist davon getrennt: der Stand des ganzen Eintrags (Titel, Auszug, Status …) für `lit-content-update`.
|
||||
|
||||
## Ändern
|
||||
|
||||
Neue Inhalte und Änderungen: siehe `divi-shortform` (Kurzform, `lit-divi-insert`, `lit-divi-update`,
|
||||
`lit-divi-move`, `lit-divi-delete`), `divi-attrs` (Vollform) und `divi-placeholders`. Ablauf:
|
||||
|
||||
1. `lit-divi-outline` lesen (liefert `tree_rev`), `lit-divi-design-system` für Farben, Variablen, Presets.
|
||||
2. Änderung mit dieser `tree_rev` senden. Die Antwort enthält die neue `tree_rev` und die neuen Pfad-IDs –
|
||||
damit direkt weiterarbeiten, ohne erneut zu lesen.
|
||||
3. `rev_mismatch`: jemand hat die Seite inzwischen geändert (z. B. im Divi-Builder) → neu lesen.
|
||||
4. Zum Schluss `lit-page-render-summary` prüfen und dem Nutzer die Vorschau (`preview_url`) bzw. den
|
||||
Divi-Builder (`edit_url`) nennen. Der Status bleibt unverändert; veröffentlichte Seiten werden live
|
||||
geändert (vorher entsteht eine Revision, `revision_id`).
|
||||
|
||||
## Was nicht bearbeitet wird
|
||||
|
||||
- Knoten mit „nur lesbar“ (globale Module `g…`, fremde Blöcke `x…`) ändern die Divi-Werkzeuge nicht.
|
||||
- Knoten mit „nur lesbar“ (globale Module `g…`, fremde Blöcke `x…`) lassen sich nur als Ganzes verschieben,
|
||||
duplizieren oder löschen; ihr Inhalt wird im Divi-Builder bearbeitet (globale Module folgen später).
|
||||
`lit/divi-replace-text` ändert sie nicht, sondern nennt sie in `skipped_nodes`.
|
||||
- Divi-4-Inhalt (Shortcodes `[et_pb_…]`) wird nicht gelesen oder geändert: Seite einmal im Divi-Builder
|
||||
öffnen und speichern, Divi 5 wandelt sie dabei um.
|
||||
- Ist das Markup beschädigt, lehnen die Werkzeuge ab (`unsupported`); die Seite im Divi-Builder öffnen und
|
||||
|
||||
@@ -23,8 +23,12 @@ Direkt sichtbar: `lit-guide`, `lit-site-status`, `lit-content-types`, `lit-conte
|
||||
`lit-divi-outline`, `lit-divi-node-get`, `lit-divi-catalog`, `lit-divi-design-system`, `lit-divi-insert`,
|
||||
`lit-divi-update`, `lit-divi-move`, `lit-divi-delete`.
|
||||
|
||||
Welche Tools direkt sichtbar sind, lässt sich unter Einstellungen → LIT MCP → Tools ändern (außer bei
|
||||
`lit-guide`, das immer direkt bleibt).
|
||||
Die Tools sind nach Integration gruppiert (Grundlagen, WordPress, Medien, Divi). Jede Gruppe hat unter
|
||||
Einstellungen → LIT MCP → Tools eine eigene Einstellung (direkt/indirekt/deaktiviert), die ihre Tools übernehmen
|
||||
(„wie Gruppe“). Eine Einstellung am einzelnen Tool geht vor; die Gruppe zeigt dann „gemischt“. „nur lesen“
|
||||
schaltet alle schreibenden Tools der Gruppe ab – auch solche mit eigener Einstellung. `lit-guide` bleibt immer
|
||||
direkt; `lit-media-preview`/`lit-media-download` bleiben bei Gruppe „indirekt“ direkt. Ab mehr als 25 direkten
|
||||
Tools warnt die Seite (Kontextkosten in claude.ai). Änderungen gelten ab der nächsten Verbindung.
|
||||
|
||||
Über `mcp-adapter-discover-abilities` / `mcp-adapter-execute-ability` (Ability-Namen `lit/…`; in Guides und
|
||||
Hinweisen immer so geschrieben):
|
||||
@@ -37,12 +41,13 @@ Einstellungen (`lit/wp-settings-get`, `lit/wp-settings-update`), Medien (`lit/me
|
||||
Die `lit-divi-…`-Werkzeuge gibt es nur, wenn der Divi-5-Builder aktiv ist.
|
||||
|
||||
Weitere Themen: `lit-guide` mit `topic` = `content-types`, `workflows`, `media`, `divi-structure`,
|
||||
`divi-attrs` oder `divi-design-system`.
|
||||
`divi-shortform`, `divi-attrs`, `divi-placeholders` oder `divi-design-system`.
|
||||
|
||||
## Rückmeldungen
|
||||
|
||||
- Erfolg: `ok`, `summary`, `changed`, `warnings`, `rev`, `revision_id` (Revision mit dem Stand vorher, nur bei
|
||||
Tools, die Inhalt überschreiben, sonst `null`), `log_id`, URLs.
|
||||
Tools, die Inhalt überschreiben, sonst `null`), `log_id`, URLs. Divi-Werkzeuge zusätzlich `tree_rev` (Stand
|
||||
des Seiteninhalts, Pflicht für die nächste Divi-Änderung) und `outline` (betroffener Abschnitt).
|
||||
- Fehler: JSON mit `code` (z. B. `validation_failed`, `rev_mismatch`, `write_failed`, `partial_write`),
|
||||
`message`, `hint` (und ggf. `fields`). Dem `hint` folgen.
|
||||
- `confirm_required`: Nutzer fragen, dann mit `confirm: true` wiederholen.
|
||||
|
||||
@@ -11,6 +11,25 @@
|
||||
5. Menü: `lit/menu-create` (optional mit `location` aus `lit/menu-list`), Einträge mit `lit/menu-set-items`.
|
||||
6. Veröffentlichen erst, wenn der Nutzer es ausdrücklich möchte.
|
||||
|
||||
## Divi-Seite bauen
|
||||
|
||||
1. `lit-divi-design-system` lesen (Farben, Schriften, Presets) und `lit-divi-catalog` für die Module.
|
||||
2. Seite als Entwurf mit Inhalt anlegen: `lit-content-create` mit `divi` (Abschnitte in Kurzform, siehe
|
||||
`divi-shortform`) – oder eine leere Seite anlegen und Abschnitte mit `lit-divi-insert` einfügen.
|
||||
3. Weitere Abschnitte/Module mit `lit-divi-insert`, Anpassungen mit `lit-divi-update`; immer die `tree_rev`
|
||||
der letzten Antwort mitgeben.
|
||||
4. `lit-page-render-summary` prüfen, `warnings` beheben, dem Nutzer `preview_url` und `edit_url` (Divi-Builder)
|
||||
nennen. Veröffentlichen nur auf Wunsch (`lit-post-publish`).
|
||||
|
||||
## Text auf vielen Seiten ersetzen
|
||||
|
||||
`lit/divi-replace-text` zuerst mit `dry_run: true` (Standard): zeigt Treffer je Seite mit `tree_rev`. Nach
|
||||
Rückfrage mit `dry_run: false`, `targets` (post_id + tree_rev aus dem Probelauf) und `confirm: true`
|
||||
ausführen; jede Seite bekommt eine eigene Revision, `results` meldet das Ergebnis je Seite. Ersetzt wird nur
|
||||
sichtbarer Text (keine Links, Stile, Code oder dynamischen Inhalte); in reinen Textfeldern (Titel,
|
||||
Button-Texte) werden Leerzeichen am Anfang und Ende der Ersetzung entfernt. Globale Module werden nicht
|
||||
geändert, sondern in `skipped_nodes` genannt – dem Nutzer sagen, dass er sie in der Divi-Bibliothek ändern muss.
|
||||
|
||||
## Menüs pflegen
|
||||
|
||||
- `lit/menu-get` liefert den Baum und `menu_rev` (Stand des Menüs). `lit/menu-set-items` verlangt diese
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# Etappe 2b – Manuelle Abnahme (Divi schreiben)
|
||||
|
||||
Vorbereitet von Task 10 (Hinweise, Guides, Version 0.3.0, Ende-zu-Ende-Test). Automatisch geprüft sind
|
||||
bereits:
|
||||
|
||||
- alle Unit-Tests (`composer test`) und Lint (`composer lint`);
|
||||
- die fünf Integrationsskripte `divi-write-smoke.sh`, `divi-read-smoke.sh`, `mcp-smoke.sh`,
|
||||
`wp-basis-smoke.sh` und `media-admin-smoke.sh` (alle in `tests/Integration/`).
|
||||
|
||||
`divi-write-smoke.sh` prüft über den OAuth-Endpunkt:
|
||||
|
||||
- Einfügen, Ändern, Verschieben, Löschen und Duplizieren;
|
||||
- Ersetzen mit Probelauf;
|
||||
- `lit-content-create` mit Divi-Inhalt;
|
||||
- den ersten Abschnitt auf einer leeren Seite;
|
||||
- einen Redakteur ohne `unfiltered_html`: unberührte Bytes bleiben gleich, und es wird weder Skript,
|
||||
Event-Handler, `javascript:` noch ein eingeschleustes Attribut gespeichert oder gerendert;
|
||||
- Divis eigene Speicherfilter (Seite mit eigenen HTML-Attributen).
|
||||
|
||||
Details stehen im Report von Task 10 (`.superpowers/sdd/2026-09-26-etappe-2b-divi-schreiben/task-10-report.md`).
|
||||
|
||||
Die folgenden Punkte lassen sich nicht automatisch prüfen, weil sie den echten Divi Visual Builder und den
|
||||
claude.ai-Connector brauchen. Jan-Niclas prüft sie selbst (Spec §1.3 Nr. 2, §13 „Bearbeitbarkeit im
|
||||
Visual Builder“). Abweichungen als kurze Notiz zurückmelden; sie werden Folgeaufgaben (z. B. anders
|
||||
gespeicherte Spaltenbreiten, D8).
|
||||
|
||||
## 1. Visual Builder: von Claude gebaute Seite
|
||||
|
||||
- [ ] Dieser Auftrag in claude.ai:
|
||||
„Lege eine Entwurfsseite ‚Abnahme Divi‘ an mit Hero (Überschrift, Text, Button mit Preset), einer
|
||||
Zeile 1/3 + 2/3 mit Bild und Blurb, einem Akkordeon und einem Kontaktformular.“
|
||||
Erwartet:
|
||||
- Claude liest vorher `lit-divi-design-system` und `lit-divi-catalog`;
|
||||
- dann `lit-content-create` mit `divi` (oder eine leere Seite plus `lit-divi-insert`);
|
||||
- die Antwort nennt `preview_url` und `edit_url`.
|
||||
- [ ] Die Seite im Divi Visual Builder öffnen (`edit_url`) und prüfen:
|
||||
- [ ] Alle Module erscheinen als **native Module**, nicht als Code-Modul.
|
||||
- [ ] Die Admin-Labels folgen dem Muster „<Abschnitt> – <Modul>“, z. B. „Hero – Button“.
|
||||
- [ ] Jedes Modul lässt sich anklicken, ändern und speichern, ohne Fehlermeldung des Builders.
|
||||
- [ ] Die Spaltenbreiten stimmen (1/3 + 2/3), auch in der Spalteneinstellung des Builders.
|
||||
- [ ] Farben aus `{{color:…}}` sind als **globale Farbe** verknüpft: der Farbwähler zeigt die globale
|
||||
Farbe, nicht einen festen Hex-Wert.
|
||||
- [ ] Das Button-Preset ist im Builder als Preset ausgewählt.
|
||||
- [ ] Akkordeon-Einträge und Kontaktformular-Felder sind einzeln bearbeitbar.
|
||||
- [ ] Eine zweite Seite nur mit `lit-divi-insert` aufbauen lassen: leere Seite anlegen, dann
|
||||
„Füge einen Abschnitt ‚Start‘ mit Überschrift und Text ein“. Im Builder öffnen: der Abschnitt
|
||||
erscheint normal und ist bearbeitbar (Ruling S9: Hülle `divi/placeholder` wie beim Builder).
|
||||
|
||||
## 2. Visual Builder: von Claude geänderte Seite
|
||||
|
||||
- [ ] Eine bestehende, im Builder gebaute Seite kopieren (`lit/post-duplicate`) und an der Kopie per
|
||||
claude.ai einen Button-Text ändern, ein Modul verschieben und einen Abschnitt duplizieren lassen.
|
||||
- [ ] Die Kopie im Builder öffnen und prüfen:
|
||||
- [ ] Alle unveränderten Module sehen aus wie vorher (Einstellungen, Presets, globale Farben).
|
||||
- [ ] Die geänderten Module sind bearbeitbar, und die Änderung ist sichtbar.
|
||||
- [ ] Die Kopie eines Abschnitts heißt „<Label> (Kopie)“.
|
||||
|
||||
## 3. Zurück zu Claude nach einer Builder-Änderung
|
||||
|
||||
- [ ] Die Seite aus Schritt 1 im Builder ändern und speichern.
|
||||
- [ ] Danach per claude.ai eine weitere Änderung verlangen. Erwartet:
|
||||
- [ ] `rev_mismatch` erscheint nur, wenn Claude eine alte `tree_rev` benutzt.
|
||||
- [ ] Nach erneutem Lesen (`lit-divi-outline`) gelingt die Änderung.
|
||||
- [ ] Die im Builder gemachten Einstellungen bleiben erhalten.
|
||||
|
||||
## 4. Rückgängig
|
||||
|
||||
- [ ] Einen Abschnitt löschen lassen. Claude fragt nach (`confirm_required`) und löscht erst nach der
|
||||
Bestätigung.
|
||||
- [ ] Den Abschnitt über `lit/revision-restore` mit der gemeldeten `revision_id` zurückholen und im Builder
|
||||
prüfen, dass er wieder vollständig da ist.
|
||||
|
||||
## 5. Text auf mehreren Seiten ersetzen
|
||||
|
||||
- [ ] „Ersetze auf allen Seiten ‚Termin‘ durch ‚Beratungstermin‘“. Erwartet:
|
||||
- [ ] Claude zeigt erst den Probelauf (Treffer je Seite) und fragt nach.
|
||||
- [ ] Erst danach ersetzt es.
|
||||
- [ ] Im Protokoll-Tab (Einstellungen → LIT MCP → Protokoll) steht je geänderter Seite ein Eintrag mit
|
||||
eigener Revision.
|
||||
- [ ] Liegt ein Treffer in einem **globalen Modul**, ändert Claude ihn nicht, sondern nennt ihn
|
||||
(`skipped_nodes`, „in der Divi-Bibliothek bearbeiten“).
|
||||
|
||||
## 6. Redakteur ohne `unfiltered_html`
|
||||
|
||||
- [ ] Mit einem Redakteurs-Konto verbinden; unter Einstellungen → LIT MCP → Zugriff muss die Rolle
|
||||
freigegeben sein.
|
||||
- [ ] Auf einer Seite eines Administrators einen Text ändern lassen.
|
||||
- [ ] Die Seite im Builder öffnen. Erwartet:
|
||||
- [ ] Eingebettete Inhalte des Administrators (z. B. iframe, eigenes HTML im Code-Modul) sind
|
||||
unverändert.
|
||||
- [ ] Die Änderung des Redakteurs ist da.
|
||||
- [ ] Den Redakteur eigenes HTML/CSS/Attribute eines Moduls setzen lassen, z. B. „füge dem Button das
|
||||
HTML-Attribut data-x hinzu“. Erwartet: Ablehnung mit „nur im Divi-Builder änderbar“.
|
||||
|
||||
## 7. Tool-Gruppen und „nur lesen“
|
||||
|
||||
- [ ] Unter Einstellungen → LIT MCP → Tools die Gruppe Divi auf „nur lesen“ stellen.
|
||||
- [ ] Neu verbinden. Die Divi-Schreibwerkzeuge (`lit-divi-insert` usw.) fehlen; die Lesewerkzeuge sind
|
||||
noch da.
|
||||
- [ ] Die Einstellung zurücksetzen.
|
||||
|
||||
## 8. Builder-Fixtures aus 2a (falls noch offen; Ruling S20)
|
||||
|
||||
- [ ] Seiten mit **globalem Modul**, Preset, Variablen und Dynamic Content exportieren:
|
||||
`tests/Integration/divi-export-page.php` (Anleitung in `2026-09-26-etappe-2a-abnahme.md` §1).
|
||||
`composer test` bleibt grün.
|
||||
- [ ] Auf einer Kopie einer solchen Seite `lit-divi-update` an einem normalen Modul ausführen. Das
|
||||
globale Modul bleibt byte-identisch: Export vorher und nachher vergleichen.
|
||||
- [ ] Hat die Seite Module mit eigenen HTML-Attributen oder einem eigenen HTML-Element (Erweitert →
|
||||
Attribute/HTML), meldet die Antwort evtl. „Inhalt beim Speichern verändert“. Das ist dokumentiert
|
||||
(Ruling S7: Divi normalisiert solche Seiten selbst). Prüfen, dass die Seite im Builder danach
|
||||
einwandfrei ist.
|
||||
@@ -18,7 +18,7 @@ Ein WordPress-Plugin, das auf jeder Kundenseite einen MCP-Server bereitstellt, m
|
||||
|
||||
1. Ein claude.ai-Connector verbindet sich per OAuth mit einer Kundenseite; nur freigegebene Rollen können Tools nutzen.
|
||||
2. Von Claude erzeugte oder geänderte Divi-Inhalte sind im Visual Builder vollständig bearbeitbar (manuell verifiziert pro Etappe).
|
||||
3. Unangetastete Inhalte bleiben byte-identisch erhalten.
|
||||
3. Unangetastete Inhalte bleiben byte-identisch erhalten. Dokumentierte Ausnahme (Ruling S7): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`AttributeSecurity`/`HtmlSecurity` auf `wp_insert_post_data`, laufen für jeden Benutzer und werden nie entfernt); die Antwort meldet das als Warnung „Inhalt beim Speichern verändert“ (`CHANGED_ON_SAVE`), die Seite bleibt gültig (E2E `divi-write-smoke.sh` Abschnitt 8).
|
||||
4. Eine neue Website (Seitenstruktur, Menü, Einstellungen, Designsystem aus einem CD) lässt sich im Dialog aufsetzen.
|
||||
5. Jede Änderung ist nachvollziehbar (Protokoll) und umkehrbar (Revision bzw. Snapshot).
|
||||
|
||||
@@ -171,7 +171,7 @@ Seiten, Theme-Builder-Layouts (`et_header_layout`, `et_body_layout`, `et_footer_
|
||||
|
||||
- Parst `post_content` in einen Baum (auf Basis von `parse_blocks`, mit Sonderbehandlung für `divi/global-layout` und `globalModule`, die `parse_blocks` laut Divi-Code nicht korrekt verarbeitet).
|
||||
- Serialisiert den Baum zurück; unveränderte Knoten werden aus ihrem Original-Markup übernommen.
|
||||
- **Invariante:** Parse → Serialize ohne Änderung ergibt byte-identischen Inhalt. Fremde/unbekannte Blöcke bleiben unberührt.
|
||||
- **Invariante:** Parse → Serialize ohne Änderung ergibt byte-identischen Inhalt. Fremde/unbekannte Blöcke bleiben unberührt. Ausnahme beim Speichern (nicht im BlockTree): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`module.decoration.attributes`, `module.advanced.html` mit `htmlBefore`/`htmlAfter`/`elementType`) – Divi parst und serialisiert dann die ganze Seite neu; gemeldet als `CHANGED_ON_SAVE` (Ruling S7).
|
||||
- Reines PHP ohne WordPress-Abhängigkeit (vollständig unit-testbar).
|
||||
- Attributwerte mit `$variable(...)$` werden beim Serialisieren wie von Divi erwartet escaped (`"` → `"`).
|
||||
- Umsetzung (D3): eigener Tokenizer mit dem Delimiter-Muster von `WP_Block_Parser` statt `parse_blocks()` – ohne WordPress testbar, jedes Byte zwischen den Delimitern bleibt erhalten. Knoten mit `globalModule`, `divi/global-layout` oder nicht dekodierbaren Attributen sind undurchsichtig: sie werden nicht betreten, nur als Ganzes verschoben, dupliziert oder gelöscht (Bearbeitung ab Etappe 3). Unbalanciertes Markup wird abgelehnt, nie repariert.
|
||||
|
||||
Reference in New Issue
Block a user