KyeYongChoo/mcq-quiz-page

Use quando o usuário solicitar um quiz de múltipla escolha ou perguntas de prática a partir de slides ou anotações, como uma página clicável com respostas verdes/vermelhas, modo de revisão, repetição de erros e exportação para PDF; pode incluir perguntas estruturadas e de análise de programa.

O que é mcq-quiz-page?

Esta skill cria uma página web interativa de quiz, projetada para converter materiais de estudo (slides, anotações, PDFs) em perguntas de múltipla escolha (MCQs) e perguntas estruturadas/de análise de programa. O usuário fornece o conteúdo fonte, e a skill gera um array JSON de perguntas que é inserido em um template HTML pré-definido. A página oferece feedback imediato (opções ficam verdes para corretas e vermelhas para erradas, com explicação), um modo de revisão que mostra apenas os erros, uma funcionalidade de repetir apenas as questões erradas, e exportação para PDF (que requer declaração de capacidade de download). A skill é compatível com agentes como Claude Code que podem publicar artefatos HTML. O fluxo de trabalho inclui: leitura do material fonte, criação das perguntas seguindo regras rigorosas de qualidade (distratores convincentes, distribuição uniforme da resposta correta, explicações concisas), e publicação como um artefato HTML. A página também suporta modo escuro e salva o progresso (respostas, rascunhos e lista de repetição) no navegador do usuário via localStorage.

Funciona com✓Claude Code~Codex CLI✓Cursor
Part ofMobile
npx skills add https://github.com/KyeYongChoo/mcq-quiz-page-skill/tree/HEAD/mcq-quiz-page

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

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

Documentação

Página de quiz de múltipla escolha

O design da página está finalizado. Não o redesenhe. Seu trabalho é escrever boas perguntas, inseri-las no modelo abaixo como JSON e publicá-lo.

O que a página faz:

  • Múltipla escolha: toque em uma opção e ela fica verde (correta) ou vermelha (errada, com a opção correta ficando verde e uma explicação sendo mostrada). Um pequeno botão "Refazer esta questão" abaixo de cada pergunta respondida redefine apenas aquela.
  • Perguntas estruturadas / de análise de programa (tipo "short"): o usuário digita uma resposta (opcional), toca em Mostrar resposta modelo, e então se marca como "Acertou" ou "Errou". As erradas contam como erros. Trechos de código aparecem em bloco monoespaçado.
  • A guia Revisão lista apenas os erros com a resposta correta / resposta modelo logo abaixo de cada um. O modo Revisão possui Exportar PDF, Copiar como texto e Refazer erros.
  • Refazer erros limpa essas perguntas e abre uma guia Retry que mostra APENAS os erros anteriores (sem rolar por todo o quiz). Ela mostra o progresso (feitos/total) e, quando todos são concluídos, um resumo com "Repetir os N ainda errados" e "Voltar para todas as perguntas". A guia Retry permanece disponível até "Redefinir tudo".
  • O progresso, as respostas digitadas e a lista de repetição são mantidos no navegador do visualizador.

Etapas

  1. Leia o material fonte (slides, anotações, PDFs) primeiro. As perguntas devem vir dele, não da memória. Para PDFs, use pdftotext -layout. Se o usuário anexar soluções de tutoriais ou provas passadas, baseie tanto MCQs quanto perguntas estruturadas/de análise de programa nelas (novas variações, não cópias literais, a menos que solicitado).
  2. Escreva as perguntas como um array JSON (esquema abaixo). O padrão é 4 opções para MCQs. Se o usuário der uma contagem, use exatamente essa contagem. Adicione perguntas estruturadas / de análise de programa quando a fonte tiver perguntas de resposta curta ou código, ou quando o usuário as solicitar (coloque-as após os MCQs da mesma seção).
  3. Verifique as perguntas com as regras de equilíbrio abaixo, idealmente com um pequeno script.
  4. Construa a página: copie o modelo para index.html, substitua o texto __DATA__ pelo JSON (escape cada </ como </), e altere apenas: o <title>, o <h1>, a frase .sub e a chave de armazenamento bps-quiz-v1 (dê a cada quiz uma nova chave, ex. quiz-<tópico>-v1, para que o progresso salvo nunca se misture). Execute node --check no script, se possível (extraia o bloco de script para um arquivo .js). Um teste rápido com Playwright (responder, marcar, Revisar, Refazer erros) vale a pena.
  5. Publique como um artefato HTML construído manualmente: chame o artefato list_types primeiro (não há tipo de quiz; use publish_files com index.html, um título real, ícone quiz). Em seguida, chame declare_capabilities com {"downloads": true} usando o número de versão mais recente. O botão PDF só funciona com essa capacidade. Ao atualizar um quiz existente, publique novamente com publish_files (modo patch, slug, base_version); a declaração de capacidade é mantida. Se não for possível publicar artefatos, entregue index.html como um arquivo (o botão PDF então permanece oculto; Copiar como texto ainda funciona).
  6. Responda em algumas frases: o que é a página, quantas perguntas (MCQ e estruturadas) e de quais seções, e que a exportação para PDF pede que o visualizador confirme o download. Não cole as perguntas no chat.

