Communitygithub.com

wiggle-that-diagram

Use quando alguém pedir para animar uma ilustração em uma apresentação de slides HTML ou relatório HTML, mostrar como um mecanismo, fluxo de trabalho ou fluxo de dados funciona passo a passo, fazer uma animação explicativa para um slide, desacelerar uma animação porque está muito rápida para ler, adicionar legendas a uma ou oferecer três estilos de animação para escolher. Constrói um arquivo autossuficiente, com legenda e looping, por mecanismo, que cabe em um iframe srcdoc (trilha de passos, movimento reduzido, encaminhamento de entrada seguro para deck), em três formatos: A diagrama clip (engine huashu, vídeo sem texto + rótulos HTML), B quadro branco (huashu y3), C SVG ao vivo + GSAP com câmera virtual.

O que é wiggle-that-diagram?

Este skill, 'wiggle-that-diagram', transforma uma ilustração estática do tipo 'como funciona' em uma animação curta, com legenda e looping, projetada para ser incorporada em decks HTML ou relatórios. Ele oferece três estilos de animação distintos (diagrama limpo, quadro branco e SVG ao vivo com GSAP), permitindo que o usuário escolha o formato que melhor se adapta ao seu conteúdo e público. O skill é particularmente útil para explicar mecanismos, fluxos de trabalho ou dados de forma visual e passo a passo, abordando a reclamação comum de que animações são 'muito rápidas para ler'. Ele garante que todas as animações em diferentes estilos compartilhem o mesmo script de legenda e contrato de incorporação, facilitando a alternância entre estilos. O skill suporta a criação de arquivos HTML autossuficientes, otimizados para incorporação em iframes com srcdoc, e inclui verificações de ritmo, testes de contrato e integração com decks de slides para garantir uma experiência polida e consistente.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/wan-huiyan/wiggle-that-diagram/tree/HEAD/plugins/wiggle-that-diagram/skills/wiggle-that-diagram

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

wiggle-that-diagram: movimento explicativo em três estilos

Transforme uma ilustração estática "como funciona" em uma animação curta, legendada e em loop que cabe dentro de um slide HTML ou relatório, e ofereça-a em três formatos para que o leitor possa escolher. O método foi desenvolvido em um deck de cliente real (consulte "Exemplo prático"). Ele usa huashu-art-motion para direção (listas de cenas, regras de câmera, as seis maneiras de falhar) e, para os estilos A e B, como mecanismo de renderização.

Quando usar e quando não usar

Use quando:

  • um deck ou relatório HTML tiver um diagrama de mecanismo (fluxo de arquivos, verificação de regras, uma pessoa aprova) e o leitor dever ver o que acontece, passo a passo, com palavras que possa ler;
  • o usuário quiser opções em formatos genuinamente diferentes, não três esquemas de cores de um;
  • uma animação anterior estava "muito rápida para ler" ou precisa de legendas.

Não use para: uma única microinteração (use animate), um vídeo narrado com voz over (use huashu-art-motion diretamente) ou um gráfico de números (use dataviz).

Os três estilos (diferem no formato, não na paleta)

A · clipe de diagramaB · quadro brancoC · SVG ao vivo
ImagemCena do canvas da engine huashu, gramática t1_3b1b, renderizada para um H.264 sem textoGramática huashu y3_whiteboard, quadro escuro, caneta desenha e letras à mão (Kalam), vídeo quadradoMundo SVG inline filmado por uma câmera virtual (viewBox animado por GSAP 3.12.5)
Palavras na imagemRótulos HTML colocados sobre o vídeo a partir de uma trilha de câmera descartada (sempre nítidos)Desenhados no vídeo (devem ser grandes o suficiente para um telefone)Texto SVG real (nítido, reflui alterando os quadros da câmera por largura)
Tamanho por filme (exemplo prático)360–820 KB (orçamento 900 KB)847–886 KB (orçamento 900 KB)21–34 KB (orçamento 40 KB)
Sensaçãoum diagrama limpo estilo 3Blue1Brownalguém explicando em um quadro brancouma animação de produto polida
Principal riscorótulos se desviam se a cena muda sem re-renderizarorçamento de tamanho apertado; rótulos de telefone 12–14 pxmatemática de câmera complicada; peculiaridades de transformação do GSAP

Todos os três compartilham UM script de legenda, as mesmas batidas e o mesmo contrato de incorporação, para que um seletor de estilo mostre a mesma história contada de três maneiras.

O método, do início ao fim

