Extrair HTML Estático
Extraia um arquivo HTML estático autocontido de qualquer aplicação web.
Qual Estratégia Usar
Você DEVE perguntar ao usuário qual estratégia deseja usar antes de prosseguir. Apresente as opções claramente, recomende a Estratégia A como padrão preferencial e forneça um breve resumo de prós/contras para cada opção para ajudá-lo a tomar uma decisão informada.
| Estratégia A (Puppeteer) | Estratégia B (Subagente de Navegador) | |
|---|---|---|
| Quando usar | A aplicação roda localmente, sem barreira de autenticação | Precisa interagir com a página primeiro (clicar, preencher formulários) |
| Fidelidade | Máxima — estilos computados resolvidos | Alta — DOM renderizado |
| Configuração | Zero — nenhum mock necessário | Zero — nenhum mock necessário |
| Framework | Qualquer | Qualquer |
| Saída | Grava em arquivo — sem limite de tamanho | Pode truncar no contexto do agente |
[!WARNING] Ponto de Verificação — Confirmação do Usuário Necessária. Você DEVE perguntar ao usuário qual estratégia ele prefere antes de prosseguir. Apresente a tabela de comparação acima, recomende a Estratégia A como padrão e aguarde aprovação explícita. NÃO tome a decisão por conta própria nem prossiga até que o usuário confirme.
Estratégia A: Instantâneo Puppeteer (Recomendado)
Inicia o Chrome headless, captura o DOM totalmente renderizado e produz um arquivo HTML autocontido com todo o CSS inline e imagens como base64. Funciona com qualquer framework — não é necessário MockPage.jsx.
Pré-requisitos
- Aplicação rodando localmente (ex.:
npm run dev) - Node.js com
puppeteerdisponível (verifique:node -e "require('puppeteer')")
Fluxo de Trabalho
-
Inicie a Aplicação e anote a porta.
[!WARNING] Ponto de Verificação — Confirmação do Usuário Necessária. Após iniciar o servidor local, você DEVE pausar e pedir confirmação ao usuário antes de executar o script de instantâneo ou iniciar um subagente de navegador. Informe a URL e a porta ao usuário para que ele possa verificar se a aplicação está rodando e renderizando corretamente. NÃO prossiga para a etapa de instantâneo até que o usuário confirme.
-
Execute o Script de Instantâneo:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 \ --output .stitch/home.html \ --wait 2000 -
Múltiplas páginas — execute uma vez por rota:
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 -
Limpe o Servidor de Desenvolvimento: Se um servidor de desenvolvimento local foi iniciado especificamente para a extração do instantâneo, certifique-se de parar o processo do servidor ou encerrar a tarefa em segundo plano assim que a extração for concluída.
Flags do Script
| Flag | Padrão | Descrição |
|---|---|---|
--url | (obrigatório) | URL a ser capturada |
--output | (obrigatório) | Caminho do arquivo de saída |
--wait | 1000 | Espera extra (ms) após rede ociosa. Aumente para aplicações com carregamento preguiçoso. |
--viewport | 1280x800 | Tamanho da janela de visualização no formato LARGURAxALTURA |
--html-class | — | Classe(s) para o elemento <html> (ex.: dark) |
--remove-fixed | false | Remove elementos fixed/sticky (banners de cookies, widgets de chat) |
--full-height | false | Redimensiona a janela de visualização para a altura total da rolagem |
--title | — | Substitui o título da página (definir como o caminho da rota, ex.: /dashboard ou /settings/profile) |
--auth-script | — | Caminho para um módulo JS/TS que exporta uma função async (page) => void padrão para autenticação |
--inline-canvas | false | Converte elementos <canvas> (ECharts, Chart.js, D3) em tags <img> base64 |
O Que Ele Faz Automaticamente
- Captura todas as regras CSSOM de
document.styleSheets(preserva estilos dinâmicos de desenvolvimento Vite/Tailwind e CSS-in-JS) - Embuti todos os
<link rel="stylesheet">→ blocos<style> - Converte
srcde<img>esrcset→ URIs de dados base64 (ignora fontes externas) - Embuti arquivos de fontes de ícone de mesma origem e relativos (
@font-face) como URIs de dados base64 para que ligaduras nunca sejam renderizadas como texto ASCII - Embuti URLs de
<source srcset>como base64 - Remove entradas
srcsetcom falha/mortas para que o navegador use osrcembutido como fallback - Remove tags
<script>, blocos de estilo de desenvolvimento Vite HMR (createHotContext,import.meta.hot) e sobreposições de desenvolvimento - Resolve caminhos relativos de CSS
url()antes de embutir
Notas por Framework
| Framework | Notas |
|---|---|
| React + Vite | Funciona imediatamente. --wait 1000. |
| Next.js | --wait 3000 para hidratação SSR. URL: http://localhost:3000. <img srcset> de /_next/image é automaticamente embutido como base64. |
| Angular (@angular/cli / v17+) | Funciona imediatamente com ng serve (URL padrão: http://localhost:4200). --wait 2000 para hidratação de animação Angular Material / PrimeNG e rotas com carregamento preguiçoso. |
| Vue / Nuxt | Funciona imediatamente. |
| Svelte / SvelteKit | Funciona imediatamente. |
| Storybook | Use a URL da história: --url http://localhost:6006/?path=/story/... |
| SSR (Webpack) | Pode precisar de --wait maior. |
Solução de Problemas
| Problema | Solução |
|---|---|
| Imagens ausentes | Aumente --wait |
| Imagens quebradas após o servidor parar | Verifique se srcset foi embutido — veja o log para "N imagens embutidas". Se as URLs srcset falharam, elas são removidas automaticamente para que o src (embutido) seja usado. |
| Ícones exibidos como texto / Fonte serifada sem estilo | Certifique-se de que snapshot.ts captura o CSSOM de document.styleSheets (etapa 0) e que as fontes de ícone de mesma origem (@font-face) são embutidas como URIs de dados base64. |
/_next/image do Next.js não embutido | Certifique-se de que o servidor de desenvolvimento está rodando quando o instantâneo é executado — o script busca as imagens otimizadas do servidor em execução. |
| Modo escuro não aplicado | --html-class dark |
| Banner de cookies na saída | --remove-fixed |
| Página requer login | Use --auth-script ./auth.ts (veja Páginas com Autenticação abaixo) |
| Gráficos mostrados como caixas vazias | Use --inline-canvas para serializar <canvas> em <img> base64 |
Cannot find module 'puppeteer' | npm install -g puppeteer |
Páginas com Autenticação
Para aplicações com proteções de login (Vue Router beforeEach, React ProtectedRoute, etc.), crie um pequeno script de autenticação que roda na sessão do Puppeteer:
// auth-myapp.ts
import type { Page } from 'puppeteer';
export default async function authenticate(page: Page) {
// Exemplo 1: Preencher e enviar um formulário de login
await page.type('#username', 'admin');
await page.type('#password', 'password123');
await page.click('#login-button');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
// Exemplo 2: Injetar cookies/localStorage diretamente
// await page.evaluate(() => {
// localStorage.setItem('token', 'mock-jwt-token');
// });
// Exemplo 3: Chamar a API de login da própria aplicação via injeção de módulo (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 });
// });
// });
}
Depois use:
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
O script navega para a --url primeiro (que pode redirecionar para o login), executa sua função de autenticação e então re-navega para a --url original com a sessão autenticada.
Estratégia B: Captura com Subagente de Navegador
Use quando precisar interagir com a página (clicar em botões, preencher formulários, navegar por abas) antes de capturar. O subagente de navegador oferece controle total, mas a saída pode truncar para páginas grandes.
Fluxo de Trabalho
-
Inicie a Aplicação localmente.
-
Navegue usando um subagente de navegador.
-
Interaja conforme necessário (clicar, rolar, preencher formulários).
-
Extraia o DOM:
document.documentElement.outerHTML[!WARNING] Páginas grandes podem truncar. Para lidar com isso:
- Remova tags
<style>antes da extração:document.querySelectorAll('style').forEach(el => el.remove()) - Re-adicione estilos estaticamente (link CDN do Tailwind, CSS fonte)
- Remova tags
-
Salve em arquivo.
Apêndice: Fallback Estático (MockPage.jsx)
[!NOTE] Este método é um último recurso para quando a aplicação não pode ser executada localmente (dependências quebradas, backend ausente, barreiras de autenticação sem solução alternativa). Requer achatar manualmente componentes React em um único arquivo JSX. Prefira a Estratégia A sempre que possível.
Quando Usar
- A aplicação não pode ser executada localmente
- A página requer autenticação sem mock/solução alternativa
- Você precisa de um estado específico da interface que é impossível de alcançar por navegação (telas de erro, estados vazios)
Referência Rápida
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:"Título da Página"
Flags principais: --no-tailwind (aplicações sem Tailwind), --html-class dark (modo escuro), --css-files (arquivos CSS extras).
Detecção automática: A configuração do Tailwind é detectada automaticamente. Diretivas @apply usam automaticamente <style type="text/tailwindcss">.
Regras do MockPage.jsx
- Inclua o layout completo — cabeçalho, barra lateral, rodapé (leia
App.jsprimeiro) - Achate todos os condicionais — escolha um estado, remova todas as ternárias e guardas
&& - Codifique todos os dados — substitua
{variável}por valores concretos, desenrole loops.map() - Preserve logotipos — use
<img>com caminhos locais (o pós-processamento os embutirá) - Remova elementos flutuantes — banners de cookies, widgets de chat, botões de feedback
Pós-Processamento
Embutir imagens locais:
npx tsx <SKILL_DIR>/scripts/post_process.ts \
.stitch/Page.html --base-dir <diretório-da-aplicação>