Esquema JSON (um objeto por pergunta, em ordem)

Múltipla escolha:

{"n": 1, "section": "Aula 4 · Linkability", "q": "Texto da pergunta?", "opts": ["Texto A", "Texto B", "Texto C", "Texto D"], "ans": 2, "ex": "Explicação de uma frase."}

Estruturada ou análise de programa (code opcional, kind opcional, ex opcional):

{"n": 5, "type": "short", "section": "Aula 4 · Linkability", "kind": "Questão estruturada", "q": "Descreva ...?", "model": ["Ponto 1 da resposta modelo", "Ponto 2"], "ex": "Nota extra opcional"}
{"n": 6, "type": "short", "section": "...", "kind": "Análise de programa", "q": "O que está errado aqui e como você corrige?", "code": "int f(char *s) {\n  char b[8];\n  strcpy(b, s);\n}", "model": ["(a) ...", "(b) ..."]}

kind padrão é "Resposta curta"; use "Questão estruturada" ou "Análise de programa". Cada entrada de model é um marcador; rotule subpartes como "(a)", "(b)" dentro dos marcadores. Escreva code com quebras de linha e recuo de 2 espaços.

Notas opcionais por opção (APENAS quando o usuário solicitar texto extra sob as opções erradas): adicione "notes": ["", "Por que B está errado", "", "Por que D está errado"] a um MCQ, uma string por opção na mesma ordem, com "" para opções que não precisam de nota (geralmente a correta). As notas aparecem sob cada opção após o usuário responder, e em Copiar/PDF. Não adicione notas a menos que solicitado.

n vai de 1 a N sem lacunas em todas as perguntas (a página indexa por n-1). ans é o índice baseado em 0 da opção correta. section agrupa perguntas na página; use a mesma string para perguntas em um grupo e mantenha os grupos contíguos. Evite caracteres fora do texto latino simples sempre que possível (o PDF usa uma fonte embutida; símbolos incomuns imprimem como ?).

Regras de qualidade das perguntas (o usuário se importa com elas)

  • Os distratores devem ser convincentes: use conceitos reais do mesmo material, associados à pergunta errada (a propriedade de um termo vizinho, um número próximo, um mecanismo plausível). Sem opções de piada, sem "todas as anteriores".
  • Não deixe a resposta correta ser a mais longa. Escreva a opção correta e os distratores com comprimento e nível de detalhe semelhantes e, às vezes, faça um distrator ser o mais longo. Objetivo: a opção correta é estritamente a mais longa em não mais que cerca de 30% das perguntas com opções de frase completa (verifique com um script; é fácil exagerar).
  • Distribua a letra correta uniformemente entre A-D (cerca de 25% cada) e nunca a mesma letra três vezes seguidas. Método mais fácil: escreva a opção correta e três distratores separadamente e, em seguida, embaralhe com uma lista de posições equilibradas no código.
  • Teste uma ideia por pergunta. Mantenha o texto da pergunta autocontido.
  • Cada MCQ recebe uma explicação em ex que diz por que a resposta está correta (e, quando útil, o que os distratores realmente são).
  • Respostas modelo de perguntas estruturadas/análise de programa devem refletir os pontos-chave da solução fonte como marcadores curtos, para que o usuário possa se autoavaliar honestamente.
  • Se um slide for apenas um diagrama que você não pode ler, diga isso em vez de inventar detalhes.