Cada etapa termina com a verificação que prova que está concluída.

  1. Encontre o mecanismo e um exemplo concreto. Leia a página do host (somente leitura). Escolha o momento a mostrar e UM exemplo real que percorre todas as batidas (uma linha, um registro, um caso, com seus valores reais). Liste o que não deve ser inventado (ids, figuras, imagens). → Verifique: você pode dizer cada batida como "este objeto faz isto", com um substantivo e um verbo.

  2. Escreva o script de legenda primeiro. Uma frase de legenda por batida, 3–6 batidas por filme, em beats.json (formato: scripts/common/beats.example.json). As legendas são idênticas em todos os estilos. Em seguida, execute a verificação de ritmo e leia o piso que ela imprime:

    python3 ~/.claude/skills/wiggle-that-diagram/scripts/common/pace_check.py beats.json
    

    → Verifique: toda batida OK, e o usuário concordou com os comprimentos de loop resultantes (ou legendas mais curtas). Nunca defina um alvo de loop separadamente das legendas; consulte "Ritmo".

  3. Escreva o briefing. Preencha references/brief-template.md em SPEC.md: feedback citado, tokens, regra de ritmo, script de legenda, batidas, contrato de incorporação com o trecho literal, orçamentos, entregáveis. → Verifique: um agente sem nenhum outro contexto poderia construir a partir dele.

  4. Rodada de direções (opcional, quando o usuário não viu os estilos). Três agentes em paralelo, um por estilo, cada um prototipando o MESMO momento de um filme (~8–12 s) e capturando imagens estáticas em 1280 e 390 px. Mostre as imagens lado a lado; o usuário escolhe uma ou mantém as três. → Verifique: a escolha do usuário está por escrito.

  5. Rodada de construção. Um agente por estilo (cada um continuando seu próprio protótipo), todos os filmes nesse estilo, escrevendo apenas em <work>/<style>/final/. Briefings: references/agent-briefs.md. Diga a contagem de agentes e o nível ao usuário antes de lançar. → Verifique: cada um retorna arquivos, imagens estáticas, NOTES.md e uma lista honesta de problemas em aberto.

  6. Receitas de construção (o que cada agente de estilo realmente fez; os scripts estão em scripts/style-a|b|c/): Dois filmes neutros são enviados em todos os três estilos, cada um como beats.json mais style-a/, style-b/, style-c/: examples/pizza/ (5 batidas, simples) e examples/expenses/ (6 batidas: cartões, motivos codificados por cores, setas para um segundo e terceiro painéis). Comece pela pasta do seu estilo.

    • A. cp -R ~/.claude/skills/huashu-art-motion/scripts/engine <A>/engine e touch <A>/engine/favicon.ico. Adicione engine/ways/way_make.js (de scripts/style-a/), execute beats_js.py beats.json --engine engine --var <VAR> (legendas e temporização do script compartilhado, nunca uma cópia manual), um arquivo de cena engine/ways/<film>.js que chama WAY.make({poster, beats, camBase, cam, labels}) e registra SCENES['<film>'], e engine/eras_<film>.js definindo FILM_SIZE=[1280,720], PUNCH=0 (caso contrário, a engine adiciona um zoom de 0.03 nas transições), SCENE_LIBS=['ways/way_make.js','ways/<film>_beats.js','ways/<film>.js'], SCENE_DIR='ways', ERAS=[{id:'<film>',dur:1}]. Não desenhe NENHUM texto no canvas. Os auxiliares de desenho (caixas arredondadas, pontos, linhas, fades, movimentos ao longo de um caminho) são escritos por filme; examples/pizza/style-a/pizza.js tem um pequeno conjunto para copiar. Em seguida, render_way.py <film> --scale 0.75 --crf 30 → build.py <film> --meta meta.json → pace_check.py work/<film>_data.json → test_embed.py final/<film>.html:<verdict beat> (ou tudo de uma vez: make.sh). Após qualquer edição de cena, re-renderize e execute midmove.py nos tempos de meio movimento: os rótulos devem ficar sobre seus objetos.
    • B. Copie a engine da mesma forma (com o favicon.ico vazio: no Chrome real, seu 404 conta como um erro de página e render.py sai com 1 após escrever um bom vídeo) e corrija render.py para chromium.launch(channel='chrome', …). Coloque kit.js em engine/demos/_hb/, escreva engine/demos/<film>/board.js (usa HB.make(), ancora eventos a BEATS[k].t0 + offset) e engine/demos/<film>/eras.js (FILM_SIZE=[1080,1080], PUNCH=0, face da fonte Kalam, SCENE_LIBS=['demos/<film>/beats.js','demos/_hb/kit.js','demos/<film>/board.js']). Em seguida, build_beats.py beats.json --engine engine → uv run --with playwright python engine/render.py --film demos/<film> --fps 30 --crf 12 --out work/<film>_master.mp4 → build_html.py <film> --beats beats.json (720², 24 fps, CRF mais baixo que caiba) → test_contract.py final/<film>.html --beats beats.json. Cada filme termina com um afastamento para todo o quadro, uma pausa de ≥ 1 s, depois uma limpeza com borracha; o quadro vazio é onde o loop recomeça. O still da última batida em beats.json é compartilhado por todos os estilos e deve cair antes do início da limpeza, ou o pôster e o quadro de movimento reduzido mostram um quadro meio apagado. Objetos que se movem (uma scooter, uma caixa) são itens K.custom com handed: false cuja posição é uma função do tempo; dê a cada um um tempo final, ou ele permanece no quadro após seu trabalho ser concluído.
    • C. Comece de examples/pizza/style-c/ (SVG, timeline, tema). Escreva src/<film>.svg (uma <svg id="scene" class="scene"> contendo <g id="world">; marque cada <text> ou seu grupo com data-b="<beat>", baseado em 1, definido para a batida em que a câmera MOSTRA pela primeira vez) e src/<film>.js (quadros da câmera FR / ORDER, cada quadro com um retângulo w para telas largas e um retângulo t para telas estreitas; revelações com R(selector, beat, offset), movimentos com camTo(i, beat, offset), desenhos de linha com draw()). No JS, beat é baseado em 0 (B[0] é a batida 1). R() é apenas para palavras, com offset ≥ 2.5 s: cada chamada de R() é registrada como uma revelação de palavra e a legenda conta como palavras no offset 0, portanto R() em uma forma sem palavras ou no offset 0 falha na verificação de espaçamento. Formas, movimento e palavras que chegam com a legenda usam tl.to(…, T(beat, offset)) simples. Envolva qualquer elemento que tenha seu próprio transform em um <g> pai e anime o filho. Então build.py beats.json --src src --out final [--css theme.css] → test_contract.py final/<film>.html --stills <scratch>/stills → widthcheck.py. O runtime (shell.html, shell.css, head.js, tail.js) é reutilizado como está; para corresponder à página do host, coloque novos tokens :root (--panel, --ink, --muted, --line, --gold, --cap-ink, --on-gold, …) em um arquivo de tema e passe-o com --css. Para uma imagem README ou chat de um filme: gif.py final/<film>.html --beat 4 --from 1.9 --to 8.6 --out film.gif (ou --still <s> para um PNG).
  7. Verifique antes de mostrar a alguém (todas as três construções pularam as três últimas; faça-as):

    • verificação de ritmo OK; todos os testes de contrato passam em 1100 e 360 px no Chrome real; tamanhos dentro do orçamento;
    • olhe as folhas de contato de cada batida em ambas as larguras (scripts/common/contact_sheet.py);
    • huashu film_gate.py nos mp4s A/B (com o shim ffprobe, se necessário), vermelho lido conforme explicado em traps.md;
    • um revisor independente que não construiu assiste a cada loop em velocidade normal e escreve as descobertas;
    • teste dentro da página do host real (lado pai de references/embed-contract.md), em um telefone real e no Safari (fallback do estilo A/B: pôster + legendas numeradas quando o H.264 não pode reproduzir).
  8. Entrega. Mostre os três estilos lado a lado (imagens estáticas ou uma página de seletor), com os comprimentos do loop, tamanhos, o que foi inventado na imagem (para o usuário confirmar) e o que não foi verificado.

