Extraire du HTML Statique
Extrayez un fichier HTML statique autonome depuis n'importe quelle application web.
Quelle Stratégie Utiliser
Vous DEVEZ demander à l'utilisateur de choisir la stratégie à utiliser avant de procéder. Présentez clairement les options, recommandez la Stratégie A comme choix par défaut, et fournissez un bref résumé des avantages et inconvénients pour chaque option afin de l'aider à prendre une décision éclairée.
| Stratégie A (Puppeteer) | Stratégie B (Sous-agent Navigateur) | |
|---|---|---|
| Quand | L'application tourne localement, pas de mur d'authentification | Besoin d'interagir d'abord avec la page (clic, remplir des formulaires) |
| Fidélité | Maximale — styles calculés résolus | Élevée — DOM rendu |
| Configuration | Aucune — pas de maquette nécessaire | Aucune — pas de maquette nécessaire |
| Framework | Quelconque | Quelconque |
| Sortie | Écrit dans un fichier — pas de limite de taille | Peut être tronquée dans le contexte de l'agent |
[!WARNING] Point de contrôle — Confirmation de l'utilisateur requise. Vous DEVEZ demander à l'utilisateur quelle stratégie il préfère avant de continuer. Présentez le tableau de comparaison ci-dessus, recommandez la Stratégie A par défaut, et attendez une approbation explicite. Ne prenez PAS la décision vous-même et ne continuez pas tant que l'utilisateur n'a pas confirmé.
Stratégie A : Instantané Puppeteer (Recommandée)
Lance Chrome headless, capture le DOM entièrement rendu, et produit un fichier HTML autonome avec toutes les CSS intégrées et les images converties en base64. Fonctionne avec n'importe quel framework — pas besoin de MockPage.jsx.
Prérequis
- Application tournant localement (ex:
npm run dev) - Node.js avec
puppeteerdisponible (vérifiez:node -e "require('puppeteer')")
Flux de travail
-
Démarrez l'Application et notez le port.
[!WARNING] Point de contrôle — Confirmation de l'utilisateur requise. Après avoir démarré le serveur local, vous DEVEZ faire une pause et demander à l'utilisateur une confirmation avant d'exécuter le script d'instantané ou de lancer un sous-agent navigateur. Signalez l'URL et le port à l'utilisateur afin qu'il puisse vérifier que l'application fonctionne et s'affiche correctement. Ne PAS passer à l'étape de l'instantané tant que l'utilisateur n'a pas confirmé.
-
Exécutez le Script d'Instantané :
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 \ --output .stitch/home.html \ --wait 2000 -
Plusieurs pages — exécutez une fois par route :
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 -
Nettoyez le Serveur de Développement : Si un serveur de développement local a été démarré spécifiquement pour l'extraction d'instantanés, assurez-vous d'arrêter le processus serveur ou de terminer la tâche en arrière-plan une fois l'extraction terminée.
Options du Script
| Option | Défaut | Description |
|---|---|---|
--url | (requis) | URL à capturer |
--output | (requis) | Chemin du fichier de sortie |
--wait | 1000 | Attente supplémentaire (ms) après l'inactivité réseau. Augmentez pour les applications à chargement paresseux. |
--viewport | 1280x800 | Taille de la zone d'affichage au format LARGEURxHAUTEUR |
--html-class | — | Classe(s) pour l'élément <html> (ex: dark) |
--remove-fixed | false | Supprimer les éléments fixes/sticky (bannières de cookies, widgets de chat) |
--full-height | false | Redimensionner la zone d'affichage à la hauteur totale du défilement |
--title | — | Remplacer le titre de la page (défini sur le chemin de la route, ex. /dashboard ou /settings/profile) |
--auth-script | — | Chemin vers un module JS/TS qui exporte une fonction async (page) => void par défaut pour l'authentification |
--inline-canvas | false | Convertir les éléments <canvas> (ECharts, Chart.js, D3) en balises <img> base64 |
Ce qu'il Fait Automatiquement
- Capture toutes les règles CSSOM à partir de
document.styleSheets(préserve les styles dynamiques Vite/Tailwind de développement et CSS-in-JS) - Intègre toutes les
<link rel="stylesheet">→ blocs<style> - Convertit les
srcetsrcsetdes<img>→ URI de données base64 (ignore les polices externes) - Intègre les fichiers de polices d'icônes de même origine et relatifs (
@font-face) sous forme d'URI de données base64 afin que les ligatures ne s'affichent jamais sous forme de texte ASCII - Intègre les URL
<source srcset>en base64 - Supprime les entrées
srcsetdéfaillantes/mortes afin que le navigateur utilise lesrcintégré - Supprime les balises
<script>, les blocs de style de développement Vite HMR (createHotContext,import.meta.hot) et les superpositions de développement - Résout les chemins relatifs CSS
url()avant l'intégration
Notes par Framework
| Framework | Notes |
|---|---|
| React + Vite | Fonctionne immédiatement. --wait 1000. |
| Next.js | --wait 3000 pour l'hydratation SSR. URL : http://localhost:3000. Les <img srcset> de /_next/image sont automatiquement intégrés en base64. |
| Angular (@angular/cli / v17+) | Fonctionne immédiatement avec ng serve (URL par défaut : http://localhost:4200). --wait 2000 pour l'hydratation des animations Angular Material / PrimeNG et les routes chargées paresseusement. |
| Vue / Nuxt | Fonctionne immédiatement. |
| Svelte / SvelteKit | Fonctionne immédiatement. |
| Storybook | Utilisez l'URL de l'histoire : --url http://localhost:6006/?path=/story/... |
| SSR (Webpack) | Peut nécessiter un --wait plus long. |
Dépannage
| Problème | Solution |
|---|---|
| Images manquantes | Augmentez --wait |
| Images affichées comme cassées après l'arrêt du serveur | Vérifiez que srcset a été intégré — consultez le journal pour « Inlined N images ». Si les URL srcset ont échoué, elles sont automatiquement supprimées afin que src (intégré) soit utilisé. |
| Les icônes s'affichent sous forme de texte / police Serif sans style | Assurez-vous que snapshot.ts capture CSSOM à partir de document.styleSheets (étape 0) et que les polices d'icônes de même origine (@font-face) sont intégrées sous forme d'URI de données base64. |
/_next/image de Next.js non intégré | Assurez-vous que le serveur de développement est en cours d'exécution lorsque l'instantané est pris — le script récupère les images optimisées depuis le serveur en cours d'exécution. |
| Mode sombre non appliqué | --html-class dark |
| Bannière de cookies dans la sortie | --remove-fixed |
| La page nécessite une connexion | Utilisez --auth-script ./auth.ts (voir Pages Authentifiées ci-dessous) |
| Graphiques affichés sous forme de boîtes vides | Utilisez --inline-canvas pour sérialiser <canvas> en <img> base64 |
Cannot find module 'puppeteer' | npm install -g puppeteer |
Pages Authentifiées
Pour les applications avec des gardes de connexion (Vue Router beforeEach, React ProtectedRoute, etc.), créez un petit script d'authentification qui s'exécute dans la session Puppeteer :
// auth-myapp.ts
import type { Page } from 'puppeteer';
export default async function authenticate(page: Page) {
// Exemple 1 : Remplir et soumettre un formulaire de connexion
await page.type('#username', 'admin');
await page.type('#password', 'password123');
await page.click('#login-button');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
// Exemple 2 : Injecter des cookies/localStorage directement
// await page.evaluate(() => {
// localStorage.setItem('token', 'mock-jwt-token');
// });
// Exemple 3 : Appeler l'API de connexion de l'application via l'injection de module (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 });
// });
// });
}
Utilisez-le ensuite :
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
Le script navigue d'abord vers l'--url (qui peut rediriger vers la page de connexion), exécute votre fonction d'authentification, puis re-navigue vers l'--url d'origine avec la session authentifiée.
Stratégie B : Capture par Sous-agent Navigateur
À utiliser lorsque vous devez interagir avec la page (cliquer sur des boutons, remplir des formulaires, naviguer entre les onglets) avant la capture. Le sous-agent navigateur vous donne un contrôle total, mais la sortie peut être tronquée pour les grandes pages.
Flux de travail
-
Démarrez l'Application localement.
-
Naviguez à l'aide d'un sous-agent navigateur.
-
Interagissez si nécessaire (cliquez, faites défiler, remplissez des formulaires).
-
Extrayez le DOM :
document.documentElement.outerHTML[!WARNING] Les grandes pages peuvent être tronquées. Pour gérer cela :
- Supprimez les balises
<style>avant l'extraction :document.querySelectorAll('style').forEach(el => el.remove()) - Ré-ajoutez les styles statiquement (lien CDN Tailwind, CSS source)
- Supprimez les balises
-
Sauvegardez dans un fichier.
Annexe : Solution de Secours Statique (MockPage.jsx)
[!NOTE] Cette méthode est un dernier recours pour les cas où l'application ne peut pas être exécutée localement (dépendances cassées, backend manquant, murs d'authentification sans contournement). Elle nécessite d'aplatir manuellement les composants React en un seul fichier JSX. Privilégiez la Stratégie A autant que possible.
Quand l'Utiliser
- L'application ne peut pas du tout être exécutée localement
- La page nécessite une authentification sans simulation ni contournement
- Vous avez besoin d'un état d'interface spécifique impossible à atteindre par la navigation (écrans d'erreur, états vides)
Référence Rapide
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:"Titre de la Page"
Options clés : --no-tailwind (applications non Tailwind), --html-class dark (mode sombre), --css-files (fichiers CSS supplémentaires).
Détection automatique : La configuration Tailwind est détectée automatiquement. Les directives @apply utilisent automatiquement <style type="text/tailwindcss">.
Règles MockPage.jsx
- Incluez la mise en page complète — en-tête, barre latérale, pied de page (lisez
App.jsd'abord) - Aplatissez toutes les conditionnelles — choisissez un état, supprimez toutes les ternaires et les gardes
&& - Codez en dur toutes les données — remplacez
{variable}par des valeurs concrètes, déroulez les boucles.map() - Conservez les logos — utilisez
<img>avec des chemins locaux (le post-traitement les intégrera) - Supprimez les éléments flottants — bannières de cookies, widgets de chat, boutons de retour d'information
Post-traitement
Intégrez les images locales :
npx tsx <SKILL_DIR>/scripts/post_process.ts \
.stitch/Page.html --base-dir <app-directory>