Extrahiere statisches HTML
Extrahiere eine eigenständige statische HTML-Datei aus jeder Webanwendung.
Welche Strategie verwenden
Du MUSST den Benutzer bitten, vor dem Fortfahren eine Strategie auszuwählen. Präsentiere die Optionen klar, empfehle Strategie A als bevorzugte Standardeinstellung und gib eine kurze Vor-/Nachteile-Zusammenfassung für jede Option, um dem Benutzer eine fundierte Entscheidung zu ermöglichen.
| Strategie A (Puppeteer) | Strategie B (Browser-Subagent) | |
|---|---|---|
| Wann | App läuft lokal, keine Authentifizierungshürde | Benötigt Interaktion mit der Seite (Klicken, Formulare ausfüllen) |
| Wiedergabetreue | Höchste — berechnete Stile aufgelöst | Hoch — gerendertes DOM |
| Einrichtung | Null — kein Mock nötig | Null — kein Mock nötig |
| Framework | Beliebig | Beliebig |
| Ausgabe | Schreibt in Datei — keine Größenbeschränkung | Kann im Agent-Kontext abgeschnitten werden |
[!WARNING] Kontrollpunkt — Benutzerbestätigung erforderlich. Du MUSST den Benutzer fragen, welche Strategie er bevorzugt, bevor du fortfährst. Präsentiere die obige Vergleichstabelle, empfehle Strategie A als Standard und warte auf explizite Zustimmung. Triff die Entscheidung NICHT selbst und fahre erst fort, wenn der Benutzer bestätigt hat.
Strategie A: Puppeteer-Snapshot (Empfohlen)
Startet einen kopflosen Chrome-Browser, erfasst das vollständig gerenderte DOM und erzeugt eine eigenständige HTML-Datei mit allen inline eingebundenen CSS und als Base64 kodierten Bildern. Funktioniert mit jedem Framework — kein MockPage.jsx erforderlich.
Voraussetzungen
- App läuft lokal (z. B.
npm run dev) - Node.js mit
puppeteerverfügbar (prüfen:node -e "require('puppeteer')")
Arbeitsablauf
-
Starte die App und notiere den Port.
[!WARNING] Kontrollpunkt — Benutzerbestätigung erforderlich. Nach dem Start des lokalen Servers MUSST du innehalten und den Benutzer um Bestätigung bitten, bevor du das Snapshot-Skript ausführst oder einen Browser-Subagenten startest. Melde die URL und den Port dem Benutzer, damit er überprüfen kann, ob die App läuft und korrekt rendert. Fahre NICHT mit dem Snapshot-Schritt fort, bis der Benutzer bestätigt.
-
Führe das Snapshot-Skript aus:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 \ --output .stitch/home.html \ --wait 2000 -
Mehrere Seiten — einmal pro Route ausführen:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 --output .stitch/home.html --wait 2000 npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173/pricing --output .stitch/pricing.html --wait 2000 npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173/dashboard --output .stitch/dashboard.html --wait 2000 --html-class dark -
Bereinige den Dev-Server: Wenn ein lokaler Dev-Server speziell für die Snapshot-Extraktion gestartet wurde, stelle sicher, dass der Server-Prozess gestoppt oder die Hintergrundaufgabe beendet wird, sobald die Extraktion abgeschlossen ist.
Skript-Flags
| Flag | Standard | Beschreibung |
|---|---|---|
--url | (erforderlich) | Zu erfassende URL |
--output | (erforderlich) | Ausgabedateipfad |
--wait | 1000 | Zusätzliche Wartezeit (ms) nach Netzwerkinaktivität. Erhöhen für Lazy-Loading-Apps. |
--viewport | 1280x800 | Viewport-Größe als BREITExHÖHE |
--html-class | — | Klasse(n) für das <html>-Element (z. B. dark) |
--remove-fixed | false | Entferne fixierte/sticky-Elemente (Cookie-Banner, Chat-Widgets) |
--full-height | false | Passe Viewport an die volle Bildlaufhöhe an |
--title | — | Überschreibe den Seitentitel (auf den Routenpfad setzen, z. B. /dashboard oder /settings/profile) |
--auth-script | — | Pfad zu einem JS/TS-Modul, das eine Standard-async (page) => void-Funktion zur Authentifizierung exportiert |
--inline-canvas | false | Konvertiere <canvas>-Elemente (ECharts, Chart.js, D3) in Base64-<img>-Tags |
Was automatisch gemacht wird
- Erfasst alle CSSOM-Regeln aus
document.styleSheets(bewahrt dynamische Vite/Tailwind-Dev-Styles und CSS-in-JS) - Bindet alle
<link rel="stylesheet">→<style>-Blöcke inline ein - Konvertiert
<img>srcundsrcset→ Base64-Daten-URIs (überspringt externe Schriftarten) - Bindet gleichherkunft- und relative Icon-Font-Dateien (
@font-face) als Base64-Daten-URIs ein, sodass Ligaturen nie als ASCII-Text gerendert werden - Bindet
<source srcset>-URLs als Base64 ein - Entfernt fehlgeschlagene/tote
srcset-Einträge, sodass der Browser auf das inline eingebundenesrczurückfällt - Entfernt
<script>-Tags, Vite-HMR-Dev-Style-Blöcke (createHotContext,import.meta.hot) und Dev-Overlays - Löst relative CSS-
url()-Pfade vor dem Inline-Einbinden auf
Framework-Hinweise
| Framework | Hinweise |
|---|---|
| React + Vite | Funktioniert sofort. --wait 1000. |
| Next.js | --wait 3000 für SSR-Hydration. URL: http://localhost:3000. <img srcset> von /_next/image wird automatisch als Base64 inline eingebunden. |
| Angular (@angular/cli / v17+) | Funktioniert sofort mit ng serve (Standard-URL: http://localhost:4200). --wait 2000 für Angular Material / PrimeNG-Animation-Hydration und Lazy-Loaded-Routen. |
| Vue / Nuxt | Funktioniert sofort. |
| Svelte / SvelteKit | Funktioniert sofort. |
| Storybook | Story-URL verwenden: --url http://localhost:6006/?path=/story/... |
| SSR (Webpack) | Möglicherweise längeres --wait erforderlich. |
Fehlerbehebung
| Problem | Lösung |
|---|---|
| Bilder fehlen | --wait erhöhen |
| Bilder werden nach Serverstopp als defekt angezeigt | Überprüfen, ob srcset inline eingebunden wurde – im Log nach "Inlined N images" suchen. Wenn srcset-URLs fehlgeschlagen sind, werden sie automatisch entfernt, sodass src (inline) verwendet wird. |
| Symbole werden als Text / Serifen-Unstyled-Schriftart angezeigt | Stelle sicher, dass snapshot.ts das CSSOM aus document.styleSheets erfasst (Schritt 0) und gleichherkunft-Icon-Fonts (@font-face) als Base64-Daten-URIs inline eingebunden werden. |
Next.js /_next/image nicht inline eingebunden | Stelle sicher, dass der Dev-Server läuft, wenn der Snapshot ausgeführt wird – das Skript ruft optimierte Bilder vom laufenden Server ab. |
| Dunkelmodus nicht angewendet | --html-class dark |
| Cookie-Banner in der Ausgabe | --remove-fixed |
| Seite erfordert Anmeldung | --auth-script ./auth.ts verwenden (siehe Auth-Geschützte Seiten unten) |
| Diagramme/Grafiken als leere Kästchen | --inline-canvas verwenden, um <canvas> in Base64-<img> zu serialisieren |
Cannot find module 'puppeteer' | npm install -g puppeteer |
Auth-Geschützte Seiten
Für Apps mit Login-Guards (Vue Router beforeEach, React ProtectedRoute usw.) ein kleines Auth-Skript erstellen, das in der Puppeteer-Sitzung ausgeführt wird:
// auth-myapp.ts
import type { Page } from 'puppeteer';
export default async function authenticate(page: Page) {
// Beispiel 1: Login-Formular ausfüllen und absenden
await page.type('#username', 'admin');
await page.type('#password', 'password123');
await page.click('#login-button');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
// Beispiel 2: Cookies/localStorage direkt injizieren
// await page.evaluate(() => {
// localStorage.setItem('token', 'mock-jwt-token');
// });
// Beispiel 3: Die eigene Login-API der App über Modulinjektion aufrufen (Vue/Vite)
// await page.evaluate(() => {
// return new Promise((resolve) => {
// const script = document.createElement('script');
// script.type = 'module';
// script.textContent = `
// import { useUserStore } from '/src/store/modules/user.ts';
// import { fetchLogin } from '/src/api/auth.ts';
// const res = await fetchLogin({ userName: 'Admin', password: '123456' });
// useUserStore().setToken(res.token, res.refreshToken);
// window.dispatchEvent(new CustomEvent('auth-done'));
// `;
// document.head.appendChild(script);
// window.addEventListener('auth-done', () => resolve(true), { once: true });
// });
// });
}
Dann verwenden:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
--url http://localhost:5173/#/dashboard \
--output .stitch/dashboard.html \
--auth-script ./auth-myapp.ts \
--inline-canvas \
--wait 5000
Das Skript navigiert zuerst zur --url (die möglicherweise zur Anmeldung weiterleitet), führt die Auth-Funktion aus und navigiert dann erneut zur ursprünglichen --url mit der authentifizierten Sitzung.
Strategie B: Browser-Subagent-Erfassung
Verwende diese Strategie, wenn du mit der Seite interagieren musst (Schaltflächen klicken, Formulare ausfüllen, Tabs navigieren), bevor du sie erfasst. Der Browser-Subagent gibt dir die volle Kontrolle, aber die Ausgabe kann bei großen Seiten abgeschnitten werden.
Arbeitsablauf
-
Starte die App lokal.
-
Navigiere mit einem Browser-Subagenten.
-
Interagiere nach Bedarf (Klicken, Scrollen, Formulare ausfüllen).
-
Extrahiere das DOM:
document.documentElement.outerHTML[!WARNING] Große Seiten können abgeschnitten werden. So gehst du damit um:
- Entferne
<style>-Tags vor der Extraktion:document.querySelectorAll('style').forEach(el => el.remove()) - Füge Styles statisch wieder hinzu (Tailwind-CDN-Link, Quell-CSS)
- Entferne
-
Speichere in einer Datei.
Anhang: Statischer Fallback (MockPage.jsx)
[!NOTE] Diese Methode ist eine letzte Notlösung für den Fall, dass die App nicht lokal ausgeführt werden kann (kaputte Abhängigkeiten, fehlendes Backend, Authentifizierungshürden ohne Umgehung). Sie erfordert das manuelle Flatten von React-Komponenten in eine einzige JSX-Datei. Bevorzuge wann immer möglich Strategie A.
Wann verwenden
- App kann überhaupt nicht lokal ausgeführt werden
- Seite erfordert Authentifizierung ohne Mock/Umgehung
- Ein bestimmter UI-Zustand wird benötigt, der durch Navigation nicht erreichbar ist (Fehlerbildschirme, leere Zustände)
Kurzreferenz
npx tsx <SKILL_DIR>/scripts/extract_inline_html.ts \
--index-css src/css/App.css \
--extra-css index.html \
--outdir .stitch \
--page src/MockPage.jsx:Page.html:"Page Title"
Wichtige Flags: --no-tailwind (Nicht-Tailwind-Apps), --html-class dark (Dunkelmodus), --css-files (zusätzliche CSS-Dateien).
Auto-Erkennung: Tailwind-Konfiguration wird automatisch erkannt. @apply-Direktiven verwenden automatisch <style type="text/tailwindcss">.
MockPage.jsx-Regeln
- Füge das vollständige Layout ein — Kopfzeile, Seitenleiste, Fußzeile (lies zuerst
App.js) - Flatte alle Bedingungen — wähle einen Zustand, entferne alle Ternäre und
&&-Guards - Harte Kodierung aller Daten — ersetze
{variable}durch konkrete Werte, rolle.map()-Schleifen auf - Logos beibehalten — verwende
<img>mit lokalen Pfaden (die Nachbearbeitung bindet sie inline ein) - Schwebende Elemente entfernen — Cookie-Banner, Chat-Widgets, Feedback-Buttons
Nachbearbeitung
Lokale Bilder inline einbinden:
npx tsx <SKILL_DIR>/scripts/post_process.ts \
.stitch/Page.html --base-dir <app-directory>