| name | mira-survey |
| description | Cria um slide de ENQUETE AO VIVO: a plateia escaneia um QR-code, vota num Google Forms, e o resultado se atualiza em TEMPO REAL no slide (donut 3D girando ou barras). Recebe dois links — o de votação (vira o QR gerado local) e o da planilha de respostas (o slide lê a contagem via endpoint gviz/JSONP). Use SEMPRE que o usuário disser /mira-survey, enquete, enquete ao vivo, votação em tempo real, poll, resultado ao vivo no slide, QR de enquete, a plateia vota e o slide atualiza, tipo Mentimeter, tipo Slido, ou pedir um slide que mostra votação em tempo real. Para um QR sem votação use /mira-qrcode; para um gráfico estático use /mira-chart. |
Skill: Enquete ao vivo no slide (QR + resultado em tempo real)
Cria um slide onde a plateia escaneia um QR-code, vota num Google Forms, e o resultado aparece se atualizando ao vivo no slide (donut 3D girando ou barras). Casos típicos: pergunta ao público numa palestra, termômetro de opinião, quiz de abertura.
Fonte da verdade: layout e técnica validados e congelados em decks/teste-survey/index.html. Os tamanhos e posições abaixo são EXATOS; em dúvida sobre uma medida, copie do artefato.
O modelo mental
O Mira não é dono da votação. Ele precisa só de uma fonte de dados que devolve a contagem atual e muda com o tempo. Essa fonte é uma planilha do Google ligada a um Google Forms:
- A plateia abre o link de votação (Google Forms) pelo QR e responde.
- Cada resposta vira uma linha na planilha de respostas.
- O slide lê a planilha a cada poucos segundos e redesenha o gráfico.
Então a skill recebe dois links e faz duas coisas com eles:
| Link | De onde vem | Para que serve no slide |
|---|
| Votação | forms.gle/... ou docs.google.com/forms/... | vira o QR-code (gerado localmente como SVG inline) |
| Planilha | docs.google.com/spreadsheets/d/<ID>/... | dela sai o SHEET_ID, que alimenta o gráfico ao vivo |
Passo 0: ter os dois links (se faltar, PEÇA)
Antes de gerar qualquer coisa, confirme que tem os dois links. Se faltar um ou os dois, pergunte e pare, não invente nem use placeholder. Texto sugerido quando faltar:
Para montar a enquete ao vivo eu preciso de dois links do seu Google Forms:
- O link de votação (o
forms.gle/... que a plateia abre para responder).
- O link da planilha de respostas (em Respostas → ícone do Sheets → a planilha; o endereço
docs.google.com/spreadsheets/...).
Pode colar os dois aqui?
A planilha precisa estar pública para leitura. Lembre o usuário: abrir a planilha → Compartilhar → "Qualquer pessoa com o link" → Leitor. Sem isso o slide não lê os votos.
Extraia o SHEET_ID do link da planilha com o padrão /spreadsheets/d/<ID>/. Ex.: de
https://docs.google.com/spreadsheets/d/1qAtv9OH2VXaHcieI5F4uI8ebYNUrzngbgSOPkcbMtNE/edit
o ID é 1qAtv9OH2VXaHcieI5F4uI8ebYNUrzngbgSOPkcbMtNE.
Passo 1: verificar a leitura antes de montar
Confirme que a planilha está pública e leia o estado atual buscando o endpoint gviz (é exatamente o que o slide vai usar). No terminal:
curl -sL "https://docs.google.com/spreadsheets/d/<SHEET_ID>/gviz/tq?tqx=out:json" | head -c 800
- Se vier
google.visualization.Query.setResponse({...}) com "status":"ok", está público e legível. O label da última coluna é a pergunta (vira o título do slide); os valores dessa coluna são as opções.
- Se vier HTML de login ou erro, a planilha não está pública: peça ao usuário para ajustar o compartilhamento e tente de novo.
Assuma uma pergunta de múltipla escolha (planilha com 2 colunas: carimbo de data/hora + resposta). O slide conta a última coluna. Se a planilha tiver várias perguntas, avise que o gráfico vai usar a última coluna e pergunte se é essa a desejada.
A armadilha
Nunca use o endpoint "Publicar na web → CSV" (/pub?output=csv) para ler os votos: ele é cacheado pelo Google por até ~5 minutos, e o "tempo real" viraria mentira (votos com minutos de atraso). Use só o gviz, que lê o estado vivo. A leitura é por JSONP (injeção de <script> com responseHandler), que fura o CORS e funciona com o deck aberto por file://.
Passo 2: gerar o QR-code localmente (jeito do /mira-qrcode)
O QR do link de votação é gerado localmente e embutido como SVG inline, sem API externa nem CDN. Receita (igual ao /mira-qrcode; não use npx qrcode, que trava no Windows):
- Instale o pacote uma vez numa pasta temp reaproveitável (pule se já existir):
npm install qrcode --no-save --prefix "<pasta-temp>"
- Gere o SVG (troque
LINK_VOTACAO pelo link de votação exato):
node -e "require('qrcode').toString('LINK_VOTACAO',{type:'svg',errorCorrectionLevel:'M',margin:0,color:{dark:'#0a0a0a',light:'#ffffff'}},(e,s)=>{if(e)throw e;process.stdout.write(s)})"
- Cole o
<svg> inteiro dentro do .qr-card (substituindo o SVG de exemplo do template). Mantenha o viewBox e o shape-rendering="crispEdges". O CSS (.qr-card svg{width:390px;height:390px}) controla o tamanho. Acrescente o comentário <!-- QR gerado localmente (pacote npm qrcode, ECC M) para LINK_VOTACAO -->.
A disposição (medidas EXATAS)
Layout em duas colunas (grid-template-columns: 1fr 520px), tema escuro, laranja da marca #FF904D:
- Esquerda (gráfico): a pergunta centralizada no topo (texto 42px) + linha "ao vivo" com bolinha pulsando; abaixo, o gráfico grande e centralizado.
- Donut 3D: wrapper de 562px, inclinado para trás (
rotateX(50deg)) com perspectiva, girando devagar (uma volta a cada 36s), com profundidade real (9 camadas SVG empilhadas em translateZ, as de baixo mais escuras = a parede lateral). O donut sobe 50px (translateY(-50px)). No centro, o total de votos (número 86px, parado). Abaixo do donut, a legenda centralizada (cor + opção + contagem + %).
- Direita (QR): card escuro arredondado, conteúdo subido (centralizado com
padding-bottom:200px, ~80px acima do centro): "Vote agora" (39px, "agora" em laranja), a instrução "Aponte a câmera do celular para o QR-code" (23px), e o QR num cartão branco de 390px. Nunca mostre o link de votação por extenso em lugar nenhum do slide, só o QR.
Não recalcule no olho; gere a partir do template abaixo.
Os dois tipos de gráfico
A skill gera um dos dois (pergunte ao usuário se ele não disser; o padrão é o donut):
- Donut 3D (padrão): o que está no template canônico abaixo.
- Barras: mesma esquerda (pergunta + ao vivo) e mesma direita (QR), trocando só o miolo do gráfico por barras horizontais animadas (ver "Variante: barras"). Use barras quando o usuário pedir, ou quando houver muitas opções (5+), onde o donut fica difícil de ler.
A camada de dados (leitura gviz + animação) é idêntica nos dois; muda só o desenho.
Template canônico (donut 3D): copie e preencha
O template completo (donut 3D, um arquivo HTML standalone) está em
references/survey-donut-template.html.
Copie-o para o index.html do deck. Os dois únicos pontos a preencher são: o
SHEET_ID no CONFIG e o <svg> do QR dentro do .qr-card (Passo 2). O título sai
sozinho do label da planilha.
Variante: gráfico de barras
Quando o usuário pedir barras, parta do mesmo template e faça SÓ estas trocas (a camada de dados não muda):
1. CSS: acrescente o bloco de barras (pode remover o CSS do donut .pie-wrap/.pie3d/.center-total/.legend, ou deixar, não atrapalha):
.bars{ width:100%; max-width:880px; display:flex; flex-direction:column; gap:30px; }
.bar-head{ display:flex; justify-content:space-between; align-items:baseline; margin-bottom:10px; }
.bar-name{ font-size:30px; font-weight:600; }
.bar-meta{ font-size:26px; color:var(--muted); font-variant-numeric:tabular-nums; }
.bar-meta b{ color:var(--ink); }
.bar-track{ height:34px; background:#22222b; border-radius:10px; overflow:hidden; }
.bar-fill{ height:100%; border-radius:10px; width:0; }
2. Markup: troque o miolo de .chartrow (o .pie-wrap + a .legend) por:
<div class="chartrow">
<div class="bars" id="bars"><div class="empty">Aguardando votos...</div></div>
</div>
3. JS: remova a IIFE buildLayers (não há camadas 3D) e troque a função draw do donut por esta draw de barras (a largura é relativa ao líder; o número e o % são sobre o total):
function draw(){
var opts = Object.keys(displayed).filter(function(o){ return (target[o]||0)>0 || displayed[o]>0.01; });
var box = document.getElementById("bars");
var ord = opts.slice().sort(function(a,b){ return (target[b]||0)-(target[a]||0); });
if(ord.length === 0){ box.innerHTML = '<div class="empty">Aguardando votos...</div>'; return; }
var totalReal = ord.reduce(function(s,o){ return s+(target[o]||0); },0) || 1;
var maxD = 0.0001; opts.forEach(function(o){ if(displayed[o]>maxD) maxD = displayed[o]; });
var html = "";
ord.forEach(function(o){
var n = target[o]||0, pct = Math.round(100*n/totalReal), w = Math.max(0, displayed[o]/maxD*100);
html += '<div class="bar"><div class="bar-head"><span class="bar-name">'+escapeHtml(o)+'</span>'+
'<span class="bar-meta"><b>'+n+'</b> · '+pct+'%</span></div>'+
'<div class="bar-track"><div class="bar-fill" style="width:'+w.toFixed(1)+'%;background:'+colorMap[o]+'"></div></div></div>';
});
box.innerHTML = html;
}
Passos
- Ter os dois links. Link de votação + link da planilha. Se faltar qualquer um, pergunte e pare (Passo 0). Lembre o usuário de deixar a planilha "qualquer pessoa com o link → Leitor".
- Extrair o
SHEET_ID do link da planilha e verificar o gviz com curl (Passo 1). Se não estiver público, peça o ajuste de compartilhamento.
- Escolher o gráfico. Donut (padrão) ou barras. Se o usuário não disse e há 5+ opções, sugira barras.
- Gerar o QR localmente (Passo 2) e colar o
<svg> inline no .qr-card.
- Montar o slide a partir do template canônico (ou da variante de barras), preenchendo
SHEET_ID e o QR. Salvar em decks/<nome-da-enquete>/index.html.
- Reportar. Caminho do arquivo; o tipo de gráfico; lembrar que o slide precisa de internet (lê a planilha ao vivo) e que abre por duplo-clique (
file://), sem servidor; e que para trocar a pergunta basta editar o Form (a planilha e o slide acompanham sozinhos).
Checklist