{
"tool_response": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"doc\": {\n \"id\": 145,\n \"parent_id\": 144,\n \"slug\": \"htmx\",\n \"path\": \"\/frontend\/htmx\",\n \"title\": \"HTMX\",\n \"description\": \"HTMX-Patterns und Regeln für das Campus-System\",\n \"content\": \"# HTMX\\n\\nHTMX ist die **einzige erlaubte Methode** für AJAX-Interaktionen im Campus-System.\\n\\n## Warum HTMX?\\n\\n| Aspekt | HTMX | fetch()\/XHR |\\n|--------|------|-------------|\\n| CSRF-Enforcement | Automatisch via Contract\/Hook | Manuell, fehleranfällig |\\n| Konsistenz | Deklarativ im HTML | Imperativ in JS verstreut |\\n| Server-Autorität | Ja | Client-State-Chaos möglich |\\n| Progressive Enhancement | Ja | Nein |\\n| Debugging | HTML-Attribute sichtbar | DevTools nötig |\\n\\n## Contract #14: htmx-patterns\\n\\nAlle HTMX-Regeln sind in Contract #14 definiert und werden durch Pre-Hooks enforced.\\n\\n### Kritische Regeln (BLOCK)\\n\\n| ID | Regel | Enforcement |\\n|----|-------|-------------|\\n| HTMX-C1 | `hx-post` MUSS `hx-headers` mit X-CSRF-TOKEN haben | Pre-Hook BLOCK |\\n| HTMX-C2 | `hx-delete` MUSS `hx-headers` mit X-CSRF-TOKEN haben | Pre-Hook BLOCK |\\n| HTMX-C3 | `hx-patch` MUSS `hx-headers` mit X-CSRF-TOKEN haben | Pre-Hook BLOCK |\\n| HTMX-C4 | `hx-delete` MUSS `hx-confirm` Attribut haben | Pre-Hook BLOCK |\\n| HTMX-C5 | `hx-put` MUSS `hx-headers` mit X-CSRF-TOKEN haben | Pre-Hook BLOCK |\\n\\n### Empfohlene Regeln (WARN)\\n\\n| ID | Regel | Enforcement |\\n|----|-------|-------------|\\n| HTMX-R1 | Kein fetch() in View-Dateien | Post-Hook WARN |\\n| HTMX-R2 | `hx-indicator` für Loading-States | Dokumentation |\\n| HTMX-R3 | `hx-swap` explizit angeben | Dokumentation |\\n| HTMX-R4 | `hx-disabled-elt` für Button-States | Dokumentation |\\n\\n## Standard-Patterns\\n\\n### 1. Button mit POST-Action\\n\\n```php\\n<button hx-post=\\\"\/api\/endpoint\\\"\\n hx-headers='{\\\"X-CSRF-TOKEN\\\": \\\"<?= $csrfToken ?>\\\"}'\\n hx-target=\\\"#result\\\"\\n hx-swap=\\\"innerHTML\\\"\\n hx-indicator=\\\"this\\\"\\n hx-disabled-elt=\\\"this\\\">\\n <span class=\\\"htmx-content\\\">Speichern<\/span>\\n <span class=\\\"htmx-indicator\\\">Wird gespeichert...<\/span>\\n<\/button>\\n```\\n\\n### 2. Delete mit Confirmation\\n\\n```php\\n<button hx-delete=\\\"\/api\/items\/<?= $id ?>\\\"\\n hx-headers='{\\\"X-CSRF-TOKEN\\\": \\\"<?= $csrfToken ?>\\\"}'\\n hx-confirm=\\\"Wirklich löschen?\\\"\\n hx-target=\\\"closest tr\\\"\\n hx-swap=\\\"outerHTML swap:0.3s\\\">\\n Löschen\\n<\/button>\\n```\\n\\n### 3. Form mit PUT\\n\\n```php\\n<form hx-put=\\\"\/api\/items\/<?= $id ?>\\\"\\n hx-headers='{\\\"X-CSRF-TOKEN\\\": \\\"<?= $csrfToken ?>\\\"}'\\n hx-target=\\\"#form-message\\\"\\n hx-swap=\\\"innerHTML\\\"\\n hx-indicator=\\\"#save-btn\\\"\\n hx-disabled-elt=\\\"#save-btn\\\">\\n <!-- Form fields -->\\n <button type=\\\"submit\\\" id=\\\"save-btn\\\">Speichern<\/button>\\n<\/form>\\n<div id=\\\"form-message\\\"><\/div>\\n```\\n\\n### 4. Inline-Edit\\n\\n```php\\n<input type=\\\"text\\\" \\n name=\\\"value\\\"\\n value=\\\"<?= htmlspecialchars($value) ?>\\\"\\n hx-post=\\\"\/api\/items\/<?= $id ?>\/field\\\"\\n hx-headers='{\\\"X-CSRF-TOKEN\\\": \\\"<?= $csrfToken ?>\\\"}'\\n hx-trigger=\\\"blur, keyup[key=='Enter']\\\"\\n hx-swap=\\\"none\\\"\\n hx-disabled-elt=\\\"this\\\"\\n hx-on::after-request=\\\"this.classList.toggle('is-saved', event.detail.successful)\\\">\\n```\\n\\n### 5. Select mit Auto-Save\\n\\n```php\\n<select name=\\\"status\\\"\\n hx-post=\\\"\/api\/items\/<?= $id ?>\/status\\\"\\n hx-headers='{\\\"X-CSRF-TOKEN\\\": \\\"<?= $csrfToken ?>\\\"}'\\n hx-swap=\\\"none\\\"\\n hx-disabled-elt=\\\"this\\\">\\n <option value=\\\"draft\\\">Entwurf<\/option>\\n <option value=\\\"active\\\">Aktiv<\/option>\\n<\/select>\\n```\\n\\n## Controller-Integration\\n\\n### Basis-Controller Methoden\\n\\n```php\\n\/\/ In Framework\\\\Controller\\n\\n\/\/ CSRF validieren (wirft 403 bei Fehler)\\n$this->requireCsrf();\\n\\n\/\/ Erfolg-Alert (via HX-Retarget zu #htmx-messages)\\n$this->htmxSuccess('Gespeichert!');\\n\\n\/\/ Fehler-Alert\\n$this->htmxError('Fehler beim Speichern');\\n\\n\/\/ Redirect (via HX-Redirect Header)\\n$this->htmxRedirect('\/items\/' . $id);\\n```\\n\\n### Beispiel-Controller\\n\\n```php\\npublic function update(int $id): void\\n{\\n $this->requireCsrf();\\n \\n try {\\n $data = $this->getJsonInput();\\n $this->useCase->update($id, $data);\\n $this->htmxSuccess('Gespeichert');\\n } catch (\\\\Exception $e) {\\n $this->htmxError($e->getMessage());\\n }\\n}\\n```\\n\\n## Layout-Anforderungen\\n\\n### Globaler Message-Container\\n\\nIn `layout.php` muss dieser Container existieren:\\n\\n```php\\n<main>\\n <div id=\\\"htmx-messages\\\" class=\\\"htmx-messages\\\" aria-live=\\\"polite\\\"><\/div>\\n <?= $content ?>\\n<\/main>\\n```\\n\\n### CSS für HTMX-States\\n\\n```css\\n\/* Message-Container *\/\\n.htmx-messages {\\n position: fixed;\\n top: 80px;\\n right: var(--space-lg);\\n z-index: 1000;\\n max-width: 400px;\\n}\\n\\n\/* Loading-States *\/\\n.htmx-indicator { display: none; }\\n.htmx-request .htmx-indicator { display: inline; }\\n.htmx-request .htmx-content { display: none; }\\n\\n\/* Disabled während Request *\/\\n.htmx-request[hx-disabled-elt] { \\n opacity: 0.6; \\n pointer-events: none; \\n}\\n\\n\/* Erfolgs-Feedback *\/\\n.is-saved {\\n border-color: var(--color-success);\\n animation: flash-success 0.5s;\\n}\\n```\\n\\n## Enforcement\\n\\n### Pre-Hook\\n\\nDatei: `\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_htmx.py`\\n\\nPrüft bei jedem Write\/Edit:\\n- HTMX-Attribute in View-Dateien\\n- CSRF-Token Präsenz\\n- hx-confirm bei Delete\\n\\n### Post-Hook\\n\\nWarnt bei:\\n- fetch() in View-Dateien\\n- Fehlenden Best-Practices\\n\\n## Ausnahmen\\n\\nHTMX ist **nicht** geeignet für:\\n- Echtzeit-Updates (SSE\/WebSocket nötig)\\n- Komplexe Client-Side-Validierung\\n- Offline-Funktionalität\\n\\nFür diese Fälle: Explizite Ausnahme dokumentieren und minimal JS verwenden.\",\n \"status\": \"published\",\n \"sort_order\": 1,\n \"depth\": 1,\n \"created_at\": \"2025-12-27T12:23:53\",\n \"updated_at\": \"2025-12-27T12:23:53\"\n }\n}"
}
]
}