@matinfo/vitepress-plugin-quiz 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/src/utils.d.ts +5 -0
- package/dist/vitepress-plugin-quiz.js +302 -244
- package/dist/vitepress-plugin-quiz.js.map +1 -1
- package/dist/vitepress-plugin-quiz.umd.cjs +4 -4
- package/dist/vitepress-plugin-quiz.umd.cjs.map +1 -1
- package/package.json +29 -21
- package/dist/src/__tests__/locales.test.d.ts +0 -1
- package/dist/src/__tests__/mdPlugin.test.d.ts +0 -1
- package/dist/src/__tests__/richText.test.d.ts +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitepress-plugin-quiz.js","sources":["../src/styleManager.ts","../src/md-plugin.ts","../src/locales.ts","../src/utils.ts","../src/QuizQuestion.vue","../src/QuizPage.vue","../src/index.ts"],"sourcesContent":["import quizCssInline from \"./quiz.css?inline\"\n\nlet stylesInjected = false\n\nexport function ensureStylesInjected(): void {\n if (typeof document === \"undefined\") return\n if (stylesInjected) return\n const styleId = \"vitepress-plugin-quiz-styles\"\n if (document.getElementById(styleId)) {\n stylesInjected = true\n return\n }\n const style = document.createElement(\"style\")\n style.id = styleId\n style.textContent = quizCssInline\n document.head.appendChild(style)\n stylesInjected = true\n}\n","import type MarkdownIt from \"markdown-it\"\nimport type StateBlock from \"markdown-it/lib/rules_block/state_block.mjs\"\nimport type { QuizData, QuizAnswer } from \"./types/quiz\"\n\nconst QUIZ_RE = /^:::quiz\\s*$/\nconst CLOSE_RE = /^:::\\s*$/\n\n/**\n * Register the quiz block rule on a markdown-it instance.\n * Use in VitePress config:\n * markdown: { config(md) { md.use(quizMarkdownPlugin) } }\n */\nexport function quizMarkdownPlugin(md: MarkdownIt): void {\n const escapeHtml = md.utils.escapeHtml\n\n md.block.ruler.before(\n \"fence\",\n \"quiz\",\n (state, startLine, endLine, silent) =>\n quizBlockRule(state, startLine, endLine, silent, escapeHtml),\n { alt: [\"paragraph\", \"reference\", \"blockquote\", \"list\"] },\n )\n\n md.renderer.rules.quiz = (tokens, idx) => tokens[idx].content\n\n // Wrap entire token stream with <QuizPage>…</QuizPage> if quiz tokens exist\n md.core.ruler.push(\"quiz_page_wrap\", (state) => {\n const hasQuiz = state.tokens.some((t) => t.type === \"quiz\")\n if (!hasQuiz) return\n\n const open = new state.Token(\"html_block\", \"\", 0)\n open.content = \"<QuizPage>\\n\"\n open.block = true\n\n const close = new state.Token(\"html_block\", \"\", 0)\n close.content = \"</QuizPage>\\n\"\n close.block = true\n\n state.tokens.unshift(open)\n state.tokens.push(close)\n })\n}\n\n/* ── block rule ──────────────────────────────────────────────────────── */\n\nfunction quizBlockRule(\n state: StateBlock,\n startLine: number,\n endLine: number,\n silent: boolean,\n escapeHtml: (s: string) => string,\n): boolean {\n const startPos = state.bMarks[startLine] + state.tShift[startLine]\n const startMax = state.eMarks[startLine]\n const lineText = state.src.slice(startPos, startMax)\n\n if (!QUIZ_RE.test(lineText)) return false\n if (silent) return true\n\n let nextLine = startLine + 1\n let found = false\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine]\n const max = state.eMarks[nextLine]\n const line = state.src.slice(pos, max)\n if (CLOSE_RE.test(line)) {\n found = true\n break\n }\n nextLine++\n }\n if (!found) return false\n\n const innerLines: string[] = []\n for (let i = startLine + 1; i < nextLine; i++) {\n const pos = state.bMarks[i] + state.tShift[i]\n const max = state.eMarks[i]\n innerLines.push(state.src.slice(pos, max))\n }\n\n const data = parseQuizBlock(innerLines)\n if (!data) return false\n\n const token = state.push(\"quiz\", \"\", 0)\n token.content = renderQuizComponent(data, escapeHtml)\n token.map = [startLine, nextLine + 1]\n token.block = true\n\n state.line = nextLine + 1\n return true\n}\n\n/* ── parser ──────────────────────────────────────────────────────────── */\n\nexport function parseQuizBlock(lines: string[]): QuizData | null {\n let question = \"\"\n const answers: QuizAnswer[] = []\n let explanation = \"\"\n const expected: string[] = []\n let shuffle = false\n\n for (const raw of lines) {\n const line = raw.trim()\n if (!line) continue\n\n if (line.startsWith(\"question:\")) {\n question = line.slice(\"question:\".length).trim()\n } else if (line.startsWith(\"answer-correct:\")) {\n answers.push({ text: line.slice(\"answer-correct:\".length).trim(), correct: true })\n } else if (line.startsWith(\"answer-input:\")) {\n expected.push(line.slice(\"answer-input:\".length).trim())\n } else if (line.startsWith(\"answer:\")) {\n answers.push({ text: line.slice(\"answer:\".length).trim(), correct: false })\n } else if (line.startsWith(\"explanation:\")) {\n explanation = line.slice(\"explanation:\".length).trim()\n } else if (line.startsWith(\"explication:\")) {\n // legacy French alias\n explanation = line.slice(\"explication:\".length).trim()\n } else if (line.startsWith(\"shuffle:\")) {\n shuffle = line.slice(\"shuffle:\".length).trim().toLowerCase() === \"true\"\n }\n }\n\n if (!question) return null\n\n if (expected.length > 0) {\n return { question, type: \"input\", answers: [], expected, explanation, shuffle: false }\n }\n\n if (answers.length === 0) return null\n return { question, type: \"choice\", answers, expected: [], explanation, shuffle }\n}\n\n/* ── renderer ────────────────────────────────────────────────────────── */\n\nfunction renderQuizComponent(data: QuizData, escapeHtml: (s: string) => string): string {\n const json = JSON.stringify(data)\n const escaped = escapeHtml(json)\n return `<QuizQuestion data-quiz=\"${escaped}\" />\\n`\n}\n","import type { QuizLocale } from \"./types/quiz\"\n\nexport const locales: Record<string, QuizLocale> = {\n en: {\n answered: \"answered\",\n validate: \"Validate\",\n reset: \"Restart\",\n correctAnswers: (n) => `correct answer${n !== 1 ? \"s\" : \"\"}`,\n multipleHint: \"Multiple answers possible\",\n explanation: \"Explanation: \",\n inputHint: \"Type your answer\",\n inputPlaceholder: \"Your answer…\",\n expectedAnswer: \"Expected answer:\",\n },\n fr: {\n answered: \"répondue(s)\",\n validate: \"Valider\",\n reset: \"Recommencer\",\n correctAnswers: (n) => `réponse${n > 1 ? \"s\" : \"\"} correcte${n > 1 ? \"s\" : \"\"}`,\n multipleHint: \"Plusieurs réponses possibles\",\n explanation: \"Explication : \",\n inputHint: \"Saisissez votre réponse\",\n inputPlaceholder: \"Votre réponse…\",\n expectedAnswer: \"Réponse attendue :\",\n },\n de: {\n answered: \"beantwortet\",\n validate: \"Überprüfen\",\n reset: \"Neu starten\",\n correctAnswers: (n) => `richtige Antwort${n !== 1 ? \"en\" : \"\"}`,\n multipleHint: \"Mehrere Antworten möglich\",\n explanation: \"Erklärung:\",\n inputHint: \"Geben Sie Ihre Antwort ein\",\n inputPlaceholder: \"Ihre Antwort…\",\n expectedAnswer: \"Erwartete Antwort:\",\n },\n it: {\n answered: \"risposta/e\",\n validate: \"Verifica\",\n reset: \"Ricominciare\",\n correctAnswers: (n) => `rispost${n !== 1 ? \"e corrette\" : \"a corretta\"}`,\n multipleHint: \"Più risposte possibili\",\n explanation: \"Spiegazione: \",\n inputHint: \"Digita la tua risposta\",\n inputPlaceholder: \"La tua risposta…\",\n expectedAnswer: \"Risposta attesa:\",\n },\n es: {\n answered: \"respondida(s)\",\n validate: \"Validar\",\n reset: \"Reiniciar\",\n correctAnswers: (n) => `respuesta${n !== 1 ? \"s correctas\" : \" correcta\"}`,\n multipleHint: \"Varias respuestas posibles\",\n explanation: \"Explicación: \",\n inputHint: \"Escribe tu respuesta\",\n inputPlaceholder: \"Tu respuesta…\",\n expectedAnswer: \"Respuesta esperada:\",\n },\n}\n\n/**\n * Get locale strings for a given language code.\n * Falls back to English if the language is not found.\n */\nexport function getLocale(lang = \"en\"): QuizLocale {\n return locales[lang] ?? locales.en\n}\n","/**\n * Inline markdown part — result of {@link parseRichText}.\n */\nexport type RichPart = { type: \"text\" | \"bold\" | \"italic\" | \"code\"; value: string }\n\n/**\n * Parse inline markdown subsets: **bold**, *italic*, `code` → typed parts array.\n * Used for question titles, answer text, and explanations.\n */\nexport function parseRichText(text: string): RichPart[] {\n const parts: RichPart[] = []\n const re = /(\\*\\*(.+?)\\*\\*)|(\\*(.+?)\\*)|(`([^`]+)`)/g\n let last = 0\n let m\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) parts.push({ type: \"text\", value: text.slice(last, m.index) })\n if (m[2]) parts.push({ type: \"bold\", value: m[2] })\n else if (m[4]) parts.push({ type: \"italic\", value: m[4] })\n else if (m[6]) parts.push({ type: \"code\", value: m[6] })\n last = re.lastIndex\n }\n if (last < text.length) parts.push({ type: \"text\", value: text.slice(last) })\n return parts\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, inject, watch, onMounted } from \"vue\"\nimport type { QuizData, QuizAnswer, QuizLocale, SavedAnswer } from \"./types/quiz\"\n\nconst props = defineProps({\n dataQuiz: { type: String, required: true },\n})\n\nconst rawQuiz = computed<QuizData | null>(() => {\n try {\n return JSON.parse(props.dataQuiz) as QuizData\n } catch {\n return null\n }\n})\n\n// Shuffle helper (Fisher-Yates)\nfunction shuffleArray<T>(arr: T[]): T[] {\n const a = [...arr]\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[a[i], a[j]] = [a[j], a[i]]\n }\n return a\n}\n\n// Shuffled answers — computed once at mount, re-shuffled on reset\nconst shuffledAnswers = ref<(QuizAnswer & { originalIndex: number })[]>([])\nfunction initAnswers() {\n if (!rawQuiz.value || rawQuiz.value.type === \"input\") {\n shuffledAnswers.value = []\n return\n }\n const indexed = rawQuiz.value.answers.map((a, i) => ({ ...a, originalIndex: i }))\n shuffledAnswers.value = rawQuiz.value.shuffle ? shuffleArray(indexed) : indexed\n}\n\n// Quiz data with shuffled answers\nconst quiz = computed(() => {\n if (!rawQuiz.value) return null\n return {\n ...rawQuiz.value,\n answers: shuffledAnswers.value,\n }\n})\n\nconst isInputMode = computed(() => rawQuiz.value?.type === 'input')\n\nconst correctCount = computed(() =>\n quiz.value && !isInputMode.value\n ? quiz.value.answers.filter((a) => a.correct).length\n : 0\n)\nconst isMultiple = computed(() => correctCount.value > 1)\n\n// Selected answer indices (for choice mode)\nconst selected = ref(new Set<number>())\n// Text input value (for fill-in-the-blank mode)\nconst inputValue = ref(\"\")\n\n// Registration with parent QuizPage\nconst register = inject<((q: { answered: typeof answered; isCorrect: typeof isCorrect }) => void) | null>(\"quiz-register\", null)\nconst validated = inject<ReturnType<typeof ref<boolean>>>(\"quiz-validated\", ref(false))\nconst resetCounter = inject<ReturnType<typeof ref<number>>>(\"quiz-reset\", ref(0))\nconst t = inject<QuizLocale>(\"quiz-locale\")!\n\n// Per-question persistence\nconst getQuestionId = inject<(() => number) | null>(\"quiz-question-id\", null)\nconst saveAnswer = inject<((id: number, data: SavedAnswer) => void) | null>(\"quiz-save-answer\", null)\nconst getAnswer = inject<((id: number) => SavedAnswer | null) | null>(\"quiz-get-answer\", null)\nconst myId: number | null = getQuestionId ? getQuestionId() : null\n\nconst answered = computed(() => {\n if (isInputMode.value) return inputValue.value.trim().length > 0\n return selected.value.size > 0\n})\n\nconst isCorrect = computed(() => {\n if (!quiz.value || !answered.value) return false\n\n if (isInputMode.value) {\n // Case-insensitive comparison against any accepted variant\n const input = inputValue.value.trim().toLowerCase()\n return rawQuiz.value!.expected.some((e) => input === e.trim().toLowerCase())\n }\n\n const correctIndices = new Set(\n quiz.value.answers\n .map((a, i) => (a.correct ? i : -1))\n .filter((i) => i >= 0)\n )\n if (selected.value.size !== correctIndices.size) return false\n for (const idx of selected.value) {\n if (!correctIndices.has(idx)) return false\n }\n return true\n})\n\n// Register this question with the parent page\nif (register) {\n register({ answered, isCorrect })\n}\n\n// Watch for reset from parent\nwatch(resetCounter, () => {\n selected.value = new Set()\n inputValue.value = ''\n initAnswers()\n})\n\nonMounted(() => {\n initAnswers()\n // Restore saved answer state\n if (getAnswer && myId !== null) {\n const saved = getAnswer(myId)\n if (saved) {\n if (saved.type === \"input\") {\n inputValue.value = saved.input\n } else if (saved.type === \"choice\") {\n selected.value = new Set(saved.selected)\n }\n }\n }\n})\n\nfunction toggle(idx: number) {\n if (validated.value) return\n const next = new Set(selected.value)\n if (isMultiple.value) {\n if (next.has(idx)) next.delete(idx)\n else next.add(idx)\n } else {\n next.clear()\n next.add(idx)\n }\n selected.value = next\n persistState()\n}\n\n// Save input value on change\nwatch(inputValue, () => {\n if (!validated.value) persistState()\n})\n\nfunction persistState() {\n if (!saveAnswer || myId === null) return\n if (isInputMode.value) {\n saveAnswer(myId, { type: \"input\", input: inputValue.value })\n } else {\n saveAnswer(myId, { type: \"choice\", selected: [...selected.value] })\n }\n}\n\nfunction answerClass(idx: number): string {\n if (!validated.value) {\n return selected.value.has(idx) ? \"quiz-answer selected\" : \"quiz-answer\"\n }\n const answer = quiz.value!.answers[idx]\n const wasPicked = selected.value.has(idx)\n if (answer.correct && wasPicked) return \"quiz-answer correct\"\n if (answer.correct && !wasPicked) return \"quiz-answer missed\"\n if (!answer.correct && wasPicked) return \"quiz-answer wrong\"\n return \"quiz-answer\"\n}\n\nconst inputClass = computed(() => {\n if (!validated.value) return \"quiz-input\"\n return isCorrect.value ? \"quiz-input correct\" : \"quiz-input wrong\"\n})\n\nimport { parseRichText, type RichPart } from './utils'\n</script>\n\n<template>\n <fieldset v-if=\"quiz\" class=\"quiz-question\" :class=\"{ validated: validated }\" :aria-label=\"quiz.question\">\n <legend class=\"quiz-question-title\">\n <template v-for=\"(part, pi) in parseRichText(quiz.question)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </legend>\n\n <!-- Choice mode -->\n <template v-if=\"!isInputMode\">\n <p v-if=\"isMultiple\" class=\"quiz-hint\">{{ t.multipleHint }}</p>\n <ul class=\"quiz-answers\" role=\"listbox\" :aria-multiselectable=\"isMultiple\">\n <li\n v-for=\"(answer, idx) in quiz.answers\"\n :key=\"answer.originalIndex\"\n :class=\"answerClass(idx)\"\n @click=\"toggle(idx)\"\n role=\"option\"\n :aria-selected=\"selected.has(idx)\"\n tabindex=\"0\"\n @keydown.enter=\"toggle(idx)\"\n @keydown.space.prevent=\"toggle(idx)\"\n >\n <span class=\"quiz-indicator\" aria-hidden=\"true\">\n <template v-if=\"isMultiple\">\n <span v-if=\"selected.has(idx)\" class=\"quiz-check\">✓</span>\n <span v-else class=\"quiz-empty\">☐</span>\n </template>\n <template v-else>\n <span v-if=\"selected.has(idx)\" class=\"quiz-check\">●</span>\n <span v-else class=\"quiz-empty\">○</span>\n </template>\n </span>\n <span class=\"quiz-answer-text\">\n <template v-for=\"(part, pi) in parseRichText(answer.text)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </span>\n </li>\n </ul>\n </template>\n\n <!-- Fill-in-the-blank mode -->\n <template v-if=\"isInputMode\">\n <p class=\"quiz-hint\">{{ t.inputHint }}</p>\n <div class=\"quiz-input-wrapper\">\n <input\n v-model=\"inputValue\"\n :class=\"inputClass\"\n type=\"text\"\n :disabled=\"validated\"\n :placeholder=\"t.inputPlaceholder\"\n :aria-label=\"quiz.question\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @keydown.enter.prevent\n />\n <p v-if=\"validated && !isCorrect\" class=\"quiz-expected\">\n {{ t.expectedAnswer }} <code>{{ rawQuiz?.expected[0] }}</code>\n </p>\n </div>\n </template>\n\n <!-- Explanation (rich text: supports **bold** and `code`) -->\n <div v-if=\"validated && quiz.explanation\" class=\"quiz-explanation\" role=\"note\">\n <strong>{{ t.explanation }}</strong>\n <template v-for=\"(part, pi) in parseRichText(quiz.explanation)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </div>\n </fieldset>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, provide, inject } from \"vue\"\nimport type { QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nconst t = inject<QuizLocale>(\"quiz-locale\")!\nconst questions = ref<QuizState[]>([])\nconst validated = ref(false)\n\n// Generate a storage key from the page path — set eagerly so children can read on mount\n// (Vue mounts children before parent, so onMounted would be too late)\nconst storageKey = typeof window !== 'undefined'\n ? 'quiz-state:' + window.location.pathname\n : ''\n\n// Restore quiz-level state immediately so validated is set before children mount\nrestoreState()\n\n// Question index counter for stable per-question storage keys\nlet questionIdx = 0\n\nprovide(\"quiz-validated\", validated)\nprovide(\"quiz-register\", (q: QuizState) => {\n questions.value.push(q)\n})\n\n// Per-question persistence: each question gets a unique sub-key\nprovide(\"quiz-question-id\", () => questionIdx++)\nprovide(\"quiz-save-answer\", (qId: number, data: SavedAnswer) => {\n if (!storageKey) return\n try {\n sessionStorage.setItem(storageKey + \":q\" + qId, JSON.stringify(data))\n } catch { /* ignore */ }\n})\nprovide(\"quiz-get-answer\", (qId: number): SavedAnswer | null => {\n if (!storageKey) return null\n try {\n const raw = sessionStorage.getItem(storageKey + \":q\" + qId)\n return raw ? JSON.parse(raw) : null\n } catch { return null }\n})\n\n// Provide a reset trigger for child components\nconst resetCounter = ref(0)\nprovide(\"quiz-reset\", resetCounter)\n\nconst totalQuestions = computed(() => questions.value.length)\nconst answeredCount = computed(() =>\n questions.value.filter((q) => q.answered).length\n)\nconst correctCount = computed(() =>\n questions.value.filter((q) => q.isCorrect).length\n)\nconst allAnswered = computed(() => answeredCount.value === totalQuestions.value)\nconst progressPct = computed(() =>\n totalQuestions.value ? Math.round((answeredCount.value / totalQuestions.value) * 100) : 0\n)\n\nfunction validate() {\n validated.value = true\n saveState()\n}\n\nfunction reset() {\n validated.value = false\n resetCounter.value++\n clearState()\n}\n\n/* ── sessionStorage persistence ───────────────────────────────────── */\nfunction saveState() {\n if (!storageKey) return\n try {\n const data = { validated: validated.value, ts: Date.now() }\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n } catch { /* storage full or unavailable */ }\n}\n\nfunction restoreState() {\n if (!storageKey) return\n try {\n const raw = sessionStorage.getItem(storageKey)\n if (!raw) return\n const data = JSON.parse(raw)\n if (data && data.validated) {\n validated.value = true\n }\n } catch { /* corrupted – ignore */ }\n}\n\nfunction clearState() {\n if (!storageKey) return\n try {\n const keysToRemove: string[] = []\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i)\n if (key && key.startsWith(storageKey)) {\n keysToRemove.push(key)\n }\n }\n keysToRemove.forEach((k) => sessionStorage.removeItem(k))\n } catch { /* ignore */ }\n}\n</script>\n\n<template>\n <div class=\"quiz-page\" role=\"region\" aria-label=\"Quiz\">\n <div\nclass=\"quiz-progress-bar\" role=\"progressbar\"\n :aria-valuenow=\"answeredCount\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"totalQuestions\"\n :aria-label=\"answeredCount + ' / ' + totalQuestions + ' ' + t.answered\"\n >\n <div class=\"quiz-progress-track\">\n <div\n class=\"quiz-progress-fill\"\n :style=\"{ width: progressPct + '%' }\"\n :class=\"{ complete: progressPct === 100 }\"\n ></div>\n </div>\n <span class=\"quiz-progress-label\" aria-live=\"polite\">\n {{ answeredCount }} / {{ totalQuestions }} {{ t.answered }}\n </span>\n </div>\n\n <slot />\n\n <div class=\"quiz-actions\">\n <button\n v-if=\"!validated\"\n class=\"quiz-btn quiz-btn-validate\"\n :disabled=\"!allAnswered\"\n @click=\"validate\"\n >\n {{ t.validate }}\n </button>\n\n <template v-if=\"validated\">\n <div\nclass=\"quiz-score\" role=\"status\" aria-live=\"polite\"\n :aria-label=\"correctCount + ' / ' + totalQuestions + ' ' + t.correctAnswers(correctCount)\"\n >\n <span class=\"quiz-score-value\">{{ correctCount }} / {{ totalQuestions }}</span>\n <span class=\"quiz-score-label\">\n {{ t.correctAnswers(correctCount) }}\n ({{ totalQuestions ? Math.round((correctCount / totalQuestions) * 100) : 0 }}%)\n </span>\n </div>\n <button class=\"quiz-btn quiz-btn-reset\" @click=\"reset\">\n {{ t.reset }}\n </button>\n </template>\n </div>\n </div>\n</template>\n","import type { App } from \"vue\"\nimport { ensureStylesInjected } from \"./styleManager\"\n\nexport { quizMarkdownPlugin } from \"./md-plugin\"\nexport { getLocale, locales } from \"./locales\"\nexport type { QuizData, QuizAnswer, QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nimport QuizQuestion from \"./QuizQuestion.vue\"\nimport QuizPage from \"./QuizPage.vue\"\nimport { getLocale as _getLocale } from \"./locales\"\n\nexport { QuizQuestion, QuizPage }\n\n/**\n * Register quiz components and locale in a VitePress `enhanceApp` hook.\n *\n * @example\n * // .vitepress/theme/index.ts\n * import { enhanceAppWithQuiz } from '@matinfo/vitepress-plugin-quiz'\n * export default {\n * enhanceApp({ app }) {\n * enhanceAppWithQuiz(app) // English (default)\n * enhanceAppWithQuiz(app, 'fr') // French\n * }\n * }\n */\nexport function enhanceAppWithQuiz(app: App, lang = \"en\"): void {\n ensureStylesInjected()\n app.provide(\"quiz-locale\", _getLocale(lang))\n app.component(\"QuizQuestion\", QuizQuestion)\n app.component(\"QuizPage\", QuizPage)\n}\n"],"names":["stylesInjected","ensureStylesInjected","styleId","style","quizCssInline","QUIZ_RE","CLOSE_RE","quizMarkdownPlugin","md","escapeHtml","state","startLine","endLine","silent","quizBlockRule","tokens","idx","t","open","close","startPos","startMax","lineText","nextLine","found","pos","max","line","innerLines","i","data","parseQuizBlock","token","renderQuizComponent","lines","question","answers","explanation","expected","shuffle","raw","json","locales","n","getLocale","lang","parseRichText","text","parts","re","last","m","props","__props","rawQuiz","computed","shuffleArray","arr","a","j","shuffledAnswers","ref","initAnswers","indexed","quiz","isInputMode","correctCount","isMultiple","selected","inputValue","register","inject","validated","resetCounter","getQuestionId","saveAnswer","getAnswer","myId","answered","isCorrect","input","e","correctIndices","watch","onMounted","saved","toggle","next","persistState","answerClass","answer","wasPicked","inputClass","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_2","_Fragment","_renderList","part","pi","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_createTextVNode","_openBlock","_hoisted_6","$event","_withKeys","_withModifiers","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","questions","storageKey","restoreState","questionIdx","provide","q","qId","totalQuestions","answeredCount","allAnswered","progressPct","validate","saveState","reset","clearState","keysToRemove","key","k","_hoisted_1","_renderSlot","_ctx","_hoisted_8","enhanceAppWithQuiz","app","_getLocale","QuizQuestion","QuizPage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vitepress-plugin-quiz.js","sources":["../src/styleManager.ts","../src/md-plugin.ts","../src/locales.ts","../src/utils.ts","../src/QuizQuestion.vue","../src/QuizPage.vue","../src/index.ts"],"sourcesContent":["import quizCssInline from \"./quiz.css?inline\"\n\nlet stylesInjected = false\n\nexport function ensureStylesInjected(): void {\n if (typeof document === \"undefined\") return\n if (stylesInjected) return\n const styleId = \"vitepress-plugin-quiz-styles\"\n if (document.getElementById(styleId)) {\n stylesInjected = true\n return\n }\n const style = document.createElement(\"style\")\n style.id = styleId\n style.textContent = quizCssInline\n document.head.appendChild(style)\n stylesInjected = true\n}\n","import type MarkdownIt from \"markdown-it\"\nimport type StateBlock from \"markdown-it/lib/rules_block/state_block.mjs\"\nimport type { QuizData, QuizAnswer } from \"./types/quiz\"\n\nconst QUIZ_RE = /^:::quiz\\s*$/\nconst CLOSE_RE = /^:::\\s*$/\nconst KEY_RE = /^([A-Za-z-]+):(.*)$/\nconst KNOWN_KEYS = new Set([\n \"question\",\n \"answer\",\n \"answer-correct\",\n \"answer-input\",\n \"explanation\",\n \"explication\",\n \"shuffle\",\n])\n\n/**\n * Register the quiz block rule on a markdown-it instance.\n * Use in VitePress config:\n * markdown: { config(md) { md.use(quizMarkdownPlugin) } }\n */\nexport function quizMarkdownPlugin(md: MarkdownIt): void {\n const escapeHtml = md.utils.escapeHtml\n\n md.block.ruler.before(\n \"fence\",\n \"quiz\",\n (state, startLine, endLine, silent) =>\n quizBlockRule(state, startLine, endLine, silent, escapeHtml),\n { alt: [\"paragraph\", \"reference\", \"blockquote\", \"list\"] },\n )\n\n md.renderer.rules.quiz = (tokens, idx) => tokens[idx].content\n\n // Wrap entire token stream with <QuizPage>…</QuizPage> if quiz tokens exist\n md.core.ruler.push(\"quiz_page_wrap\", (state) => {\n const hasQuiz = state.tokens.some((t) => t.type === \"quiz\")\n if (!hasQuiz) return\n\n const open = new state.Token(\"html_block\", \"\", 0)\n open.content = \"<QuizPage>\\n\"\n open.block = true\n\n const close = new state.Token(\"html_block\", \"\", 0)\n close.content = \"</QuizPage>\\n\"\n close.block = true\n\n state.tokens.unshift(open)\n state.tokens.push(close)\n })\n}\n\n/* ── block rule ──────────────────────────────────────────────────────── */\n\nfunction quizBlockRule(\n state: StateBlock,\n startLine: number,\n endLine: number,\n silent: boolean,\n escapeHtml: (s: string) => string,\n): boolean {\n // 4+ spaces of indentation is an indented code block, not a quiz\n if (state.sCount[startLine] - state.blkIndent >= 4) return false\n\n const startPos = state.bMarks[startLine] + state.tShift[startLine]\n const startMax = state.eMarks[startLine]\n const lineText = state.src.slice(startPos, startMax)\n\n if (!QUIZ_RE.test(lineText)) return false\n if (silent) return true\n\n let nextLine = startLine + 1\n let found = false\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine]\n const max = state.eMarks[nextLine]\n const line = state.src.slice(pos, max)\n if (CLOSE_RE.test(line)) {\n found = true\n break\n }\n nextLine++\n }\n if (!found) return false\n\n const innerLines: string[] = []\n for (let i = startLine + 1; i < nextLine; i++) {\n const pos = state.bMarks[i] + state.tShift[i]\n const max = state.eMarks[i]\n innerLines.push(state.src.slice(pos, max))\n }\n\n const data = parseQuizBlock(innerLines)\n if (data?.type === \"choice\" && !data.answers.some((a) => a.correct)) {\n console.warn(\n `[vitepress-plugin-quiz] :quiz block at line ${startLine + 1} has no 'answer-correct:'; ` +\n \"it can never be answered correctly.\",\n )\n }\n if (!data) {\n console.warn(\n `[vitepress-plugin-quiz] Ignored invalid :::quiz block at line ${startLine + 1}: ` +\n \"needs a 'question:' and at least one 'answer:'/'answer-correct:' or 'answer-input:'.\",\n )\n return false\n }\n\n const token = state.push(\"quiz\", \"\", 0)\n token.content = renderQuizComponent(data, escapeHtml)\n token.map = [startLine, nextLine + 1]\n token.block = true\n\n state.line = nextLine + 1\n return true\n}\n\n/* ── parser ──────────────────────────────────────────────────────────── */\n\nexport function parseQuizBlock(lines: string[]): QuizData | null {\n let question = \"\"\n const answers: QuizAnswer[] = []\n let explanation = \"\"\n const expected: string[] = []\n let shuffle = false\n\n // Lines that are not a known `key:` continue the previous field (multi-line values).\n let append: ((extra: string) => void) | null = null\n\n for (const raw of lines) {\n const line = raw.trim()\n if (!line) continue\n\n const m = KEY_RE.exec(line)\n const key = m?.[1].toLowerCase()\n if (!m || !key || !KNOWN_KEYS.has(key)) {\n append?.(line)\n continue\n }\n const value = m[2].trim()\n const join = (cur: string, extra: string) => (cur ? `${cur} ${extra}` : extra)\n\n switch (key) {\n case \"question\":\n question = value\n append = (x) => (question = join(question, x))\n break\n case \"answer-correct\":\n case \"answer\": {\n const entry: QuizAnswer = { text: value, correct: key === \"answer-correct\" }\n answers.push(entry)\n append = (x) => (entry.text = join(entry.text, x))\n break\n }\n case \"answer-input\":\n if (value) expected.push(value)\n append = null\n break\n case \"explanation\":\n case \"explication\": // legacy French alias\n explanation = value\n append = (x) => (explanation = join(explanation, x))\n break\n case \"shuffle\":\n shuffle = value.toLowerCase() === \"true\"\n append = null\n break\n }\n }\n\n if (!question) return null\n\n if (expected.length > 0) {\n return { question, type: \"input\", answers: [], expected, explanation, shuffle: false }\n }\n\n if (answers.length === 0) return null\n return { question, type: \"choice\", answers, expected: [], explanation, shuffle }\n}\n\n/* ── renderer ────────────────────────────────────────────────────────── */\n\nfunction renderQuizComponent(data: QuizData, escapeHtml: (s: string) => string): string {\n const json = JSON.stringify(data)\n const escaped = escapeHtml(json)\n return `<QuizQuestion data-quiz=\"${escaped}\" />\\n`\n}\n","import type { QuizLocale } from \"./types/quiz\"\n\nexport const locales: Record<string, QuizLocale> = {\n en: {\n answered: \"answered\",\n validate: \"Validate\",\n reset: \"Restart\",\n correctAnswers: (n) => `correct answer${n !== 1 ? \"s\" : \"\"}`,\n multipleHint: \"Multiple answers possible\",\n explanation: \"Explanation: \",\n inputHint: \"Type your answer\",\n inputPlaceholder: \"Your answer…\",\n expectedAnswer: \"Expected answer:\",\n },\n fr: {\n answered: \"répondue(s)\",\n validate: \"Valider\",\n reset: \"Recommencer\",\n correctAnswers: (n) => `réponse${n > 1 ? \"s\" : \"\"} correcte${n > 1 ? \"s\" : \"\"}`,\n multipleHint: \"Plusieurs réponses possibles\",\n explanation: \"Explication : \",\n inputHint: \"Saisissez votre réponse\",\n inputPlaceholder: \"Votre réponse…\",\n expectedAnswer: \"Réponse attendue :\",\n },\n de: {\n answered: \"beantwortet\",\n validate: \"Überprüfen\",\n reset: \"Neu starten\",\n correctAnswers: (n) => `richtige Antwort${n !== 1 ? \"en\" : \"\"}`,\n multipleHint: \"Mehrere Antworten möglich\",\n explanation: \"Erklärung:\",\n inputHint: \"Geben Sie Ihre Antwort ein\",\n inputPlaceholder: \"Ihre Antwort…\",\n expectedAnswer: \"Erwartete Antwort:\",\n },\n it: {\n answered: \"risposta/e\",\n validate: \"Verifica\",\n reset: \"Ricominciare\",\n correctAnswers: (n) => `rispost${n !== 1 ? \"e corrette\" : \"a corretta\"}`,\n multipleHint: \"Più risposte possibili\",\n explanation: \"Spiegazione: \",\n inputHint: \"Digita la tua risposta\",\n inputPlaceholder: \"La tua risposta…\",\n expectedAnswer: \"Risposta attesa:\",\n },\n es: {\n answered: \"respondida(s)\",\n validate: \"Validar\",\n reset: \"Reiniciar\",\n correctAnswers: (n) => `respuesta${n !== 1 ? \"s correctas\" : \" correcta\"}`,\n multipleHint: \"Varias respuestas posibles\",\n explanation: \"Explicación: \",\n inputHint: \"Escribe tu respuesta\",\n inputPlaceholder: \"Tu respuesta…\",\n expectedAnswer: \"Respuesta esperada:\",\n },\n}\n\n/**\n * Get locale strings for a given language code.\n * Falls back to English if the language is not found.\n */\nexport function getLocale(lang = \"en\"): QuizLocale {\n return locales[lang] ?? locales.en\n}\n","/**\n * Inline markdown part — result of {@link parseRichText}.\n */\nexport type RichPart = { type: \"text\" | \"bold\" | \"italic\" | \"code\"; value: string }\n\n/**\n * Parse inline markdown subsets: **bold**, *italic*, `code` → typed parts array.\n * Used for question titles, answer text, and explanations.\n */\nexport function parseRichText(text: string): RichPart[] {\n const parts: RichPart[] = []\n const re = /(\\*\\*(?!\\s)(.+?)\\*\\*)|(\\*(?!\\s)(.+?)(?<!\\s)\\*)|(`([^`]+)`)/g\n let last = 0\n let m\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) parts.push({ type: \"text\", value: text.slice(last, m.index) })\n if (m[2]) parts.push({ type: \"bold\", value: m[2] })\n else if (m[4]) parts.push({ type: \"italic\", value: m[4] })\n else if (m[6]) parts.push({ type: \"code\", value: m[6] })\n last = re.lastIndex\n }\n if (last < text.length) parts.push({ type: \"text\", value: text.slice(last) })\n return parts\n}\n\n/**\n * Normalise a typed answer for lenient comparison: case-insensitive,\n * accent-insensitive (\"Réseau\" == \"reseau\"), trimmed, whitespace collapsed.\n */\nexport function normalizeInput(text: string): string {\n return text\n .normalize(\"NFD\")\n .replace(/\\p{M}/gu, \"\")\n .trim()\n .replace(/\\s+/g, \" \")\n .toLowerCase()\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, inject, watch, onMounted, onUnmounted } from \"vue\"\nimport type { QuizData, QuizAnswer, QuizLocale, SavedAnswer } from \"./types/quiz\"\n\nconst props = defineProps({\n dataQuiz: { type: String, required: true },\n})\n\nconst rawQuiz = computed<QuizData | null>(() => {\n try {\n return JSON.parse(props.dataQuiz) as QuizData\n } catch {\n return null\n }\n})\n\n// Shuffle helper (Fisher-Yates)\nfunction shuffleArray<T>(arr: T[]): T[] {\n const a = [...arr]\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[a[i], a[j]] = [a[j], a[i]]\n }\n return a\n}\n\n// Shuffled answers — computed once at mount, re-shuffled on reset\nconst shuffledAnswers = ref<(QuizAnswer & { originalIndex: number })[]>([])\nfunction initAnswers() {\n if (!rawQuiz.value || rawQuiz.value.type === \"input\") {\n shuffledAnswers.value = []\n return\n }\n const indexed = rawQuiz.value.answers.map((a, i) => ({ ...a, originalIndex: i }))\n shuffledAnswers.value = rawQuiz.value.shuffle ? shuffleArray(indexed) : indexed\n}\n\n// Quiz data with shuffled answers\nconst quiz = computed(() => {\n if (!rawQuiz.value) return null\n return {\n ...rawQuiz.value,\n answers: shuffledAnswers.value,\n }\n})\n\nconst isInputMode = computed(() => rawQuiz.value?.type === 'input')\n\nconst correctCount = computed(() =>\n quiz.value && !isInputMode.value\n ? quiz.value.answers.filter((a) => a.correct).length\n : 0\n)\nconst isMultiple = computed(() => correctCount.value > 1)\n\n// Selected answers, by ORIGINAL index (stable across shuffles and reloads)\nconst selected = ref(new Set<number>())\n// Text input value (for fill-in-the-blank mode)\nconst inputValue = ref(\"\")\n\n// Registration with parent QuizPage\nconst register = inject<((q: { answered: typeof answered; isCorrect: typeof isCorrect }) => () => void) | null>(\"quiz-register\", null)\nconst validated = inject<ReturnType<typeof ref<boolean>>>(\"quiz-validated\", ref(false))\nconst resetCounter = inject<ReturnType<typeof ref<number>>>(\"quiz-reset\", ref(0))\nconst t = inject<QuizLocale>(\"quiz-locale\")!\n\n// Per-question persistence\nconst getQuestionId = inject<(() => number) | null>(\"quiz-question-id\", null)\nconst saveAnswer = inject<((id: number, data: SavedAnswer) => void) | null>(\"quiz-save-answer\", null)\nconst getAnswer = inject<((id: number) => SavedAnswer | null) | null>(\"quiz-get-answer\", null)\nconst myId: number | null = getQuestionId ? getQuestionId() : null\n\nconst answered = computed(() => {\n if (isInputMode.value) return inputValue.value.trim().length > 0\n return selected.value.size > 0\n})\n\nconst isCorrect = computed(() => {\n if (!quiz.value || !answered.value) return false\n\n if (isInputMode.value) {\n // Case-insensitive comparison against any accepted variant\n const input = normalizeInput(inputValue.value)\n return rawQuiz.value!.expected.some((e) => input === normalizeInput(e))\n }\n\n const correctIndices = new Set(\n rawQuiz.value!.answers\n .map((a, i) => (a.correct ? i : -1))\n .filter((i) => i >= 0)\n )\n if (selected.value.size !== correctIndices.size) return false\n for (const idx of selected.value) {\n if (!correctIndices.has(idx)) return false\n }\n return true\n})\n\n// Register this question with the parent page\nif (register && rawQuiz.value) {\n const unregister = register({ answered, isCorrect })\n onUnmounted(unregister)\n}\n\n// Watch for reset from parent\nwatch(resetCounter, () => {\n selected.value = new Set()\n inputValue.value = ''\n initAnswers()\n})\n\nonMounted(() => {\n initAnswers()\n // Restore saved answer state\n if (getAnswer && myId !== null) {\n const saved = getAnswer(myId)\n if (saved) {\n if (saved.type === \"input\" && isInputMode.value && typeof saved.input === \"string\") {\n inputValue.value = saved.input\n } else if (saved.type === \"choice\" && !isInputMode.value && Array.isArray(saved.selected)) {\n const max = rawQuiz.value?.answers.length ?? 0\n selected.value = new Set(\n saved.selected.filter((i) => Number.isInteger(i) && i >= 0 && i < max),\n )\n }\n }\n }\n})\n\nfunction toggle(orig: number) {\n if (validated.value) return\n const next = new Set(selected.value)\n if (isMultiple.value) {\n if (next.has(orig)) next.delete(orig)\n else next.add(orig)\n } else {\n next.clear()\n next.add(orig)\n }\n selected.value = next\n persistState()\n}\n\n// Save input value on change\nwatch(inputValue, () => {\n if (!validated.value) persistState()\n})\n\nfunction persistState() {\n if (!saveAnswer || myId === null) return\n if (isInputMode.value) {\n saveAnswer(myId, { type: \"input\", input: inputValue.value })\n } else {\n saveAnswer(myId, { type: \"choice\", selected: [...selected.value] })\n }\n}\n\nfunction answerClass(answer: QuizAnswer & { originalIndex: number }): string {\n const wasPicked = selected.value.has(answer.originalIndex)\n if (!validated.value) {\n return wasPicked ? \"quiz-answer selected\" : \"quiz-answer\"\n }\n if (answer.correct && wasPicked) return \"quiz-answer correct\"\n if (answer.correct && !wasPicked) return \"quiz-answer missed\"\n if (!answer.correct && wasPicked) return \"quiz-answer wrong\"\n return \"quiz-answer\"\n}\n\nconst inputClass = computed(() => {\n if (!validated.value) return \"quiz-input\"\n return isCorrect.value ? \"quiz-input correct\" : \"quiz-input wrong\"\n})\n\nimport { parseRichText, normalizeInput } from './utils'\n</script>\n\n<template>\n <fieldset v-if=\"quiz\" class=\"quiz-question\" :class=\"{ validated: validated }\" :aria-label=\"quiz.question\">\n <legend class=\"quiz-question-title\">\n <template v-for=\"(part, pi) in parseRichText(quiz.question)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </legend>\n\n <!-- Choice mode -->\n <template v-if=\"!isInputMode\">\n <p v-if=\"isMultiple\" class=\"quiz-hint\">{{ t.multipleHint }}</p>\n <ul class=\"quiz-answers\" role=\"listbox\" :aria-multiselectable=\"isMultiple\">\n <li\n v-for=\"answer in quiz.answers\"\n :key=\"answer.originalIndex\"\n :class=\"answerClass(answer)\"\n @click=\"toggle(answer.originalIndex)\"\n role=\"option\"\n :aria-selected=\"selected.has(answer.originalIndex)\"\n tabindex=\"0\"\n @keydown.enter=\"toggle(answer.originalIndex)\"\n @keydown.space.prevent=\"toggle(answer.originalIndex)\"\n >\n <span class=\"quiz-indicator\" aria-hidden=\"true\">\n <template v-if=\"isMultiple\">\n <span v-if=\"selected.has(answer.originalIndex)\" class=\"quiz-check\">✓</span>\n <span v-else class=\"quiz-empty\">☐</span>\n </template>\n <template v-else>\n <span v-if=\"selected.has(answer.originalIndex)\" class=\"quiz-check\">●</span>\n <span v-else class=\"quiz-empty\">○</span>\n </template>\n </span>\n <span class=\"quiz-answer-text\">\n <template v-for=\"(part, pi) in parseRichText(answer.text)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </span>\n </li>\n </ul>\n </template>\n\n <!-- Fill-in-the-blank mode -->\n <template v-if=\"isInputMode\">\n <p class=\"quiz-hint\">{{ t.inputHint }}</p>\n <div class=\"quiz-input-wrapper\">\n <input\n v-model=\"inputValue\"\n :class=\"inputClass\"\n type=\"text\"\n :disabled=\"validated\"\n :placeholder=\"t.inputPlaceholder\"\n :aria-label=\"quiz.question\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @keydown.enter.prevent\n />\n <p v-if=\"validated && !isCorrect\" class=\"quiz-expected\">\n {{ t.expectedAnswer }} <code>{{ rawQuiz?.expected[0] }}</code>\n </p>\n </div>\n </template>\n\n <!-- Explanation (rich text: supports **bold** and `code`) -->\n <div v-if=\"validated && quiz.explanation\" class=\"quiz-explanation\" role=\"note\">\n <strong>{{ t.explanation }}</strong>\n <template v-for=\"(part, pi) in parseRichText(quiz.explanation)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </div>\n </fieldset>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, provide, inject } from \"vue\"\nimport type { QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nconst t = inject<QuizLocale>(\"quiz-locale\")!\nconst questions = ref<QuizState[]>([])\nconst validated = ref(false)\n\n// Generate a storage key from the page path — set eagerly so children can read on mount\n// (Vue mounts children before parent, so onMounted would be too late)\nconst storageKey = typeof window !== 'undefined'\n ? 'quiz-state:' + window.location.pathname\n : ''\n\n// Restore quiz-level state immediately so validated is set before children mount\nrestoreState()\n\n// Question index counter for stable per-question storage keys\nlet questionIdx = 0\n\nprovide(\"quiz-validated\", validated)\nprovide(\"quiz-register\", (q: QuizState) => {\n questions.value.push(q)\n // returned disposer keeps counts correct on unmount (HMR, v-if)\n return () => {\n const i = questions.value.indexOf(q)\n if (i >= 0) questions.value.splice(i, 1)\n }\n})\n\n// Per-question persistence: each question gets a unique sub-key\nprovide(\"quiz-question-id\", () => questionIdx++)\nprovide(\"quiz-save-answer\", (qId: number, data: SavedAnswer) => {\n if (!storageKey) return\n try {\n sessionStorage.setItem(storageKey + \":q\" + qId, JSON.stringify(data))\n } catch { /* ignore */ }\n})\nprovide(\"quiz-get-answer\", (qId: number): SavedAnswer | null => {\n if (!storageKey) return null\n try {\n const raw = sessionStorage.getItem(storageKey + \":q\" + qId)\n return raw ? JSON.parse(raw) : null\n } catch { return null }\n})\n\n// Provide a reset trigger for child components\nconst resetCounter = ref(0)\nprovide(\"quiz-reset\", resetCounter)\n\nconst totalQuestions = computed(() => questions.value.length)\nconst answeredCount = computed(() =>\n questions.value.filter((q) => q.answered).length\n)\nconst correctCount = computed(() =>\n questions.value.filter((q) => q.isCorrect).length\n)\nconst allAnswered = computed(\n () => totalQuestions.value > 0 && answeredCount.value === totalQuestions.value,\n)\nconst progressPct = computed(() =>\n totalQuestions.value ? Math.round((answeredCount.value / totalQuestions.value) * 100) : 0\n)\n\nfunction validate() {\n validated.value = true\n saveState()\n}\n\nfunction reset() {\n validated.value = false\n resetCounter.value++\n clearState()\n}\n\n/* ── sessionStorage persistence ───────────────────────────────────── */\nfunction saveState() {\n if (!storageKey) return\n try {\n const data = { validated: validated.value, ts: Date.now() }\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n } catch { /* storage full or unavailable */ }\n}\n\nfunction restoreState() {\n if (!storageKey) return\n try {\n const raw = sessionStorage.getItem(storageKey)\n if (!raw) return\n const data = JSON.parse(raw)\n if (data && data.validated) {\n validated.value = true\n }\n } catch { /* corrupted – ignore */ }\n}\n\nfunction clearState() {\n if (!storageKey) return\n try {\n const keysToRemove: string[] = []\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i)\n if (key && key.startsWith(storageKey)) {\n keysToRemove.push(key)\n }\n }\n keysToRemove.forEach((k) => sessionStorage.removeItem(k))\n } catch { /* ignore */ }\n}\n</script>\n\n<template>\n <div class=\"quiz-page\" role=\"region\" aria-label=\"Quiz\">\n <div\nclass=\"quiz-progress-bar\" role=\"progressbar\"\n :aria-valuenow=\"answeredCount\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"totalQuestions\"\n :aria-label=\"answeredCount + ' / ' + totalQuestions + ' ' + t.answered\"\n >\n <div class=\"quiz-progress-track\">\n <div\n class=\"quiz-progress-fill\"\n :style=\"{ width: progressPct + '%' }\"\n :class=\"{ complete: progressPct === 100 }\"\n ></div>\n </div>\n <span class=\"quiz-progress-label\" aria-live=\"polite\">\n {{ answeredCount }} / {{ totalQuestions }} {{ t.answered }}\n </span>\n </div>\n\n <slot />\n\n <div class=\"quiz-actions\">\n <button\n v-if=\"!validated\"\n class=\"quiz-btn quiz-btn-validate\"\n :disabled=\"!allAnswered\"\n @click=\"validate\"\n >\n {{ t.validate }}\n </button>\n\n <template v-if=\"validated\">\n <div\nclass=\"quiz-score\" role=\"status\" aria-live=\"polite\"\n :aria-label=\"correctCount + ' / ' + totalQuestions + ' ' + t.correctAnswers(correctCount)\"\n >\n <span class=\"quiz-score-value\">{{ correctCount }} / {{ totalQuestions }}</span>\n <span class=\"quiz-score-label\">\n {{ t.correctAnswers(correctCount) }}\n ({{ totalQuestions ? Math.round((correctCount / totalQuestions) * 100) : 0 }}%)\n </span>\n </div>\n <button class=\"quiz-btn quiz-btn-reset\" @click=\"reset\">\n {{ t.reset }}\n </button>\n </template>\n </div>\n </div>\n</template>\n","import type { App } from \"vue\"\nimport { ensureStylesInjected } from \"./styleManager\"\n\nexport { quizMarkdownPlugin } from \"./md-plugin\"\nexport { getLocale, locales } from \"./locales\"\nexport type { QuizData, QuizAnswer, QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nimport QuizQuestion from \"./QuizQuestion.vue\"\nimport QuizPage from \"./QuizPage.vue\"\nimport { getLocale as _getLocale } from \"./locales\"\n\nexport { QuizQuestion, QuizPage }\n\n/**\n * Register quiz components and locale in a VitePress `enhanceApp` hook.\n *\n * @example\n * // .vitepress/theme/index.ts\n * import { enhanceAppWithQuiz } from '@matinfo/vitepress-plugin-quiz'\n * export default {\n * enhanceApp({ app }) {\n * enhanceAppWithQuiz(app) // English (default)\n * enhanceAppWithQuiz(app, 'fr') // French\n * }\n * }\n */\nexport function enhanceAppWithQuiz(app: App, lang = \"en\"): void {\n ensureStylesInjected()\n app.provide(\"quiz-locale\", _getLocale(lang))\n app.component(\"QuizQuestion\", QuizQuestion)\n app.component(\"QuizPage\", QuizPage)\n}\n"],"names":["stylesInjected","ensureStylesInjected","styleId","style","quizCssInline","QUIZ_RE","CLOSE_RE","KEY_RE","KNOWN_KEYS","quizMarkdownPlugin","md","escapeHtml","state","startLine","endLine","silent","quizBlockRule","tokens","idx","t","open","close","startPos","startMax","lineText","nextLine","found","pos","max","line","innerLines","i","data","parseQuizBlock","a","token","renderQuizComponent","lines","question","answers","explanation","expected","shuffle","append","raw","m","key","value","join","cur","extra","x","entry","json","locales","n","getLocale","lang","parseRichText","text","parts","re","last","normalizeInput","props","__props","rawQuiz","computed","shuffleArray","arr","j","shuffledAnswers","ref","initAnswers","indexed","quiz","isInputMode","correctCount","isMultiple","selected","inputValue","register","inject","validated","resetCounter","getQuestionId","saveAnswer","getAnswer","myId","answered","isCorrect","input","e","correctIndices","unregister","onUnmounted","watch","onMounted","saved","toggle","orig","next","persistState","answerClass","answer","wasPicked","inputClass","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_2","_Fragment","_renderList","part","pi","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_createTextVNode","_openBlock","_hoisted_6","$event","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","questions","storageKey","restoreState","questionIdx","provide","q","qId","totalQuestions","answeredCount","allAnswered","progressPct","validate","saveState","reset","clearState","keysToRemove","k","_hoisted_1","_renderSlot","_ctx","_hoisted_8","enhanceAppWithQuiz","app","_getLocale","QuizQuestion","QuizPage"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
4
|
-
`}const S={en:{answered:"answered",validate:"Validate",reset:"Restart",correctAnswers:t=>`correct answer${t!==1?"s":""}`,multipleHint:"Multiple answers possible",explanation:"Explanation: ",inputHint:"Type your answer",inputPlaceholder:"Your answer…",expectedAnswer:"Expected answer:"},fr:{answered:"répondue(s)",validate:"Valider",reset:"Recommencer",correctAnswers:t=>`réponse${t>1?"s":""} correcte${t>1?"s":""}`,multipleHint:"Plusieurs réponses possibles",explanation:"Explication : ",inputHint:"Saisissez votre réponse",inputPlaceholder:"Votre réponse…",expectedAnswer:"Réponse attendue :"},de:{answered:"beantwortet",validate:"Überprüfen",reset:"Neu starten",correctAnswers:t=>`richtige Antwort${t!==1?"en":""}`,multipleHint:"Mehrere Antworten möglich",explanation:"Erklärung:",inputHint:"Geben Sie Ihre Antwort ein",inputPlaceholder:"Ihre Antwort…",expectedAnswer:"Erwartete Antwort:"},it:{answered:"risposta/e",validate:"Verifica",reset:"Ricominciare",correctAnswers:t=>`rispost${t!==1?"e corrette":"a corretta"}`,multipleHint:"Più risposte possibili",explanation:"Spiegazione: ",inputHint:"Digita la tua risposta",inputPlaceholder:"La tua risposta…",expectedAnswer:"Risposta attesa:"},es:{answered:"respondida(s)",validate:"Validar",reset:"Reiniciar",correctAnswers:t=>`respuesta${t!==1?"s correctas":" correcta"}`,multipleHint:"Varias respuestas posibles",explanation:"Explicación: ",inputHint:"Escribe tu respuesta",inputPlaceholder:"Tu respuesta…",expectedAnswer:"Respuesta esperada:"}};function D(t="en"){return S[t]??S.en}function V(t){const n=[],r=/(\*\*(.+?)\*\*)|(\*(.+?)\*)|(`([^`]+)`)/g;let l=0,o;for(;(o=r.exec(t))!==null;)o.index>l&&n.push({type:"text",value:t.slice(l,o.index)}),o[2]?n.push({type:"bold",value:o[2]}):o[4]?n.push({type:"italic",value:o[4]}):o[6]&&n.push({type:"code",value:o[6]}),l=r.lastIndex;return l<t.length&&n.push({type:"text",value:t.slice(l)}),n}const L=["aria-label"],O={class:"quiz-question-title"},K={key:0},J={key:1},U={key:2},G={key:0,class:"quiz-hint"},Y=["aria-multiselectable"],Z=["onClick","aria-selected","onKeydown"],X={class:"quiz-indicator","aria-hidden":"true"},ee={key:0,class:"quiz-check"},te={key:1,class:"quiz-empty"},ne={key:0,class:"quiz-check"},re={key:1,class:"quiz-empty"},oe={class:"quiz-answer-text"},ae={key:0},ie={key:1},se={key:2},le={class:"quiz-hint"},ce={class:"quiz-input-wrapper"},pe=["disabled","placeholder","aria-label"],de={key:0,class:"quiz-expected"},ue={key:2,class:"quiz-explanation",role:"note"},fe={key:0},ge={key:1},me={key:2},Q=e.defineComponent({__name:"QuizQuestion",props:{dataQuiz:{type:String,required:!0}},setup(t){const n=t,r=e.computed(()=>{try{return JSON.parse(n.dataQuiz)}catch{return null}});function l(c){const s=[...c];for(let a=s.length-1;a>0;a--){const z=Math.floor(Math.random()*(a+1));[s[a],s[z]]=[s[z],s[a]]}return s}const o=e.ref([]);function h(){if(!r.value||r.value.type==="input"){o.value=[];return}const c=r.value.answers.map((s,a)=>({...s,originalIndex:a}));o.value=r.value.shuffle?l(c):c}const d=e.computed(()=>r.value?{...r.value,answers:o.value}:null),i=e.computed(()=>r.value?.type==="input"),m=e.computed(()=>d.value&&!i.value?d.value.answers.filter(c=>c.correct).length:0),b=e.computed(()=>m.value>1),u=e.ref(new Set),k=e.ref(""),_=e.inject("quiz-register",null),f=e.inject("quiz-validated",e.ref(!1)),v=e.inject("quiz-reset",e.ref(0)),x=e.inject("quiz-locale"),B=e.inject("quiz-question-id",null),p=e.inject("quiz-save-answer",null),g=e.inject("quiz-get-answer",null),q=B?B():null,$=e.computed(()=>i.value?k.value.trim().length>0:u.value.size>0),N=e.computed(()=>{if(!d.value||!$.value)return!1;if(i.value){const s=k.value.trim().toLowerCase();return r.value.expected.some(a=>s===a.trim().toLowerCase())}const c=new Set(d.value.answers.map((s,a)=>s.correct?a:-1).filter(s=>s>=0));if(u.value.size!==c.size)return!1;for(const s of u.value)if(!c.has(s))return!1;return!0});_&&_({answered:$,isCorrect:N}),e.watch(v,()=>{u.value=new Set,k.value="",h()}),e.onMounted(()=>{if(h(),g&&q!==null){const c=g(q);c&&(c.type==="input"?k.value=c.input:c.type==="choice"&&(u.value=new Set(c.selected)))}});function C(c){if(f.value)return;const s=new Set(u.value);b.value?s.has(c)?s.delete(c):s.add(c):(s.clear(),s.add(c)),u.value=s,M()}e.watch(k,()=>{f.value||M()});function M(){!p||q===null||(i.value?p(q,{type:"input",input:k.value}):p(q,{type:"choice",selected:[...u.value]}))}function Be(c){if(!f.value)return u.value.has(c)?"quiz-answer selected":"quiz-answer";const s=d.value.answers[c],a=u.value.has(c);return s.correct&&a?"quiz-answer correct":s.correct&&!a?"quiz-answer missed":!s.correct&&a?"quiz-answer wrong":"quiz-answer"}const Ee=e.computed(()=>f.value?N.value?"quiz-input correct":"quiz-input wrong":"quiz-input");return(c,s)=>d.value?(e.openBlock(),e.createElementBlock("fieldset",{key:0,class:e.normalizeClass(["quiz-question",{validated:e.unref(f)}]),"aria-label":d.value.question},[e.createElementVNode("legend",O,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(V)(d.value.question),(a,z)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:z},[a.type==="bold"?(e.openBlock(),e.createElementBlock("strong",K,e.toDisplayString(a.value),1)):a.type==="italic"?(e.openBlock(),e.createElementBlock("em",J,e.toDisplayString(a.value),1)):a.type==="code"?(e.openBlock(),e.createElementBlock("code",U,e.toDisplayString(a.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(a.value),1)],64))],64))),128))]),i.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[b.value?(e.openBlock(),e.createElementBlock("p",G,e.toDisplayString(e.unref(x).multipleHint),1)):e.createCommentVNode("",!0),e.createElementVNode("ul",{class:"quiz-answers",role:"listbox","aria-multiselectable":b.value},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value.answers,(a,z)=>(e.openBlock(),e.createElementBlock("li",{key:a.originalIndex,class:e.normalizeClass(Be(z)),onClick:y=>C(z),role:"option","aria-selected":u.value.has(z),tabindex:"0",onKeydown:[e.withKeys(y=>C(z),["enter"]),e.withKeys(e.withModifiers(y=>C(z),["prevent"]),["space"])]},[e.createElementVNode("span",X,[b.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[u.value.has(z)?(e.openBlock(),e.createElementBlock("span",ee,"✓")):(e.openBlock(),e.createElementBlock("span",te,"☐"))],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[u.value.has(z)?(e.openBlock(),e.createElementBlock("span",ne,"●")):(e.openBlock(),e.createElementBlock("span",re,"○"))],64))]),e.createElementVNode("span",oe,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(V)(a.text),(y,Se)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:Se},[y.type==="bold"?(e.openBlock(),e.createElementBlock("strong",ae,e.toDisplayString(y.value),1)):y.type==="italic"?(e.openBlock(),e.createElementBlock("em",ie,e.toDisplayString(y.value),1)):y.type==="code"?(e.openBlock(),e.createElementBlock("code",se,e.toDisplayString(y.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(y.value),1)],64))],64))),128))])],42,Z))),128))],8,Y)],64)),i.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("p",le,e.toDisplayString(e.unref(x).inputHint),1),e.createElementVNode("div",ce,[e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":s[0]||(s[0]=a=>k.value=a),class:e.normalizeClass(Ee.value),type:"text",disabled:e.unref(f),placeholder:e.unref(x).inputPlaceholder,"aria-label":d.value.question,autocomplete:"off",spellcheck:"false",onKeydown:s[1]||(s[1]=e.withKeys(e.withModifiers(()=>{},["prevent"]),["enter"]))},null,42,pe),[[e.vModelText,k.value]]),e.unref(f)&&!N.value?(e.openBlock(),e.createElementBlock("p",de,[e.createTextVNode(e.toDisplayString(e.unref(x).expectedAnswer)+" ",1),e.createElementVNode("code",null,e.toDisplayString(r.value?.expected[0]),1)])):e.createCommentVNode("",!0)])],64)):e.createCommentVNode("",!0),e.unref(f)&&d.value.explanation?(e.openBlock(),e.createElementBlock("div",ue,[e.createElementVNode("strong",null,e.toDisplayString(e.unref(x).explanation),1),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(V)(d.value.explanation),(a,z)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:z},[a.type==="bold"?(e.openBlock(),e.createElementBlock("strong",fe,e.toDisplayString(a.value),1)):a.type==="italic"?(e.openBlock(),e.createElementBlock("em",ge,e.toDisplayString(a.value),1)):a.type==="code"?(e.openBlock(),e.createElementBlock("code",me,e.toDisplayString(a.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(a.value),1)],64))],64))),128))])):e.createCommentVNode("",!0)],10,L)):e.createCommentVNode("",!0)}}),he={class:"quiz-page",role:"region","aria-label":"Quiz"},ke=["aria-valuenow","aria-valuemax","aria-label"],ze={class:"quiz-progress-track"},be={class:"quiz-progress-label","aria-live":"polite"},we={class:"quiz-actions"},xe=["disabled"],ye=["aria-label"],qe={class:"quiz-score-value"},_e={class:"quiz-score-label"},A=e.defineComponent({__name:"QuizPage",setup(t){const n=e.inject("quiz-locale"),r=e.ref([]),l=e.ref(!1),o=typeof window<"u"?"quiz-state:"+window.location.pathname:"";x();let h=0;e.provide("quiz-validated",l),e.provide("quiz-register",p=>{r.value.push(p)}),e.provide("quiz-question-id",()=>h++),e.provide("quiz-save-answer",(p,g)=>{if(o)try{sessionStorage.setItem(o+":q"+p,JSON.stringify(g))}catch{}}),e.provide("quiz-get-answer",p=>{if(!o)return null;try{const g=sessionStorage.getItem(o+":q"+p);return g?JSON.parse(g):null}catch{return null}});const d=e.ref(0);e.provide("quiz-reset",d);const i=e.computed(()=>r.value.length),m=e.computed(()=>r.value.filter(p=>p.answered).length),b=e.computed(()=>r.value.filter(p=>p.isCorrect).length),u=e.computed(()=>m.value===i.value),k=e.computed(()=>i.value?Math.round(m.value/i.value*100):0);function _(){l.value=!0,v()}function f(){l.value=!1,d.value++,B()}function v(){if(o)try{const p={validated:l.value,ts:Date.now()};sessionStorage.setItem(o,JSON.stringify(p))}catch{}}function x(){if(o)try{const p=sessionStorage.getItem(o);if(!p)return;const g=JSON.parse(p);g&&g.validated&&(l.value=!0)}catch{}}function B(){if(o)try{const p=[];for(let g=0;g<sessionStorage.length;g++){const q=sessionStorage.key(g);q&&q.startsWith(o)&&p.push(q)}p.forEach(g=>sessionStorage.removeItem(g))}catch{}}return(p,g)=>(e.openBlock(),e.createElementBlock("div",he,[e.createElementVNode("div",{class:"quiz-progress-bar",role:"progressbar","aria-valuenow":m.value,"aria-valuemin":0,"aria-valuemax":i.value,"aria-label":m.value+" / "+i.value+" "+e.unref(n).answered},[e.createElementVNode("div",ze,[e.createElementVNode("div",{class:e.normalizeClass(["quiz-progress-fill",{complete:k.value===100}]),style:e.normalizeStyle({width:k.value+"%"})},null,6)]),e.createElementVNode("span",be,e.toDisplayString(m.value)+" / "+e.toDisplayString(i.value)+" "+e.toDisplayString(e.unref(n).answered),1)],8,ke),e.renderSlot(p.$slots,"default"),e.createElementVNode("div",we,[l.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("button",{key:0,class:"quiz-btn quiz-btn-validate",disabled:!u.value,onClick:_},e.toDisplayString(e.unref(n).validate),9,xe)),l.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",{class:"quiz-score",role:"status","aria-live":"polite","aria-label":b.value+" / "+i.value+" "+e.unref(n).correctAnswers(b.value)},[e.createElementVNode("span",qe,e.toDisplayString(b.value)+" / "+e.toDisplayString(i.value),1),e.createElementVNode("span",_e,e.toDisplayString(e.unref(n).correctAnswers(b.value))+" ("+e.toDisplayString(i.value?Math.round(b.value/i.value*100):0)+"%) ",1)],8,ye),e.createElementVNode("button",{class:"quiz-btn quiz-btn-reset",onClick:f},e.toDisplayString(e.unref(n).reset),1)],64)):e.createCommentVNode("",!0)])]))}});function ve(t,n="en"){P(),t.provide("quiz-locale",D(n)),t.component("QuizQuestion",Q),t.component("QuizPage",A)}w.QuizPage=A,w.QuizQuestion=Q,w.enhanceAppWithQuiz=ve,w.getLocale=D,w.locales=S,w.quizMarkdownPlugin=T,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(b,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(b=typeof globalThis<"u"?globalThis:b||self,e(b.VitepressPluginQuiz={},b.Vue))})(this,(function(b,e){"use strict";const M=".quiz-progress-bar{position:sticky;top:var(--vp-nav-height, 64px);z-index:20;display:flex;align-items:center;gap:12px;margin:-20px -24px 24px;padding:12px 24px;background:var(--vp-c-bg);border-bottom:1px solid var(--vp-c-divider)}@media(max-width:959px){.quiz-progress-bar{top:calc(var(--vp-nav-height, 64px) - 16px)}}.quiz-progress-track{flex:1;height:8px;background:var(--vp-c-bg-soft);border-radius:4px;overflow:hidden}.quiz-progress-fill{height:100%;background:var(--vp-c-brand-1);border-radius:4px;transition:width .35s ease}.quiz-progress-fill.complete{background:var(--vp-c-green-1, #10b981)}.quiz-progress-label{font-size:13px;color:var(--vp-c-text-2);white-space:nowrap}.quiz-question{border:1px solid var(--vp-c-divider);border-radius:8px;padding:20px 24px;margin-bottom:16px;background:var(--vp-c-bg);transition:border-color .2s}.quiz-question-title{font-weight:600;font-size:15px;margin:0 0 6px;padding:0 5px;color:var(--vp-c-text-1);float:none;width:auto}.quiz-hint{font-size:13px;color:var(--vp-c-text-3);margin:0 0 10px;font-style:italic}.quiz-answers{list-style:none;padding:0;margin:0}.quiz-answer{display:flex;align-items:baseline;gap:10px;padding:8px 12px;margin:4px 0;border:1px solid transparent;border-radius:6px;cursor:pointer;transition:background-color .15s,border-color .15s;user-select:none;font-size:14px;color:var(--vp-c-text-1)}.quiz-answer:hover:not(.correct):not(.wrong):not(.missed){background:var(--vp-c-bg-soft)}.quiz-answer.selected{border-color:var(--vp-c-brand-1);background:var(--vp-c-brand-soft)}.quiz-answer.correct{border-color:var(--vp-c-green-1, #10b981);background:var(--vp-c-green-soft, rgba(16, 185, 129, .12));cursor:default}.quiz-answer.wrong{border-color:var(--vp-c-red-1, #ef4444);background:var(--vp-c-red-soft, rgba(239, 68, 68, .12));cursor:default}.quiz-answer.missed{border-color:var(--vp-c-yellow-1, #f59e0b);background:var(--vp-c-yellow-soft, rgba(245, 158, 11, .1));cursor:default}.validated .quiz-answer{pointer-events:none}.quiz-indicator{flex-shrink:0;width:18px;text-align:center;font-size:15px;line-height:1}.quiz-empty{color:var(--vp-c-text-3)}.quiz-check{color:var(--vp-c-brand-1)}.quiz-answer.correct .quiz-check{color:var(--vp-c-green-1, #10b981)}.quiz-answer.wrong .quiz-check{color:var(--vp-c-red-1, #ef4444)}.quiz-answer-text code{font-family:var(--vp-font-family-mono);font-size:13px;padding:1px 5px;border-radius:4px;background:var(--vp-c-bg-soft);color:var(--vp-c-text-1)}.quiz-explanation{margin-top:12px;padding:10px 14px;border-left:3px solid var(--vp-c-brand-1);background:var(--vp-c-bg-soft);border-radius:0 6px 6px 0;font-size:13px;color:var(--vp-c-text-2)}.quiz-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin-top:24px;padding-top:20px;border-top:1px solid var(--vp-c-divider)}.quiz-btn{display:inline-flex;align-items:center;justify-content:center;padding:10px 28px;font-size:14px;font-weight:600;border:1px solid transparent;border-radius:8px;cursor:pointer;transition:opacity .2s,background .2s}.quiz-btn:disabled{opacity:.45;cursor:not-allowed}.quiz-btn-validate{background:var(--vp-c-brand-1);color:var(--vp-c-white, #fff)}.quiz-btn-validate:hover:not(:disabled){opacity:.88}.quiz-btn-reset{background:var(--vp-c-bg-soft);border-color:var(--vp-c-divider);color:var(--vp-c-text-1)}.quiz-btn-reset:hover{background:var(--vp-c-bg-mute)}.quiz-score{display:flex;align-items:baseline;gap:8px}.quiz-score-value{font-size:22px;font-weight:700;color:var(--vp-c-brand-1)}.quiz-score-label{font-size:14px;color:var(--vp-c-text-2)}.quiz-input-wrapper{margin-top:8px}.quiz-input{width:100%;max-width:400px;padding:8px 12px;font-size:14px;font-family:var(--vp-font-family-mono);border:1px solid var(--vp-c-divider);border-radius:6px;background:var(--vp-c-bg);color:var(--vp-c-text-1);outline:none;transition:border-color .15s}.quiz-input:focus{border-color:var(--vp-c-brand-1)}.quiz-input:disabled{cursor:not-allowed;opacity:.8}.quiz-input.correct{border-color:var(--vp-c-green-1, #10b981);background:var(--vp-c-green-soft, rgba(16, 185, 129, .12))}.quiz-input.wrong{border-color:var(--vp-c-red-1, #ef4444);background:var(--vp-c-red-soft, rgba(239, 68, 68, .12))}.quiz-expected{margin-top:6px;font-size:13px;color:var(--vp-c-text-2)}.quiz-expected code{font-family:var(--vp-font-family-mono);font-size:13px;padding:1px 5px;border-radius:4px;background:var(--vp-c-bg-soft);color:var(--vp-c-green-1, #10b981)}";let B=!1;function P(){if(typeof document>"u"||B)return;const t="vitepress-plugin-quiz-styles";if(document.getElementById(t)){B=!0;return}const r=document.createElement("style");r.id=t,r.textContent=M,document.head.appendChild(r),B=!0}const j=/^:::quiz\s*$/,F=/^:::\s*$/,T=/^([A-Za-z-]+):(.*)$/,H=new Set(["question","answer","answer-correct","answer-input","explanation","explication","shuffle"]);function R(t){const r=t.utils.escapeHtml;t.block.ruler.before("fence","quiz",(o,c,a,z)=>K(o,c,a,z,r),{alt:["paragraph","reference","blockquote","list"]}),t.renderer.rules.quiz=(o,c)=>o[c].content,t.core.ruler.push("quiz_page_wrap",o=>{if(!o.tokens.some(p=>p.type==="quiz"))return;const a=new o.Token("html_block","",0);a.content=`<QuizPage>
|
|
2
|
+
`,a.block=!0;const z=new o.Token("html_block","",0);z.content=`</QuizPage>
|
|
3
|
+
`,z.block=!0,o.tokens.unshift(a),o.tokens.push(z)})}function K(t,r,o,c,a){if(t.sCount[r]-t.blkIndent>=4)return!1;const z=t.bMarks[r]+t.tShift[r],p=t.eMarks[r],d=t.src.slice(z,p);if(!j.test(d))return!1;if(c)return!0;let m=r+1,h=!1;for(;m<o;){const s=t.bMarks[m]+t.tShift[m],w=t.eMarks[m],q=t.src.slice(s,w);if(F.test(q)){h=!0;break}m++}if(!h)return!1;const g=[];for(let s=r+1;s<m;s++){const w=t.bMarks[s]+t.tShift[s],q=t.eMarks[s];g.push(t.src.slice(w,q))}const f=O(g);if(f?.type==="choice"&&!f.answers.some(s=>s.correct)&&console.warn(`[vitepress-plugin-quiz] :quiz block at line ${r+1} has no 'answer-correct:'; it can never be answered correctly.`),!f)return console.warn(`[vitepress-plugin-quiz] Ignored invalid :::quiz block at line ${r+1}: needs a 'question:' and at least one 'answer:'/'answer-correct:' or 'answer-input:'.`),!1;const y=t.push("quiz","",0);return y.content=J(f,a),y.map=[r,m+1],y.block=!0,t.line=m+1,!0}function O(t){let r="";const o=[];let c="";const a=[];let z=!1,p=null;for(const d of t){const m=d.trim();if(!m)continue;const h=T.exec(m),g=h?.[1].toLowerCase();if(!h||!g||!H.has(g)){p?.(m);continue}const f=h[2].trim(),y=(s,w)=>s?`${s} ${w}`:w;switch(g){case"question":r=f,p=s=>r=y(r,s);break;case"answer-correct":case"answer":{const s={text:f,correct:g==="answer-correct"};o.push(s),p=w=>s.text=y(s.text,w);break}case"answer-input":f&&a.push(f),p=null;break;case"explanation":case"explication":c=f,p=s=>c=y(c,s);break;case"shuffle":z=f.toLowerCase()==="true",p=null;break}}return r?a.length>0?{question:r,type:"input",answers:[],expected:a,explanation:c,shuffle:!1}:o.length===0?null:{question:r,type:"choice",answers:o,expected:[],explanation:c,shuffle:z}:null}function J(t,r){const o=JSON.stringify(t);return`<QuizQuestion data-quiz="${r(o)}" />
|
|
4
|
+
`}const E={en:{answered:"answered",validate:"Validate",reset:"Restart",correctAnswers:t=>`correct answer${t!==1?"s":""}`,multipleHint:"Multiple answers possible",explanation:"Explanation: ",inputHint:"Type your answer",inputPlaceholder:"Your answer…",expectedAnswer:"Expected answer:"},fr:{answered:"répondue(s)",validate:"Valider",reset:"Recommencer",correctAnswers:t=>`réponse${t>1?"s":""} correcte${t>1?"s":""}`,multipleHint:"Plusieurs réponses possibles",explanation:"Explication : ",inputHint:"Saisissez votre réponse",inputPlaceholder:"Votre réponse…",expectedAnswer:"Réponse attendue :"},de:{answered:"beantwortet",validate:"Überprüfen",reset:"Neu starten",correctAnswers:t=>`richtige Antwort${t!==1?"en":""}`,multipleHint:"Mehrere Antworten möglich",explanation:"Erklärung:",inputHint:"Geben Sie Ihre Antwort ein",inputPlaceholder:"Ihre Antwort…",expectedAnswer:"Erwartete Antwort:"},it:{answered:"risposta/e",validate:"Verifica",reset:"Ricominciare",correctAnswers:t=>`rispost${t!==1?"e corrette":"a corretta"}`,multipleHint:"Più risposte possibili",explanation:"Spiegazione: ",inputHint:"Digita la tua risposta",inputPlaceholder:"La tua risposta…",expectedAnswer:"Risposta attesa:"},es:{answered:"respondida(s)",validate:"Validar",reset:"Reiniciar",correctAnswers:t=>`respuesta${t!==1?"s correctas":" correcta"}`,multipleHint:"Varias respuestas posibles",explanation:"Explicación: ",inputHint:"Escribe tu respuesta",inputPlaceholder:"Tu respuesta…",expectedAnswer:"Respuesta esperada:"}};function C(t="en"){return E[t]??E.en}function S(t){const r=[],o=/(\*\*(?!\s)(.+?)\*\*)|(\*(?!\s)(.+?)(?<!\s)\*)|(`([^`]+)`)/g;let c=0,a;for(;(a=o.exec(t))!==null;)a.index>c&&r.push({type:"text",value:t.slice(c,a.index)}),a[2]?r.push({type:"bold",value:a[2]}):a[4]?r.push({type:"italic",value:a[4]}):a[6]&&r.push({type:"code",value:a[6]}),c=o.lastIndex;return c<t.length&&r.push({type:"text",value:t.slice(c)}),r}function I(t){return t.normalize("NFD").replace(/\p{M}/gu,"").trim().replace(/\s+/g," ").toLowerCase()}const L=["aria-label"],W={class:"quiz-question-title"},U={key:0},Y={key:1},Z={key:2},G={key:0,class:"quiz-hint"},X=["aria-multiselectable"],ee=["onClick","aria-selected","onKeydown"],te={class:"quiz-indicator","aria-hidden":"true"},ne={key:0,class:"quiz-check"},re={key:1,class:"quiz-empty"},oe={key:0,class:"quiz-check"},ae={key:1,class:"quiz-empty"},ie={class:"quiz-answer-text"},se={key:0},le={key:1},ce={key:2},pe={class:"quiz-hint"},de={class:"quiz-input-wrapper"},ue=["disabled","placeholder","aria-label"],fe={key:0,class:"quiz-expected"},ge={key:2,class:"quiz-explanation",role:"note"},me={key:0},ke={key:1},he={key:2},D=e.defineComponent({__name:"QuizQuestion",props:{dataQuiz:{type:String,required:!0}},setup(t){const r=t,o=e.computed(()=>{try{return JSON.parse(r.dataQuiz)}catch{return null}});function c(l){const i=[...l];for(let n=i.length-1;n>0;n--){const x=Math.floor(Math.random()*(n+1));[i[n],i[x]]=[i[x],i[n]]}return i}const a=e.ref([]);function z(){if(!o.value||o.value.type==="input"){a.value=[];return}const l=o.value.answers.map((i,n)=>({...i,originalIndex:n}));a.value=o.value.shuffle?c(l):l}const p=e.computed(()=>o.value?{...o.value,answers:a.value}:null),d=e.computed(()=>o.value?.type==="input"),m=e.computed(()=>p.value&&!d.value?p.value.answers.filter(l=>l.correct).length:0),h=e.computed(()=>m.value>1),g=e.ref(new Set),f=e.ref(""),y=e.inject("quiz-register",null),s=e.inject("quiz-validated",e.ref(!1)),w=e.inject("quiz-reset",e.ref(0)),q=e.inject("quiz-locale"),_=e.inject("quiz-question-id",null),u=e.inject("quiz-save-answer",null),k=e.inject("quiz-get-answer",null),v=_?_():null,$=e.computed(()=>d.value?f.value.trim().length>0:g.value.size>0),N=e.computed(()=>{if(!p.value||!$.value)return!1;if(d.value){const i=I(f.value);return o.value.expected.some(n=>i===I(n))}const l=new Set(o.value.answers.map((i,n)=>i.correct?n:-1).filter(i=>i>=0));if(g.value.size!==l.size)return!1;for(const i of g.value)if(!l.has(i))return!1;return!0});if(y&&o.value){const l=y({answered:$,isCorrect:N});e.onUnmounted(l)}e.watch(w,()=>{g.value=new Set,f.value="",z()}),e.onMounted(()=>{if(z(),k&&v!==null){const l=k(v);if(l){if(l.type==="input"&&d.value&&typeof l.input=="string")f.value=l.input;else if(l.type==="choice"&&!d.value&&Array.isArray(l.selected)){const i=o.value?.answers.length??0;g.value=new Set(l.selected.filter(n=>Number.isInteger(n)&&n>=0&&n<i))}}}});function V(l){if(s.value)return;const i=new Set(g.value);h.value?i.has(l)?i.delete(l):i.add(l):(i.clear(),i.add(l)),g.value=i,Q()}e.watch(f,()=>{s.value||Q()});function Q(){!u||v===null||(d.value?u(v,{type:"input",input:f.value}):u(v,{type:"choice",selected:[...g.value]}))}function Se(l){const i=g.value.has(l.originalIndex);return s.value?l.correct&&i?"quiz-answer correct":l.correct&&!i?"quiz-answer missed":!l.correct&&i?"quiz-answer wrong":"quiz-answer":i?"quiz-answer selected":"quiz-answer"}const Ne=e.computed(()=>s.value?N.value?"quiz-input correct":"quiz-input wrong":"quiz-input");return(l,i)=>p.value?(e.openBlock(),e.createElementBlock("fieldset",{key:0,class:e.normalizeClass(["quiz-question",{validated:e.unref(s)}]),"aria-label":p.value.question},[e.createElementVNode("legend",W,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(S)(p.value.question),(n,x)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:x},[n.type==="bold"?(e.openBlock(),e.createElementBlock("strong",U,e.toDisplayString(n.value),1)):n.type==="italic"?(e.openBlock(),e.createElementBlock("em",Y,e.toDisplayString(n.value),1)):n.type==="code"?(e.openBlock(),e.createElementBlock("code",Z,e.toDisplayString(n.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(n.value),1)],64))],64))),128))]),d.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[h.value?(e.openBlock(),e.createElementBlock("p",G,e.toDisplayString(e.unref(q).multipleHint),1)):e.createCommentVNode("",!0),e.createElementVNode("ul",{class:"quiz-answers",role:"listbox","aria-multiselectable":h.value},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(p.value.answers,n=>(e.openBlock(),e.createElementBlock("li",{key:n.originalIndex,class:e.normalizeClass(Se(n)),onClick:x=>V(n.originalIndex),role:"option","aria-selected":g.value.has(n.originalIndex),tabindex:"0",onKeydown:[e.withKeys(x=>V(n.originalIndex),["enter"]),e.withKeys(e.withModifiers(x=>V(n.originalIndex),["prevent"]),["space"])]},[e.createElementVNode("span",te,[h.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[g.value.has(n.originalIndex)?(e.openBlock(),e.createElementBlock("span",ne,"✓")):(e.openBlock(),e.createElementBlock("span",re,"☐"))],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[g.value.has(n.originalIndex)?(e.openBlock(),e.createElementBlock("span",oe,"●")):(e.openBlock(),e.createElementBlock("span",ae,"○"))],64))]),e.createElementVNode("span",ie,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(S)(n.text),(x,Ve)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:Ve},[x.type==="bold"?(e.openBlock(),e.createElementBlock("strong",se,e.toDisplayString(x.value),1)):x.type==="italic"?(e.openBlock(),e.createElementBlock("em",le,e.toDisplayString(x.value),1)):x.type==="code"?(e.openBlock(),e.createElementBlock("code",ce,e.toDisplayString(x.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(x.value),1)],64))],64))),128))])],42,ee))),128))],8,X)],64)),d.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("p",pe,e.toDisplayString(e.unref(q).inputHint),1),e.createElementVNode("div",de,[e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":i[0]||(i[0]=n=>f.value=n),class:e.normalizeClass(Ne.value),type:"text",disabled:e.unref(s),placeholder:e.unref(q).inputPlaceholder,"aria-label":p.value.question,autocomplete:"off",spellcheck:"false",onKeydown:i[1]||(i[1]=e.withKeys(e.withModifiers(()=>{},["prevent"]),["enter"]))},null,42,ue),[[e.vModelText,f.value]]),e.unref(s)&&!N.value?(e.openBlock(),e.createElementBlock("p",fe,[e.createTextVNode(e.toDisplayString(e.unref(q).expectedAnswer)+" ",1),e.createElementVNode("code",null,e.toDisplayString(o.value?.expected[0]),1)])):e.createCommentVNode("",!0)])],64)):e.createCommentVNode("",!0),e.unref(s)&&p.value.explanation?(e.openBlock(),e.createElementBlock("div",ge,[e.createElementVNode("strong",null,e.toDisplayString(e.unref(q).explanation),1),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(S)(p.value.explanation),(n,x)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:x},[n.type==="bold"?(e.openBlock(),e.createElementBlock("strong",me,e.toDisplayString(n.value),1)):n.type==="italic"?(e.openBlock(),e.createElementBlock("em",ke,e.toDisplayString(n.value),1)):n.type==="code"?(e.openBlock(),e.createElementBlock("code",he,e.toDisplayString(n.value),1)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createTextVNode(e.toDisplayString(n.value),1)],64))],64))),128))])):e.createCommentVNode("",!0)],10,L)):e.createCommentVNode("",!0)}}),xe={class:"quiz-page",role:"region","aria-label":"Quiz"},ze=["aria-valuenow","aria-valuemax","aria-label"],be={class:"quiz-progress-track"},ye={class:"quiz-progress-label","aria-live":"polite"},we={class:"quiz-actions"},qe=["disabled"],ve=["aria-label"],_e={class:"quiz-score-value"},Be={class:"quiz-score-label"},A=e.defineComponent({__name:"QuizPage",setup(t){const r=e.inject("quiz-locale"),o=e.ref([]),c=e.ref(!1),a=typeof window<"u"?"quiz-state:"+window.location.pathname:"";q();let z=0;e.provide("quiz-validated",c),e.provide("quiz-register",u=>(o.value.push(u),()=>{const k=o.value.indexOf(u);k>=0&&o.value.splice(k,1)})),e.provide("quiz-question-id",()=>z++),e.provide("quiz-save-answer",(u,k)=>{if(a)try{sessionStorage.setItem(a+":q"+u,JSON.stringify(k))}catch{}}),e.provide("quiz-get-answer",u=>{if(!a)return null;try{const k=sessionStorage.getItem(a+":q"+u);return k?JSON.parse(k):null}catch{return null}});const p=e.ref(0);e.provide("quiz-reset",p);const d=e.computed(()=>o.value.length),m=e.computed(()=>o.value.filter(u=>u.answered).length),h=e.computed(()=>o.value.filter(u=>u.isCorrect).length),g=e.computed(()=>d.value>0&&m.value===d.value),f=e.computed(()=>d.value?Math.round(m.value/d.value*100):0);function y(){c.value=!0,w()}function s(){c.value=!1,p.value++,_()}function w(){if(a)try{const u={validated:c.value,ts:Date.now()};sessionStorage.setItem(a,JSON.stringify(u))}catch{}}function q(){if(a)try{const u=sessionStorage.getItem(a);if(!u)return;const k=JSON.parse(u);k&&k.validated&&(c.value=!0)}catch{}}function _(){if(a)try{const u=[];for(let k=0;k<sessionStorage.length;k++){const v=sessionStorage.key(k);v&&v.startsWith(a)&&u.push(v)}u.forEach(k=>sessionStorage.removeItem(k))}catch{}}return(u,k)=>(e.openBlock(),e.createElementBlock("div",xe,[e.createElementVNode("div",{class:"quiz-progress-bar",role:"progressbar","aria-valuenow":m.value,"aria-valuemin":0,"aria-valuemax":d.value,"aria-label":m.value+" / "+d.value+" "+e.unref(r).answered},[e.createElementVNode("div",be,[e.createElementVNode("div",{class:e.normalizeClass(["quiz-progress-fill",{complete:f.value===100}]),style:e.normalizeStyle({width:f.value+"%"})},null,6)]),e.createElementVNode("span",ye,e.toDisplayString(m.value)+" / "+e.toDisplayString(d.value)+" "+e.toDisplayString(e.unref(r).answered),1)],8,ze),e.renderSlot(u.$slots,"default"),e.createElementVNode("div",we,[c.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("button",{key:0,class:"quiz-btn quiz-btn-validate",disabled:!g.value,onClick:y},e.toDisplayString(e.unref(r).validate),9,qe)),c.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",{class:"quiz-score",role:"status","aria-live":"polite","aria-label":h.value+" / "+d.value+" "+e.unref(r).correctAnswers(h.value)},[e.createElementVNode("span",_e,e.toDisplayString(h.value)+" / "+e.toDisplayString(d.value),1),e.createElementVNode("span",Be,e.toDisplayString(e.unref(r).correctAnswers(h.value))+" ("+e.toDisplayString(d.value?Math.round(h.value/d.value*100):0)+"%) ",1)],8,ve),e.createElementVNode("button",{class:"quiz-btn quiz-btn-reset",onClick:s},e.toDisplayString(e.unref(r).reset),1)],64)):e.createCommentVNode("",!0)])]))}});function Ee(t,r="en"){P(),t.provide("quiz-locale",C(r)),t.component("QuizQuestion",D),t.component("QuizPage",A)}b.QuizPage=A,b.QuizQuestion=D,b.enhanceAppWithQuiz=Ee,b.getLocale=C,b.locales=E,b.quizMarkdownPlugin=R,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
|
|
5
5
|
//# sourceMappingURL=vitepress-plugin-quiz.umd.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitepress-plugin-quiz.umd.cjs","sources":["../src/styleManager.ts","../src/md-plugin.ts","../src/locales.ts","../src/utils.ts","../src/QuizQuestion.vue","../src/QuizPage.vue","../src/index.ts"],"sourcesContent":["import quizCssInline from \"./quiz.css?inline\"\n\nlet stylesInjected = false\n\nexport function ensureStylesInjected(): void {\n if (typeof document === \"undefined\") return\n if (stylesInjected) return\n const styleId = \"vitepress-plugin-quiz-styles\"\n if (document.getElementById(styleId)) {\n stylesInjected = true\n return\n }\n const style = document.createElement(\"style\")\n style.id = styleId\n style.textContent = quizCssInline\n document.head.appendChild(style)\n stylesInjected = true\n}\n","import type MarkdownIt from \"markdown-it\"\nimport type StateBlock from \"markdown-it/lib/rules_block/state_block.mjs\"\nimport type { QuizData, QuizAnswer } from \"./types/quiz\"\n\nconst QUIZ_RE = /^:::quiz\\s*$/\nconst CLOSE_RE = /^:::\\s*$/\n\n/**\n * Register the quiz block rule on a markdown-it instance.\n * Use in VitePress config:\n * markdown: { config(md) { md.use(quizMarkdownPlugin) } }\n */\nexport function quizMarkdownPlugin(md: MarkdownIt): void {\n const escapeHtml = md.utils.escapeHtml\n\n md.block.ruler.before(\n \"fence\",\n \"quiz\",\n (state, startLine, endLine, silent) =>\n quizBlockRule(state, startLine, endLine, silent, escapeHtml),\n { alt: [\"paragraph\", \"reference\", \"blockquote\", \"list\"] },\n )\n\n md.renderer.rules.quiz = (tokens, idx) => tokens[idx].content\n\n // Wrap entire token stream with <QuizPage>…</QuizPage> if quiz tokens exist\n md.core.ruler.push(\"quiz_page_wrap\", (state) => {\n const hasQuiz = state.tokens.some((t) => t.type === \"quiz\")\n if (!hasQuiz) return\n\n const open = new state.Token(\"html_block\", \"\", 0)\n open.content = \"<QuizPage>\\n\"\n open.block = true\n\n const close = new state.Token(\"html_block\", \"\", 0)\n close.content = \"</QuizPage>\\n\"\n close.block = true\n\n state.tokens.unshift(open)\n state.tokens.push(close)\n })\n}\n\n/* ── block rule ──────────────────────────────────────────────────────── */\n\nfunction quizBlockRule(\n state: StateBlock,\n startLine: number,\n endLine: number,\n silent: boolean,\n escapeHtml: (s: string) => string,\n): boolean {\n const startPos = state.bMarks[startLine] + state.tShift[startLine]\n const startMax = state.eMarks[startLine]\n const lineText = state.src.slice(startPos, startMax)\n\n if (!QUIZ_RE.test(lineText)) return false\n if (silent) return true\n\n let nextLine = startLine + 1\n let found = false\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine]\n const max = state.eMarks[nextLine]\n const line = state.src.slice(pos, max)\n if (CLOSE_RE.test(line)) {\n found = true\n break\n }\n nextLine++\n }\n if (!found) return false\n\n const innerLines: string[] = []\n for (let i = startLine + 1; i < nextLine; i++) {\n const pos = state.bMarks[i] + state.tShift[i]\n const max = state.eMarks[i]\n innerLines.push(state.src.slice(pos, max))\n }\n\n const data = parseQuizBlock(innerLines)\n if (!data) return false\n\n const token = state.push(\"quiz\", \"\", 0)\n token.content = renderQuizComponent(data, escapeHtml)\n token.map = [startLine, nextLine + 1]\n token.block = true\n\n state.line = nextLine + 1\n return true\n}\n\n/* ── parser ──────────────────────────────────────────────────────────── */\n\nexport function parseQuizBlock(lines: string[]): QuizData | null {\n let question = \"\"\n const answers: QuizAnswer[] = []\n let explanation = \"\"\n const expected: string[] = []\n let shuffle = false\n\n for (const raw of lines) {\n const line = raw.trim()\n if (!line) continue\n\n if (line.startsWith(\"question:\")) {\n question = line.slice(\"question:\".length).trim()\n } else if (line.startsWith(\"answer-correct:\")) {\n answers.push({ text: line.slice(\"answer-correct:\".length).trim(), correct: true })\n } else if (line.startsWith(\"answer-input:\")) {\n expected.push(line.slice(\"answer-input:\".length).trim())\n } else if (line.startsWith(\"answer:\")) {\n answers.push({ text: line.slice(\"answer:\".length).trim(), correct: false })\n } else if (line.startsWith(\"explanation:\")) {\n explanation = line.slice(\"explanation:\".length).trim()\n } else if (line.startsWith(\"explication:\")) {\n // legacy French alias\n explanation = line.slice(\"explication:\".length).trim()\n } else if (line.startsWith(\"shuffle:\")) {\n shuffle = line.slice(\"shuffle:\".length).trim().toLowerCase() === \"true\"\n }\n }\n\n if (!question) return null\n\n if (expected.length > 0) {\n return { question, type: \"input\", answers: [], expected, explanation, shuffle: false }\n }\n\n if (answers.length === 0) return null\n return { question, type: \"choice\", answers, expected: [], explanation, shuffle }\n}\n\n/* ── renderer ────────────────────────────────────────────────────────── */\n\nfunction renderQuizComponent(data: QuizData, escapeHtml: (s: string) => string): string {\n const json = JSON.stringify(data)\n const escaped = escapeHtml(json)\n return `<QuizQuestion data-quiz=\"${escaped}\" />\\n`\n}\n","import type { QuizLocale } from \"./types/quiz\"\n\nexport const locales: Record<string, QuizLocale> = {\n en: {\n answered: \"answered\",\n validate: \"Validate\",\n reset: \"Restart\",\n correctAnswers: (n) => `correct answer${n !== 1 ? \"s\" : \"\"}`,\n multipleHint: \"Multiple answers possible\",\n explanation: \"Explanation: \",\n inputHint: \"Type your answer\",\n inputPlaceholder: \"Your answer…\",\n expectedAnswer: \"Expected answer:\",\n },\n fr: {\n answered: \"répondue(s)\",\n validate: \"Valider\",\n reset: \"Recommencer\",\n correctAnswers: (n) => `réponse${n > 1 ? \"s\" : \"\"} correcte${n > 1 ? \"s\" : \"\"}`,\n multipleHint: \"Plusieurs réponses possibles\",\n explanation: \"Explication : \",\n inputHint: \"Saisissez votre réponse\",\n inputPlaceholder: \"Votre réponse…\",\n expectedAnswer: \"Réponse attendue :\",\n },\n de: {\n answered: \"beantwortet\",\n validate: \"Überprüfen\",\n reset: \"Neu starten\",\n correctAnswers: (n) => `richtige Antwort${n !== 1 ? \"en\" : \"\"}`,\n multipleHint: \"Mehrere Antworten möglich\",\n explanation: \"Erklärung:\",\n inputHint: \"Geben Sie Ihre Antwort ein\",\n inputPlaceholder: \"Ihre Antwort…\",\n expectedAnswer: \"Erwartete Antwort:\",\n },\n it: {\n answered: \"risposta/e\",\n validate: \"Verifica\",\n reset: \"Ricominciare\",\n correctAnswers: (n) => `rispost${n !== 1 ? \"e corrette\" : \"a corretta\"}`,\n multipleHint: \"Più risposte possibili\",\n explanation: \"Spiegazione: \",\n inputHint: \"Digita la tua risposta\",\n inputPlaceholder: \"La tua risposta…\",\n expectedAnswer: \"Risposta attesa:\",\n },\n es: {\n answered: \"respondida(s)\",\n validate: \"Validar\",\n reset: \"Reiniciar\",\n correctAnswers: (n) => `respuesta${n !== 1 ? \"s correctas\" : \" correcta\"}`,\n multipleHint: \"Varias respuestas posibles\",\n explanation: \"Explicación: \",\n inputHint: \"Escribe tu respuesta\",\n inputPlaceholder: \"Tu respuesta…\",\n expectedAnswer: \"Respuesta esperada:\",\n },\n}\n\n/**\n * Get locale strings for a given language code.\n * Falls back to English if the language is not found.\n */\nexport function getLocale(lang = \"en\"): QuizLocale {\n return locales[lang] ?? locales.en\n}\n","/**\n * Inline markdown part — result of {@link parseRichText}.\n */\nexport type RichPart = { type: \"text\" | \"bold\" | \"italic\" | \"code\"; value: string }\n\n/**\n * Parse inline markdown subsets: **bold**, *italic*, `code` → typed parts array.\n * Used for question titles, answer text, and explanations.\n */\nexport function parseRichText(text: string): RichPart[] {\n const parts: RichPart[] = []\n const re = /(\\*\\*(.+?)\\*\\*)|(\\*(.+?)\\*)|(`([^`]+)`)/g\n let last = 0\n let m\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) parts.push({ type: \"text\", value: text.slice(last, m.index) })\n if (m[2]) parts.push({ type: \"bold\", value: m[2] })\n else if (m[4]) parts.push({ type: \"italic\", value: m[4] })\n else if (m[6]) parts.push({ type: \"code\", value: m[6] })\n last = re.lastIndex\n }\n if (last < text.length) parts.push({ type: \"text\", value: text.slice(last) })\n return parts\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, inject, watch, onMounted } from \"vue\"\nimport type { QuizData, QuizAnswer, QuizLocale, SavedAnswer } from \"./types/quiz\"\n\nconst props = defineProps({\n dataQuiz: { type: String, required: true },\n})\n\nconst rawQuiz = computed<QuizData | null>(() => {\n try {\n return JSON.parse(props.dataQuiz) as QuizData\n } catch {\n return null\n }\n})\n\n// Shuffle helper (Fisher-Yates)\nfunction shuffleArray<T>(arr: T[]): T[] {\n const a = [...arr]\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[a[i], a[j]] = [a[j], a[i]]\n }\n return a\n}\n\n// Shuffled answers — computed once at mount, re-shuffled on reset\nconst shuffledAnswers = ref<(QuizAnswer & { originalIndex: number })[]>([])\nfunction initAnswers() {\n if (!rawQuiz.value || rawQuiz.value.type === \"input\") {\n shuffledAnswers.value = []\n return\n }\n const indexed = rawQuiz.value.answers.map((a, i) => ({ ...a, originalIndex: i }))\n shuffledAnswers.value = rawQuiz.value.shuffle ? shuffleArray(indexed) : indexed\n}\n\n// Quiz data with shuffled answers\nconst quiz = computed(() => {\n if (!rawQuiz.value) return null\n return {\n ...rawQuiz.value,\n answers: shuffledAnswers.value,\n }\n})\n\nconst isInputMode = computed(() => rawQuiz.value?.type === 'input')\n\nconst correctCount = computed(() =>\n quiz.value && !isInputMode.value\n ? quiz.value.answers.filter((a) => a.correct).length\n : 0\n)\nconst isMultiple = computed(() => correctCount.value > 1)\n\n// Selected answer indices (for choice mode)\nconst selected = ref(new Set<number>())\n// Text input value (for fill-in-the-blank mode)\nconst inputValue = ref(\"\")\n\n// Registration with parent QuizPage\nconst register = inject<((q: { answered: typeof answered; isCorrect: typeof isCorrect }) => void) | null>(\"quiz-register\", null)\nconst validated = inject<ReturnType<typeof ref<boolean>>>(\"quiz-validated\", ref(false))\nconst resetCounter = inject<ReturnType<typeof ref<number>>>(\"quiz-reset\", ref(0))\nconst t = inject<QuizLocale>(\"quiz-locale\")!\n\n// Per-question persistence\nconst getQuestionId = inject<(() => number) | null>(\"quiz-question-id\", null)\nconst saveAnswer = inject<((id: number, data: SavedAnswer) => void) | null>(\"quiz-save-answer\", null)\nconst getAnswer = inject<((id: number) => SavedAnswer | null) | null>(\"quiz-get-answer\", null)\nconst myId: number | null = getQuestionId ? getQuestionId() : null\n\nconst answered = computed(() => {\n if (isInputMode.value) return inputValue.value.trim().length > 0\n return selected.value.size > 0\n})\n\nconst isCorrect = computed(() => {\n if (!quiz.value || !answered.value) return false\n\n if (isInputMode.value) {\n // Case-insensitive comparison against any accepted variant\n const input = inputValue.value.trim().toLowerCase()\n return rawQuiz.value!.expected.some((e) => input === e.trim().toLowerCase())\n }\n\n const correctIndices = new Set(\n quiz.value.answers\n .map((a, i) => (a.correct ? i : -1))\n .filter((i) => i >= 0)\n )\n if (selected.value.size !== correctIndices.size) return false\n for (const idx of selected.value) {\n if (!correctIndices.has(idx)) return false\n }\n return true\n})\n\n// Register this question with the parent page\nif (register) {\n register({ answered, isCorrect })\n}\n\n// Watch for reset from parent\nwatch(resetCounter, () => {\n selected.value = new Set()\n inputValue.value = ''\n initAnswers()\n})\n\nonMounted(() => {\n initAnswers()\n // Restore saved answer state\n if (getAnswer && myId !== null) {\n const saved = getAnswer(myId)\n if (saved) {\n if (saved.type === \"input\") {\n inputValue.value = saved.input\n } else if (saved.type === \"choice\") {\n selected.value = new Set(saved.selected)\n }\n }\n }\n})\n\nfunction toggle(idx: number) {\n if (validated.value) return\n const next = new Set(selected.value)\n if (isMultiple.value) {\n if (next.has(idx)) next.delete(idx)\n else next.add(idx)\n } else {\n next.clear()\n next.add(idx)\n }\n selected.value = next\n persistState()\n}\n\n// Save input value on change\nwatch(inputValue, () => {\n if (!validated.value) persistState()\n})\n\nfunction persistState() {\n if (!saveAnswer || myId === null) return\n if (isInputMode.value) {\n saveAnswer(myId, { type: \"input\", input: inputValue.value })\n } else {\n saveAnswer(myId, { type: \"choice\", selected: [...selected.value] })\n }\n}\n\nfunction answerClass(idx: number): string {\n if (!validated.value) {\n return selected.value.has(idx) ? \"quiz-answer selected\" : \"quiz-answer\"\n }\n const answer = quiz.value!.answers[idx]\n const wasPicked = selected.value.has(idx)\n if (answer.correct && wasPicked) return \"quiz-answer correct\"\n if (answer.correct && !wasPicked) return \"quiz-answer missed\"\n if (!answer.correct && wasPicked) return \"quiz-answer wrong\"\n return \"quiz-answer\"\n}\n\nconst inputClass = computed(() => {\n if (!validated.value) return \"quiz-input\"\n return isCorrect.value ? \"quiz-input correct\" : \"quiz-input wrong\"\n})\n\nimport { parseRichText, type RichPart } from './utils'\n</script>\n\n<template>\n <fieldset v-if=\"quiz\" class=\"quiz-question\" :class=\"{ validated: validated }\" :aria-label=\"quiz.question\">\n <legend class=\"quiz-question-title\">\n <template v-for=\"(part, pi) in parseRichText(quiz.question)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </legend>\n\n <!-- Choice mode -->\n <template v-if=\"!isInputMode\">\n <p v-if=\"isMultiple\" class=\"quiz-hint\">{{ t.multipleHint }}</p>\n <ul class=\"quiz-answers\" role=\"listbox\" :aria-multiselectable=\"isMultiple\">\n <li\n v-for=\"(answer, idx) in quiz.answers\"\n :key=\"answer.originalIndex\"\n :class=\"answerClass(idx)\"\n @click=\"toggle(idx)\"\n role=\"option\"\n :aria-selected=\"selected.has(idx)\"\n tabindex=\"0\"\n @keydown.enter=\"toggle(idx)\"\n @keydown.space.prevent=\"toggle(idx)\"\n >\n <span class=\"quiz-indicator\" aria-hidden=\"true\">\n <template v-if=\"isMultiple\">\n <span v-if=\"selected.has(idx)\" class=\"quiz-check\">✓</span>\n <span v-else class=\"quiz-empty\">☐</span>\n </template>\n <template v-else>\n <span v-if=\"selected.has(idx)\" class=\"quiz-check\">●</span>\n <span v-else class=\"quiz-empty\">○</span>\n </template>\n </span>\n <span class=\"quiz-answer-text\">\n <template v-for=\"(part, pi) in parseRichText(answer.text)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </span>\n </li>\n </ul>\n </template>\n\n <!-- Fill-in-the-blank mode -->\n <template v-if=\"isInputMode\">\n <p class=\"quiz-hint\">{{ t.inputHint }}</p>\n <div class=\"quiz-input-wrapper\">\n <input\n v-model=\"inputValue\"\n :class=\"inputClass\"\n type=\"text\"\n :disabled=\"validated\"\n :placeholder=\"t.inputPlaceholder\"\n :aria-label=\"quiz.question\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @keydown.enter.prevent\n />\n <p v-if=\"validated && !isCorrect\" class=\"quiz-expected\">\n {{ t.expectedAnswer }} <code>{{ rawQuiz?.expected[0] }}</code>\n </p>\n </div>\n </template>\n\n <!-- Explanation (rich text: supports **bold** and `code`) -->\n <div v-if=\"validated && quiz.explanation\" class=\"quiz-explanation\" role=\"note\">\n <strong>{{ t.explanation }}</strong>\n <template v-for=\"(part, pi) in parseRichText(quiz.explanation)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </div>\n </fieldset>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, provide, inject } from \"vue\"\nimport type { QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nconst t = inject<QuizLocale>(\"quiz-locale\")!\nconst questions = ref<QuizState[]>([])\nconst validated = ref(false)\n\n// Generate a storage key from the page path — set eagerly so children can read on mount\n// (Vue mounts children before parent, so onMounted would be too late)\nconst storageKey = typeof window !== 'undefined'\n ? 'quiz-state:' + window.location.pathname\n : ''\n\n// Restore quiz-level state immediately so validated is set before children mount\nrestoreState()\n\n// Question index counter for stable per-question storage keys\nlet questionIdx = 0\n\nprovide(\"quiz-validated\", validated)\nprovide(\"quiz-register\", (q: QuizState) => {\n questions.value.push(q)\n})\n\n// Per-question persistence: each question gets a unique sub-key\nprovide(\"quiz-question-id\", () => questionIdx++)\nprovide(\"quiz-save-answer\", (qId: number, data: SavedAnswer) => {\n if (!storageKey) return\n try {\n sessionStorage.setItem(storageKey + \":q\" + qId, JSON.stringify(data))\n } catch { /* ignore */ }\n})\nprovide(\"quiz-get-answer\", (qId: number): SavedAnswer | null => {\n if (!storageKey) return null\n try {\n const raw = sessionStorage.getItem(storageKey + \":q\" + qId)\n return raw ? JSON.parse(raw) : null\n } catch { return null }\n})\n\n// Provide a reset trigger for child components\nconst resetCounter = ref(0)\nprovide(\"quiz-reset\", resetCounter)\n\nconst totalQuestions = computed(() => questions.value.length)\nconst answeredCount = computed(() =>\n questions.value.filter((q) => q.answered).length\n)\nconst correctCount = computed(() =>\n questions.value.filter((q) => q.isCorrect).length\n)\nconst allAnswered = computed(() => answeredCount.value === totalQuestions.value)\nconst progressPct = computed(() =>\n totalQuestions.value ? Math.round((answeredCount.value / totalQuestions.value) * 100) : 0\n)\n\nfunction validate() {\n validated.value = true\n saveState()\n}\n\nfunction reset() {\n validated.value = false\n resetCounter.value++\n clearState()\n}\n\n/* ── sessionStorage persistence ───────────────────────────────────── */\nfunction saveState() {\n if (!storageKey) return\n try {\n const data = { validated: validated.value, ts: Date.now() }\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n } catch { /* storage full or unavailable */ }\n}\n\nfunction restoreState() {\n if (!storageKey) return\n try {\n const raw = sessionStorage.getItem(storageKey)\n if (!raw) return\n const data = JSON.parse(raw)\n if (data && data.validated) {\n validated.value = true\n }\n } catch { /* corrupted – ignore */ }\n}\n\nfunction clearState() {\n if (!storageKey) return\n try {\n const keysToRemove: string[] = []\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i)\n if (key && key.startsWith(storageKey)) {\n keysToRemove.push(key)\n }\n }\n keysToRemove.forEach((k) => sessionStorage.removeItem(k))\n } catch { /* ignore */ }\n}\n</script>\n\n<template>\n <div class=\"quiz-page\" role=\"region\" aria-label=\"Quiz\">\n <div\nclass=\"quiz-progress-bar\" role=\"progressbar\"\n :aria-valuenow=\"answeredCount\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"totalQuestions\"\n :aria-label=\"answeredCount + ' / ' + totalQuestions + ' ' + t.answered\"\n >\n <div class=\"quiz-progress-track\">\n <div\n class=\"quiz-progress-fill\"\n :style=\"{ width: progressPct + '%' }\"\n :class=\"{ complete: progressPct === 100 }\"\n ></div>\n </div>\n <span class=\"quiz-progress-label\" aria-live=\"polite\">\n {{ answeredCount }} / {{ totalQuestions }} {{ t.answered }}\n </span>\n </div>\n\n <slot />\n\n <div class=\"quiz-actions\">\n <button\n v-if=\"!validated\"\n class=\"quiz-btn quiz-btn-validate\"\n :disabled=\"!allAnswered\"\n @click=\"validate\"\n >\n {{ t.validate }}\n </button>\n\n <template v-if=\"validated\">\n <div\nclass=\"quiz-score\" role=\"status\" aria-live=\"polite\"\n :aria-label=\"correctCount + ' / ' + totalQuestions + ' ' + t.correctAnswers(correctCount)\"\n >\n <span class=\"quiz-score-value\">{{ correctCount }} / {{ totalQuestions }}</span>\n <span class=\"quiz-score-label\">\n {{ t.correctAnswers(correctCount) }}\n ({{ totalQuestions ? Math.round((correctCount / totalQuestions) * 100) : 0 }}%)\n </span>\n </div>\n <button class=\"quiz-btn quiz-btn-reset\" @click=\"reset\">\n {{ t.reset }}\n </button>\n </template>\n </div>\n </div>\n</template>\n","import type { App } from \"vue\"\nimport { ensureStylesInjected } from \"./styleManager\"\n\nexport { quizMarkdownPlugin } from \"./md-plugin\"\nexport { getLocale, locales } from \"./locales\"\nexport type { QuizData, QuizAnswer, QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nimport QuizQuestion from \"./QuizQuestion.vue\"\nimport QuizPage from \"./QuizPage.vue\"\nimport { getLocale as _getLocale } from \"./locales\"\n\nexport { QuizQuestion, QuizPage }\n\n/**\n * Register quiz components and locale in a VitePress `enhanceApp` hook.\n *\n * @example\n * // .vitepress/theme/index.ts\n * import { enhanceAppWithQuiz } from '@matinfo/vitepress-plugin-quiz'\n * export default {\n * enhanceApp({ app }) {\n * enhanceAppWithQuiz(app) // English (default)\n * enhanceAppWithQuiz(app, 'fr') // French\n * }\n * }\n */\nexport function enhanceAppWithQuiz(app: App, lang = \"en\"): void {\n ensureStylesInjected()\n app.provide(\"quiz-locale\", _getLocale(lang))\n app.component(\"QuizQuestion\", QuizQuestion)\n app.component(\"QuizPage\", QuizPage)\n}\n"],"names":["stylesInjected","ensureStylesInjected","styleId","style","quizCssInline","QUIZ_RE","CLOSE_RE","quizMarkdownPlugin","md","escapeHtml","state","startLine","endLine","silent","quizBlockRule","tokens","idx","t","open","close","startPos","startMax","lineText","nextLine","found","pos","max","line","innerLines","i","data","parseQuizBlock","token","renderQuizComponent","lines","question","answers","explanation","expected","shuffle","raw","json","locales","n","getLocale","lang","parseRichText","text","parts","re","last","m","props","__props","rawQuiz","computed","shuffleArray","arr","a","j","shuffledAnswers","ref","initAnswers","indexed","quiz","isInputMode","correctCount","isMultiple","selected","inputValue","register","inject","validated","resetCounter","getQuestionId","saveAnswer","getAnswer","myId","answered","isCorrect","input","e","correctIndices","watch","onMounted","saved","toggle","next","persistState","answerClass","answer","wasPicked","inputClass","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_2","_Fragment","_renderList","part","pi","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_createTextVNode","_openBlock","_hoisted_6","$event","_withKeys","_withModifiers","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","questions","storageKey","restoreState","questionIdx","provide","q","qId","totalQuestions","answeredCount","allAnswered","progressPct","validate","saveState","reset","clearState","keysToRemove","key","k","_hoisted_1","_renderSlot","_ctx","_hoisted_8","enhanceAppWithQuiz","app","_getLocale","QuizQuestion","QuizPage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vitepress-plugin-quiz.umd.cjs","sources":["../src/styleManager.ts","../src/md-plugin.ts","../src/locales.ts","../src/utils.ts","../src/QuizQuestion.vue","../src/QuizPage.vue","../src/index.ts"],"sourcesContent":["import quizCssInline from \"./quiz.css?inline\"\n\nlet stylesInjected = false\n\nexport function ensureStylesInjected(): void {\n if (typeof document === \"undefined\") return\n if (stylesInjected) return\n const styleId = \"vitepress-plugin-quiz-styles\"\n if (document.getElementById(styleId)) {\n stylesInjected = true\n return\n }\n const style = document.createElement(\"style\")\n style.id = styleId\n style.textContent = quizCssInline\n document.head.appendChild(style)\n stylesInjected = true\n}\n","import type MarkdownIt from \"markdown-it\"\nimport type StateBlock from \"markdown-it/lib/rules_block/state_block.mjs\"\nimport type { QuizData, QuizAnswer } from \"./types/quiz\"\n\nconst QUIZ_RE = /^:::quiz\\s*$/\nconst CLOSE_RE = /^:::\\s*$/\nconst KEY_RE = /^([A-Za-z-]+):(.*)$/\nconst KNOWN_KEYS = new Set([\n \"question\",\n \"answer\",\n \"answer-correct\",\n \"answer-input\",\n \"explanation\",\n \"explication\",\n \"shuffle\",\n])\n\n/**\n * Register the quiz block rule on a markdown-it instance.\n * Use in VitePress config:\n * markdown: { config(md) { md.use(quizMarkdownPlugin) } }\n */\nexport function quizMarkdownPlugin(md: MarkdownIt): void {\n const escapeHtml = md.utils.escapeHtml\n\n md.block.ruler.before(\n \"fence\",\n \"quiz\",\n (state, startLine, endLine, silent) =>\n quizBlockRule(state, startLine, endLine, silent, escapeHtml),\n { alt: [\"paragraph\", \"reference\", \"blockquote\", \"list\"] },\n )\n\n md.renderer.rules.quiz = (tokens, idx) => tokens[idx].content\n\n // Wrap entire token stream with <QuizPage>…</QuizPage> if quiz tokens exist\n md.core.ruler.push(\"quiz_page_wrap\", (state) => {\n const hasQuiz = state.tokens.some((t) => t.type === \"quiz\")\n if (!hasQuiz) return\n\n const open = new state.Token(\"html_block\", \"\", 0)\n open.content = \"<QuizPage>\\n\"\n open.block = true\n\n const close = new state.Token(\"html_block\", \"\", 0)\n close.content = \"</QuizPage>\\n\"\n close.block = true\n\n state.tokens.unshift(open)\n state.tokens.push(close)\n })\n}\n\n/* ── block rule ──────────────────────────────────────────────────────── */\n\nfunction quizBlockRule(\n state: StateBlock,\n startLine: number,\n endLine: number,\n silent: boolean,\n escapeHtml: (s: string) => string,\n): boolean {\n // 4+ spaces of indentation is an indented code block, not a quiz\n if (state.sCount[startLine] - state.blkIndent >= 4) return false\n\n const startPos = state.bMarks[startLine] + state.tShift[startLine]\n const startMax = state.eMarks[startLine]\n const lineText = state.src.slice(startPos, startMax)\n\n if (!QUIZ_RE.test(lineText)) return false\n if (silent) return true\n\n let nextLine = startLine + 1\n let found = false\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine]\n const max = state.eMarks[nextLine]\n const line = state.src.slice(pos, max)\n if (CLOSE_RE.test(line)) {\n found = true\n break\n }\n nextLine++\n }\n if (!found) return false\n\n const innerLines: string[] = []\n for (let i = startLine + 1; i < nextLine; i++) {\n const pos = state.bMarks[i] + state.tShift[i]\n const max = state.eMarks[i]\n innerLines.push(state.src.slice(pos, max))\n }\n\n const data = parseQuizBlock(innerLines)\n if (data?.type === \"choice\" && !data.answers.some((a) => a.correct)) {\n console.warn(\n `[vitepress-plugin-quiz] :quiz block at line ${startLine + 1} has no 'answer-correct:'; ` +\n \"it can never be answered correctly.\",\n )\n }\n if (!data) {\n console.warn(\n `[vitepress-plugin-quiz] Ignored invalid :::quiz block at line ${startLine + 1}: ` +\n \"needs a 'question:' and at least one 'answer:'/'answer-correct:' or 'answer-input:'.\",\n )\n return false\n }\n\n const token = state.push(\"quiz\", \"\", 0)\n token.content = renderQuizComponent(data, escapeHtml)\n token.map = [startLine, nextLine + 1]\n token.block = true\n\n state.line = nextLine + 1\n return true\n}\n\n/* ── parser ──────────────────────────────────────────────────────────── */\n\nexport function parseQuizBlock(lines: string[]): QuizData | null {\n let question = \"\"\n const answers: QuizAnswer[] = []\n let explanation = \"\"\n const expected: string[] = []\n let shuffle = false\n\n // Lines that are not a known `key:` continue the previous field (multi-line values).\n let append: ((extra: string) => void) | null = null\n\n for (const raw of lines) {\n const line = raw.trim()\n if (!line) continue\n\n const m = KEY_RE.exec(line)\n const key = m?.[1].toLowerCase()\n if (!m || !key || !KNOWN_KEYS.has(key)) {\n append?.(line)\n continue\n }\n const value = m[2].trim()\n const join = (cur: string, extra: string) => (cur ? `${cur} ${extra}` : extra)\n\n switch (key) {\n case \"question\":\n question = value\n append = (x) => (question = join(question, x))\n break\n case \"answer-correct\":\n case \"answer\": {\n const entry: QuizAnswer = { text: value, correct: key === \"answer-correct\" }\n answers.push(entry)\n append = (x) => (entry.text = join(entry.text, x))\n break\n }\n case \"answer-input\":\n if (value) expected.push(value)\n append = null\n break\n case \"explanation\":\n case \"explication\": // legacy French alias\n explanation = value\n append = (x) => (explanation = join(explanation, x))\n break\n case \"shuffle\":\n shuffle = value.toLowerCase() === \"true\"\n append = null\n break\n }\n }\n\n if (!question) return null\n\n if (expected.length > 0) {\n return { question, type: \"input\", answers: [], expected, explanation, shuffle: false }\n }\n\n if (answers.length === 0) return null\n return { question, type: \"choice\", answers, expected: [], explanation, shuffle }\n}\n\n/* ── renderer ────────────────────────────────────────────────────────── */\n\nfunction renderQuizComponent(data: QuizData, escapeHtml: (s: string) => string): string {\n const json = JSON.stringify(data)\n const escaped = escapeHtml(json)\n return `<QuizQuestion data-quiz=\"${escaped}\" />\\n`\n}\n","import type { QuizLocale } from \"./types/quiz\"\n\nexport const locales: Record<string, QuizLocale> = {\n en: {\n answered: \"answered\",\n validate: \"Validate\",\n reset: \"Restart\",\n correctAnswers: (n) => `correct answer${n !== 1 ? \"s\" : \"\"}`,\n multipleHint: \"Multiple answers possible\",\n explanation: \"Explanation: \",\n inputHint: \"Type your answer\",\n inputPlaceholder: \"Your answer…\",\n expectedAnswer: \"Expected answer:\",\n },\n fr: {\n answered: \"répondue(s)\",\n validate: \"Valider\",\n reset: \"Recommencer\",\n correctAnswers: (n) => `réponse${n > 1 ? \"s\" : \"\"} correcte${n > 1 ? \"s\" : \"\"}`,\n multipleHint: \"Plusieurs réponses possibles\",\n explanation: \"Explication : \",\n inputHint: \"Saisissez votre réponse\",\n inputPlaceholder: \"Votre réponse…\",\n expectedAnswer: \"Réponse attendue :\",\n },\n de: {\n answered: \"beantwortet\",\n validate: \"Überprüfen\",\n reset: \"Neu starten\",\n correctAnswers: (n) => `richtige Antwort${n !== 1 ? \"en\" : \"\"}`,\n multipleHint: \"Mehrere Antworten möglich\",\n explanation: \"Erklärung:\",\n inputHint: \"Geben Sie Ihre Antwort ein\",\n inputPlaceholder: \"Ihre Antwort…\",\n expectedAnswer: \"Erwartete Antwort:\",\n },\n it: {\n answered: \"risposta/e\",\n validate: \"Verifica\",\n reset: \"Ricominciare\",\n correctAnswers: (n) => `rispost${n !== 1 ? \"e corrette\" : \"a corretta\"}`,\n multipleHint: \"Più risposte possibili\",\n explanation: \"Spiegazione: \",\n inputHint: \"Digita la tua risposta\",\n inputPlaceholder: \"La tua risposta…\",\n expectedAnswer: \"Risposta attesa:\",\n },\n es: {\n answered: \"respondida(s)\",\n validate: \"Validar\",\n reset: \"Reiniciar\",\n correctAnswers: (n) => `respuesta${n !== 1 ? \"s correctas\" : \" correcta\"}`,\n multipleHint: \"Varias respuestas posibles\",\n explanation: \"Explicación: \",\n inputHint: \"Escribe tu respuesta\",\n inputPlaceholder: \"Tu respuesta…\",\n expectedAnswer: \"Respuesta esperada:\",\n },\n}\n\n/**\n * Get locale strings for a given language code.\n * Falls back to English if the language is not found.\n */\nexport function getLocale(lang = \"en\"): QuizLocale {\n return locales[lang] ?? locales.en\n}\n","/**\n * Inline markdown part — result of {@link parseRichText}.\n */\nexport type RichPart = { type: \"text\" | \"bold\" | \"italic\" | \"code\"; value: string }\n\n/**\n * Parse inline markdown subsets: **bold**, *italic*, `code` → typed parts array.\n * Used for question titles, answer text, and explanations.\n */\nexport function parseRichText(text: string): RichPart[] {\n const parts: RichPart[] = []\n const re = /(\\*\\*(?!\\s)(.+?)\\*\\*)|(\\*(?!\\s)(.+?)(?<!\\s)\\*)|(`([^`]+)`)/g\n let last = 0\n let m\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) parts.push({ type: \"text\", value: text.slice(last, m.index) })\n if (m[2]) parts.push({ type: \"bold\", value: m[2] })\n else if (m[4]) parts.push({ type: \"italic\", value: m[4] })\n else if (m[6]) parts.push({ type: \"code\", value: m[6] })\n last = re.lastIndex\n }\n if (last < text.length) parts.push({ type: \"text\", value: text.slice(last) })\n return parts\n}\n\n/**\n * Normalise a typed answer for lenient comparison: case-insensitive,\n * accent-insensitive (\"Réseau\" == \"reseau\"), trimmed, whitespace collapsed.\n */\nexport function normalizeInput(text: string): string {\n return text\n .normalize(\"NFD\")\n .replace(/\\p{M}/gu, \"\")\n .trim()\n .replace(/\\s+/g, \" \")\n .toLowerCase()\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, inject, watch, onMounted, onUnmounted } from \"vue\"\nimport type { QuizData, QuizAnswer, QuizLocale, SavedAnswer } from \"./types/quiz\"\n\nconst props = defineProps({\n dataQuiz: { type: String, required: true },\n})\n\nconst rawQuiz = computed<QuizData | null>(() => {\n try {\n return JSON.parse(props.dataQuiz) as QuizData\n } catch {\n return null\n }\n})\n\n// Shuffle helper (Fisher-Yates)\nfunction shuffleArray<T>(arr: T[]): T[] {\n const a = [...arr]\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[a[i], a[j]] = [a[j], a[i]]\n }\n return a\n}\n\n// Shuffled answers — computed once at mount, re-shuffled on reset\nconst shuffledAnswers = ref<(QuizAnswer & { originalIndex: number })[]>([])\nfunction initAnswers() {\n if (!rawQuiz.value || rawQuiz.value.type === \"input\") {\n shuffledAnswers.value = []\n return\n }\n const indexed = rawQuiz.value.answers.map((a, i) => ({ ...a, originalIndex: i }))\n shuffledAnswers.value = rawQuiz.value.shuffle ? shuffleArray(indexed) : indexed\n}\n\n// Quiz data with shuffled answers\nconst quiz = computed(() => {\n if (!rawQuiz.value) return null\n return {\n ...rawQuiz.value,\n answers: shuffledAnswers.value,\n }\n})\n\nconst isInputMode = computed(() => rawQuiz.value?.type === 'input')\n\nconst correctCount = computed(() =>\n quiz.value && !isInputMode.value\n ? quiz.value.answers.filter((a) => a.correct).length\n : 0\n)\nconst isMultiple = computed(() => correctCount.value > 1)\n\n// Selected answers, by ORIGINAL index (stable across shuffles and reloads)\nconst selected = ref(new Set<number>())\n// Text input value (for fill-in-the-blank mode)\nconst inputValue = ref(\"\")\n\n// Registration with parent QuizPage\nconst register = inject<((q: { answered: typeof answered; isCorrect: typeof isCorrect }) => () => void) | null>(\"quiz-register\", null)\nconst validated = inject<ReturnType<typeof ref<boolean>>>(\"quiz-validated\", ref(false))\nconst resetCounter = inject<ReturnType<typeof ref<number>>>(\"quiz-reset\", ref(0))\nconst t = inject<QuizLocale>(\"quiz-locale\")!\n\n// Per-question persistence\nconst getQuestionId = inject<(() => number) | null>(\"quiz-question-id\", null)\nconst saveAnswer = inject<((id: number, data: SavedAnswer) => void) | null>(\"quiz-save-answer\", null)\nconst getAnswer = inject<((id: number) => SavedAnswer | null) | null>(\"quiz-get-answer\", null)\nconst myId: number | null = getQuestionId ? getQuestionId() : null\n\nconst answered = computed(() => {\n if (isInputMode.value) return inputValue.value.trim().length > 0\n return selected.value.size > 0\n})\n\nconst isCorrect = computed(() => {\n if (!quiz.value || !answered.value) return false\n\n if (isInputMode.value) {\n // Case-insensitive comparison against any accepted variant\n const input = normalizeInput(inputValue.value)\n return rawQuiz.value!.expected.some((e) => input === normalizeInput(e))\n }\n\n const correctIndices = new Set(\n rawQuiz.value!.answers\n .map((a, i) => (a.correct ? i : -1))\n .filter((i) => i >= 0)\n )\n if (selected.value.size !== correctIndices.size) return false\n for (const idx of selected.value) {\n if (!correctIndices.has(idx)) return false\n }\n return true\n})\n\n// Register this question with the parent page\nif (register && rawQuiz.value) {\n const unregister = register({ answered, isCorrect })\n onUnmounted(unregister)\n}\n\n// Watch for reset from parent\nwatch(resetCounter, () => {\n selected.value = new Set()\n inputValue.value = ''\n initAnswers()\n})\n\nonMounted(() => {\n initAnswers()\n // Restore saved answer state\n if (getAnswer && myId !== null) {\n const saved = getAnswer(myId)\n if (saved) {\n if (saved.type === \"input\" && isInputMode.value && typeof saved.input === \"string\") {\n inputValue.value = saved.input\n } else if (saved.type === \"choice\" && !isInputMode.value && Array.isArray(saved.selected)) {\n const max = rawQuiz.value?.answers.length ?? 0\n selected.value = new Set(\n saved.selected.filter((i) => Number.isInteger(i) && i >= 0 && i < max),\n )\n }\n }\n }\n})\n\nfunction toggle(orig: number) {\n if (validated.value) return\n const next = new Set(selected.value)\n if (isMultiple.value) {\n if (next.has(orig)) next.delete(orig)\n else next.add(orig)\n } else {\n next.clear()\n next.add(orig)\n }\n selected.value = next\n persistState()\n}\n\n// Save input value on change\nwatch(inputValue, () => {\n if (!validated.value) persistState()\n})\n\nfunction persistState() {\n if (!saveAnswer || myId === null) return\n if (isInputMode.value) {\n saveAnswer(myId, { type: \"input\", input: inputValue.value })\n } else {\n saveAnswer(myId, { type: \"choice\", selected: [...selected.value] })\n }\n}\n\nfunction answerClass(answer: QuizAnswer & { originalIndex: number }): string {\n const wasPicked = selected.value.has(answer.originalIndex)\n if (!validated.value) {\n return wasPicked ? \"quiz-answer selected\" : \"quiz-answer\"\n }\n if (answer.correct && wasPicked) return \"quiz-answer correct\"\n if (answer.correct && !wasPicked) return \"quiz-answer missed\"\n if (!answer.correct && wasPicked) return \"quiz-answer wrong\"\n return \"quiz-answer\"\n}\n\nconst inputClass = computed(() => {\n if (!validated.value) return \"quiz-input\"\n return isCorrect.value ? \"quiz-input correct\" : \"quiz-input wrong\"\n})\n\nimport { parseRichText, normalizeInput } from './utils'\n</script>\n\n<template>\n <fieldset v-if=\"quiz\" class=\"quiz-question\" :class=\"{ validated: validated }\" :aria-label=\"quiz.question\">\n <legend class=\"quiz-question-title\">\n <template v-for=\"(part, pi) in parseRichText(quiz.question)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </legend>\n\n <!-- Choice mode -->\n <template v-if=\"!isInputMode\">\n <p v-if=\"isMultiple\" class=\"quiz-hint\">{{ t.multipleHint }}</p>\n <ul class=\"quiz-answers\" role=\"listbox\" :aria-multiselectable=\"isMultiple\">\n <li\n v-for=\"answer in quiz.answers\"\n :key=\"answer.originalIndex\"\n :class=\"answerClass(answer)\"\n @click=\"toggle(answer.originalIndex)\"\n role=\"option\"\n :aria-selected=\"selected.has(answer.originalIndex)\"\n tabindex=\"0\"\n @keydown.enter=\"toggle(answer.originalIndex)\"\n @keydown.space.prevent=\"toggle(answer.originalIndex)\"\n >\n <span class=\"quiz-indicator\" aria-hidden=\"true\">\n <template v-if=\"isMultiple\">\n <span v-if=\"selected.has(answer.originalIndex)\" class=\"quiz-check\">✓</span>\n <span v-else class=\"quiz-empty\">☐</span>\n </template>\n <template v-else>\n <span v-if=\"selected.has(answer.originalIndex)\" class=\"quiz-check\">●</span>\n <span v-else class=\"quiz-empty\">○</span>\n </template>\n </span>\n <span class=\"quiz-answer-text\">\n <template v-for=\"(part, pi) in parseRichText(answer.text)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </span>\n </li>\n </ul>\n </template>\n\n <!-- Fill-in-the-blank mode -->\n <template v-if=\"isInputMode\">\n <p class=\"quiz-hint\">{{ t.inputHint }}</p>\n <div class=\"quiz-input-wrapper\">\n <input\n v-model=\"inputValue\"\n :class=\"inputClass\"\n type=\"text\"\n :disabled=\"validated\"\n :placeholder=\"t.inputPlaceholder\"\n :aria-label=\"quiz.question\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @keydown.enter.prevent\n />\n <p v-if=\"validated && !isCorrect\" class=\"quiz-expected\">\n {{ t.expectedAnswer }} <code>{{ rawQuiz?.expected[0] }}</code>\n </p>\n </div>\n </template>\n\n <!-- Explanation (rich text: supports **bold** and `code`) -->\n <div v-if=\"validated && quiz.explanation\" class=\"quiz-explanation\" role=\"note\">\n <strong>{{ t.explanation }}</strong>\n <template v-for=\"(part, pi) in parseRichText(quiz.explanation)\" :key=\"pi\">\n <strong v-if=\"part.type === 'bold'\">{{ part.value }}</strong>\n <em v-else-if=\"part.type === 'italic'\">{{ part.value }}</em>\n <code v-else-if=\"part.type === 'code'\">{{ part.value }}</code>\n <template v-else>{{ part.value }}</template>\n </template>\n </div>\n </fieldset>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, provide, inject } from \"vue\"\nimport type { QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nconst t = inject<QuizLocale>(\"quiz-locale\")!\nconst questions = ref<QuizState[]>([])\nconst validated = ref(false)\n\n// Generate a storage key from the page path — set eagerly so children can read on mount\n// (Vue mounts children before parent, so onMounted would be too late)\nconst storageKey = typeof window !== 'undefined'\n ? 'quiz-state:' + window.location.pathname\n : ''\n\n// Restore quiz-level state immediately so validated is set before children mount\nrestoreState()\n\n// Question index counter for stable per-question storage keys\nlet questionIdx = 0\n\nprovide(\"quiz-validated\", validated)\nprovide(\"quiz-register\", (q: QuizState) => {\n questions.value.push(q)\n // returned disposer keeps counts correct on unmount (HMR, v-if)\n return () => {\n const i = questions.value.indexOf(q)\n if (i >= 0) questions.value.splice(i, 1)\n }\n})\n\n// Per-question persistence: each question gets a unique sub-key\nprovide(\"quiz-question-id\", () => questionIdx++)\nprovide(\"quiz-save-answer\", (qId: number, data: SavedAnswer) => {\n if (!storageKey) return\n try {\n sessionStorage.setItem(storageKey + \":q\" + qId, JSON.stringify(data))\n } catch { /* ignore */ }\n})\nprovide(\"quiz-get-answer\", (qId: number): SavedAnswer | null => {\n if (!storageKey) return null\n try {\n const raw = sessionStorage.getItem(storageKey + \":q\" + qId)\n return raw ? JSON.parse(raw) : null\n } catch { return null }\n})\n\n// Provide a reset trigger for child components\nconst resetCounter = ref(0)\nprovide(\"quiz-reset\", resetCounter)\n\nconst totalQuestions = computed(() => questions.value.length)\nconst answeredCount = computed(() =>\n questions.value.filter((q) => q.answered).length\n)\nconst correctCount = computed(() =>\n questions.value.filter((q) => q.isCorrect).length\n)\nconst allAnswered = computed(\n () => totalQuestions.value > 0 && answeredCount.value === totalQuestions.value,\n)\nconst progressPct = computed(() =>\n totalQuestions.value ? Math.round((answeredCount.value / totalQuestions.value) * 100) : 0\n)\n\nfunction validate() {\n validated.value = true\n saveState()\n}\n\nfunction reset() {\n validated.value = false\n resetCounter.value++\n clearState()\n}\n\n/* ── sessionStorage persistence ───────────────────────────────────── */\nfunction saveState() {\n if (!storageKey) return\n try {\n const data = { validated: validated.value, ts: Date.now() }\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n } catch { /* storage full or unavailable */ }\n}\n\nfunction restoreState() {\n if (!storageKey) return\n try {\n const raw = sessionStorage.getItem(storageKey)\n if (!raw) return\n const data = JSON.parse(raw)\n if (data && data.validated) {\n validated.value = true\n }\n } catch { /* corrupted – ignore */ }\n}\n\nfunction clearState() {\n if (!storageKey) return\n try {\n const keysToRemove: string[] = []\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i)\n if (key && key.startsWith(storageKey)) {\n keysToRemove.push(key)\n }\n }\n keysToRemove.forEach((k) => sessionStorage.removeItem(k))\n } catch { /* ignore */ }\n}\n</script>\n\n<template>\n <div class=\"quiz-page\" role=\"region\" aria-label=\"Quiz\">\n <div\nclass=\"quiz-progress-bar\" role=\"progressbar\"\n :aria-valuenow=\"answeredCount\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"totalQuestions\"\n :aria-label=\"answeredCount + ' / ' + totalQuestions + ' ' + t.answered\"\n >\n <div class=\"quiz-progress-track\">\n <div\n class=\"quiz-progress-fill\"\n :style=\"{ width: progressPct + '%' }\"\n :class=\"{ complete: progressPct === 100 }\"\n ></div>\n </div>\n <span class=\"quiz-progress-label\" aria-live=\"polite\">\n {{ answeredCount }} / {{ totalQuestions }} {{ t.answered }}\n </span>\n </div>\n\n <slot />\n\n <div class=\"quiz-actions\">\n <button\n v-if=\"!validated\"\n class=\"quiz-btn quiz-btn-validate\"\n :disabled=\"!allAnswered\"\n @click=\"validate\"\n >\n {{ t.validate }}\n </button>\n\n <template v-if=\"validated\">\n <div\nclass=\"quiz-score\" role=\"status\" aria-live=\"polite\"\n :aria-label=\"correctCount + ' / ' + totalQuestions + ' ' + t.correctAnswers(correctCount)\"\n >\n <span class=\"quiz-score-value\">{{ correctCount }} / {{ totalQuestions }}</span>\n <span class=\"quiz-score-label\">\n {{ t.correctAnswers(correctCount) }}\n ({{ totalQuestions ? Math.round((correctCount / totalQuestions) * 100) : 0 }}%)\n </span>\n </div>\n <button class=\"quiz-btn quiz-btn-reset\" @click=\"reset\">\n {{ t.reset }}\n </button>\n </template>\n </div>\n </div>\n</template>\n","import type { App } from \"vue\"\nimport { ensureStylesInjected } from \"./styleManager\"\n\nexport { quizMarkdownPlugin } from \"./md-plugin\"\nexport { getLocale, locales } from \"./locales\"\nexport type { QuizData, QuizAnswer, QuizLocale, QuizState, SavedAnswer } from \"./types/quiz\"\n\nimport QuizQuestion from \"./QuizQuestion.vue\"\nimport QuizPage from \"./QuizPage.vue\"\nimport { getLocale as _getLocale } from \"./locales\"\n\nexport { QuizQuestion, QuizPage }\n\n/**\n * Register quiz components and locale in a VitePress `enhanceApp` hook.\n *\n * @example\n * // .vitepress/theme/index.ts\n * import { enhanceAppWithQuiz } from '@matinfo/vitepress-plugin-quiz'\n * export default {\n * enhanceApp({ app }) {\n * enhanceAppWithQuiz(app) // English (default)\n * enhanceAppWithQuiz(app, 'fr') // French\n * }\n * }\n */\nexport function enhanceAppWithQuiz(app: App, lang = \"en\"): void {\n ensureStylesInjected()\n app.provide(\"quiz-locale\", _getLocale(lang))\n app.component(\"QuizQuestion\", QuizQuestion)\n app.component(\"QuizPage\", QuizPage)\n}\n"],"names":["stylesInjected","ensureStylesInjected","styleId","style","quizCssInline","QUIZ_RE","CLOSE_RE","KEY_RE","KNOWN_KEYS","quizMarkdownPlugin","md","escapeHtml","state","startLine","endLine","silent","quizBlockRule","tokens","idx","t","open","close","startPos","startMax","lineText","nextLine","found","pos","max","line","innerLines","i","data","parseQuizBlock","a","token","renderQuizComponent","lines","question","answers","explanation","expected","shuffle","append","raw","m","key","value","join","cur","extra","x","entry","json","locales","n","getLocale","lang","parseRichText","text","parts","re","last","normalizeInput","props","__props","rawQuiz","computed","shuffleArray","arr","j","shuffledAnswers","ref","initAnswers","indexed","quiz","isInputMode","correctCount","isMultiple","selected","inputValue","register","inject","validated","resetCounter","getQuestionId","saveAnswer","getAnswer","myId","answered","isCorrect","input","e","correctIndices","unregister","onUnmounted","watch","onMounted","saved","toggle","orig","next","persistState","answerClass","answer","wasPicked","inputClass","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_2","_Fragment","_renderList","part","pi","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_createTextVNode","_openBlock","_hoisted_6","$event","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","questions","storageKey","restoreState","questionIdx","provide","q","qId","totalQuestions","answeredCount","allAnswered","progressPct","validate","saveState","reset","clearState","keysToRemove","_hoisted_1","_renderSlot","_ctx","_hoisted_8","enhanceAppWithQuiz","app","_getLocale","QuizQuestion","QuizPage"],"mappings":"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"}
|