Modelo (index.html)

<!DOCTYPE html>
<html lang="pt"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quiz do Tópico</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@500&display=swap">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
:root{--bg:#f5f6f8;--card:#ffffff;--ink:#16202e;--mute:#5b6678;--line:#d5dae3;--accent:#1d4ed8;--accent-soft:#e6ecff;
--ok:#0f7a4d;--ok-soft:#dcf3e6;--ok-line:#52b887;--bad:#b42318;--bad-soft:#fde4e1;--bad-line:#e57a70;
--body:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;--mono:"IBM Plex Mono",ui-monospace,Menlo,monospace}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#10151d;--card:#171e29;--ink:#e6eaf1;--mute:#9aa6b8;--line:#2e3849;--accent:#8aa9ff;--accent-soft:#1f2a45;--ok:#6fe0aa;--ok-soft:#12332a;--ok-line:#2f8a63;--bad:#ff9a8f;--bad-soft:#3b1b1a;--bad-line:#a94a42;color-scheme:dark}}
:root[data-theme="dark"]{--bg:#10151d;--card:#171e29;--ink:#e6eaf1;--mute:#9aa6b8;--line:#2e3849;--accent:#8aa9ff;--accent-soft:#1f2a45;--ok:#6fe0aa;--ok-soft:#12332a;--ok-line:#2f8a63;--bad:#ff9a8f;--bad-soft:#3b1b1a;--bad-line:#a94a42;color-scheme:dark}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:400 16px/1.55 var(--body)}
main{max-width:760px;margin:0 auto;padding:24px 16px 64px}
h1{font-size:1.7rem;line-height:1.2;margin:0 0 6px;text-wrap:balance}
.sub{color:var(--mute);margin:0 0 14px}
.bar{position:sticky;top:0;z-index:3;background:var(--bg);border-bottom:1px solid var(--line);padding:10px 0;display:flex;flex-wrap:wrap;gap:8px 10px;align-items:center}
.tabs{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:var(--card)}
.tabs button{border:0;background:transparent;color:var(--ink);font:500 .88rem var(--body);padding:8px 14px;cursor:pointer}
.tabs button[aria-pressed="true"]{background:var(--accent);color:var(--bg)}
.stats{font-size:.88rem;color:var(--mute);font-variant-numeric:tabular-nums;display:flex;gap:12px;flex-wrap:wrap}
.stats b{color:var(--ink)}
.acts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.btn{font:500 .85rem var(--body);color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:8px;padding:7px 12px;cursor:pointer;text-decoration:none;display:inline-flex;gap:8px;align-items:center}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--bg)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn:not(:disabled):hover{filter:brightness(.95)}
:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
.jump{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 0}
.jump a{font:500 .85rem var(--body);color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:8px;padding:7px 11px;text-decoration:none}
.jump a span{color:var(--mute);margin-left:8px;font-variant-numeric:tabular-nums}
h2{font-size:1.08rem;margin:30px 0 14px;padding-bottom:8px;border-bottom:2px solid var(--accent);text-wrap:balance;scroll-margin-top:70px}
.banner{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:18px 0 6px}
.banner p{margin:0}.banner p+p{margin-top:8px;color:var(--mute);font-size:.92rem}
.q{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px 16px 14px;margin-bottom:14px;scroll-margin-top:70px}
h3{font-size:1rem;font-weight:600;margin:0 0 12px;display:flex;gap:10px;line-height:1.45}
.num{font:500 .8rem var(--mono);color:var(--accent);background:var(--accent-soft);border-radius:6px;padding:2px 7px;height:fit-content;font-variant-numeric:tabular-nums}
.sec{display:block;font-weight:400;font-size:.78rem;color:var(--mute);margin-bottom:2px}
.opts{display:grid;gap:8px}
.opt{display:flex;gap:10px;align-items:flex-start;text-align:left;width:100%;font:inherit;color:inherit;background:transparent;border:1px solid var(--line);border-radius:8px;padding:10px 12px;cursor:pointer}
.opt b{font:500 .85rem var(--mono);color:var(--mute);min-width:1.2em;padding-top:1px}
.opt:not(:disabled):hover{border-color:var(--accent);background:var(--accent-soft)}
.opt:disabled{cursor:default}
.opt.ok{background:var(--ok-soft);border-color:var(--ok-line)}
.opt.ok b,.opt.ok .tag{color:var(--ok)}
.opt.bad{background:var(--bad-soft);border-color:var(--bad-line)}
.opt.bad b,.opt.bad .tag{color:var(--bad)}
.opt .txt{flex:1}
.tag{font:500 .75rem var(--mono);white-space:nowrap;padding-top:2px}
.fb{margin-top:10px;border-radius:8px;padding:10px 12px;background:var(--accent-soft)}
.fb p{margin:0}.fb .head{font-weight:600}.fb.good .head{color:var(--ok)}.fb.miss .head{color:var(--bad)}
.fb .why{color:var(--mute);font-size:.92rem;margin-top:4px}
.empty{text-align:center;color:var(--mute);padding:36px 12px}
.code{margin:0 0 12px;padding:10px 12px;background:var(--bg);border:1px solid var(--line);border-radius:8px;font:500 .82rem/1.5 var(--mono);overflow-x:auto;white-space:pre;tab-size:2}
textarea.ans{width:100%;font:inherit;color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:8px;padding:10px 12px;resize:vertical;min-height:84px}
textarea.ans[readonly]{background:var(--bg)}
.row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:10px}
.ask{color:var(--mute);font-size:.9rem}
.model{margin:6px 0 0;padding-left:20px}
.model li{margin:3px 0}
.mark{margin:10px 0 0;font-weight:600;font-size:.92rem}
.mark.good{color:var(--ok)}.mark.miss{color:var(--bad)}
.onote{display:block;margin-top:4px;font-size:.86rem;font-weight:400;color:var(--mute)}
.btn.small{padding:5px 10px;font-size:.8rem;color:var(--mute)}
</style></head><body><main>
<h1>Quiz do Tópico</h1>
<p class="sub">N perguntas do material do curso. Toque em uma opção para responder. No modo Revisão, você vê apenas as que errou e pode exportá-las como PDF.</p>
<div class="bar" role="toolbar" aria-label="Controles do quiz">
  <div class="tabs" role="group" aria-label="Modo">
    <button type="button" id="tab-quiz" aria-pressed="true">Quiz</button>
    <button type="button" id="tab-review" aria-pressed="false">Revisão</button>
    <button type="button" id="tab-retry" aria-pressed="false" hidden>Repetir</button>
  </div>
  <div class="stats" id="stats" aria-live="polite"></div>
  <div class="acts" id="acts"></div>