Lado do host: todos os nove em um slide com um seletor (scripts/deck/)

O que o deck de exemplo faz e o que quebrou no caminho:

  1. Incorporar, não vincular. Cada filme vai para o deck como <script type="text/plain" id="anim-{style}-{way}"> com </script → <\/script e <!-- → <\!-- escapados; o JS o decodifica em iframe.srcdoc apenas quando o slide é mostrado (um iframe, construído sob demanda). embed_anims.py faz isso de forma idempotente entre os marcadores <!--ANIM-DATA-START--> / <!--ANIM-DATA-END-->, então reexecutar após uma reconstrução substitui apenas os dados. O deck cresceu de ~0.3 MB para 4.5 MB.
  2. Um slide, dois grupos de botões segmentados (Caminho, Estilo; aria-pressed), mais um link "Veja como funciona" dentro de cada painel de detalhes do Caminho que define o caminho, fecha o painel e salta para o slide.
  3. Play/pause segue o slide: go() despacha um evento deck:slide; o alternador posta anim-play quando seu slide se torna atual (e no carregamento do iframe, se atual), anim-pause caso contrário.
  4. Gestos sobre o iframe: os manipuladores de wheel/tecla/swipe do deck foram refatorados em onWheel(dy,mode), onKey(key), onSwipe(dy,dx), chamados tanto pelos próprios listeners do deck quanto pelas mensagens encaminhadas do iframe (aceitar apenas e.source === frame.contentWindow). howtest2.py prova que um gesto = um slide.
  5. Ajuste a uma tela de laptop: estreite o layout interno primeiro, zoom por último. Em 1280×800 os filmes tinham 740–840 px de altura na largura total, então a legenda caía abaixo da tela. O que funciona (howFit em embed_anims.py): o iframe srcdoc é de mesma origem, então o deck mede contentDocument.body altura por conta própria. Ele tenta larguras internas da largura da caixa para baixo até 740 px (busca binária, esperando ~90 ms por largura porque um filme pode definir seu layout em JS no redimensionamento, quadro oculto enquanto isso) e mantém a mais larga que couber; somente se 740 px ainda for muito alto, ele define frame.style.zoom. Estreitar vence o zoom porque a legenda mantém seu tamanho enquanto a imagem encolhe. Armadilhas encontradas no caminho: (a) zoom com width:100% alarga o layout interno (1126 → 1876 px) e faz um loop até o piso, então sempre fixe frame.style.width em px; (b) documentElement.scrollHeight nunca relata menos do que a altura atual do iframe, então não pode conduzir o encolhimento (meça body em vez disso, com o quadro tornado alto); (c) medir no mesmo tick que a mudança de largura lê um layout JS obsoleto. Peça aos filmes um layout lado a lado quando largo (imagens quadradas) e ≤ 580 px de altura total a 780 px de largura. Telefones (< 900 px) não cabem; o slide rola, com um cabeçalho compacto.
  6. Testes: howtest.py (alternância, link profundo, setas), howtest2.py (todos os 9 carregam e reproduzem no Chrome real, wheel/tecla/swipe sobre o iframe movem exatamente um slide), além das próprias verificações de um gesto um slide do deck. Qualquer teste de deck que codifique números de slide de forma fixa quebra quando o slide é inserido: procure slides por id.

Ritmo (a principal reclamação do usuário sobre o primeiro corte)

Os primeiros protótipos tinham 8–13 s. O veredito do leitor: muito rápido para ler o que estava acontecendo, e precisava de legendas. A regra usada para a reconstrução:

  • cada batida dura ≥ max(4,5 s, (palavras da legenda + novas palavras da imagem) / 3 + 1,5 s). A contagem canônica é scripts/common/pace_check.py (e o build.py do estilo C, que conta da mesma forma a partir do SVG). O próprio pace_check.py do estilo A verifica os tempos reais dos rótulos do filme construído e difere em uma coisa: uma palavra repetida por um novo rótulo em uma batida posterior conta novamente lá;
  • algo novo aparece apenas ≥ 2,5 s após as palavras anteriores; a câmera fica parada enquanto as palavras são lidas;
  • movimentos lentos da câmera entre as batidas (1,4–2,0 s, easing senoidal), no máximo UM acento rápido por filme, no veredito;
  • ≥ 1 s de pausa no último quadro antes do loop.

Consequência a dizer ao usuário antecipadamente: legendas de ~20 palavras por batida fazem filmes de 5–6 batidas de 40–80 s. No exemplo prático, os alvos de 18–34 s do briefing eram impossíveis (as legendas sozinhas precisavam de 31,5–51,7 s); todos os três agentes escolheram a regra de leitura e construíram loops de 35–82 s, o que ninguém havia confirmado com o usuário. Loops mais curtos significam legendas mais curtas, alteradas uma vez em beats.json para todos os estilos.

Contrato de incorporação, tamanho e telefones (resumo; texto completo em references/embed-contract.md)

  • Um arquivo HTML autossuficiente por filme, carregado pelo host em <iframe srcdoc>. Imagem + faixa de legenda + trilha de passos + Play/Pause, nada mais. Mídia embutida em base64;

Habilidades Relacionadas