</div>
<div id="view"></div>
</main>
<script>
var DATA=__DATA__;
var L="ABCD", KEY="bps-quiz-v1", DKEY=KEY+"-d", RKEY=KEY+"-r";
var answers={}, drafts={}, retry=[], mode="quiz", dl=null;
try{var s=localStorage.getItem(KEY); if(s) answers=JSON.parse(s)||{};}catch(e){}
try{var s2=localStorage.getItem(DKEY); if(s2) drafts=JSON.parse(s2)||{};}catch(e){}
try{var s3=localStorage.getItem(RKEY); if(s3) retry=JSON.parse(s3)||[];}catch(e){}
function save(){try{localStorage.setItem(KEY,JSON.stringify(answers));}catch(e){}}
function saveD(){try{localStorage.setItem(DKEY,JSON.stringify(drafts));}catch(e){}}
function saveR(){try{localStorage.setItem(RKEY,JSON.stringify(retry));}catch(e){}}
function esc(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");}
function byN(n){return DATA[n-1];}
function isDone(q){var a=answers[q.n]; return q.type==="short" ? (a==="ok"||a==="miss") : a!==undefined;}
function wrongList(){return DATA.filter(function(q){return q.type==="short" ? answers[q.n]==="miss" : (answers[q.n]!==undefined&&answers[q.n]!==q.ans);});}
function doneCount(){return DATA.filter(isDone).length;}

function shortHTML(q,ctx){
  var review=(ctx!=="quiz"), a=answers[q.n], marked=(a==="ok"||a==="miss"), shown=marked||a==="rev";
  var lab=(review?esc(q.section)+" - ":"")+esc(q.q);
  …
  [truncado para tradução]
}```

Habilidades Relacionadas