@matinfo/vitepress-plugin-exercices-form
v1.0.9
Published
VitePress plugin for interactive exercises with answer boxes and client-side PDF generation
Maintainers
Readme
vitepress-plugin-exercices-form
A VitePress plugin that adds interactive answer boxes to exercise pages, with localStorage persistence and client-side PDF export.
Features:
- ✏️ Text and code answer boxes with localStorage persistence
- 📄 Client-side PDF export (student name, class, date)
- 🌍 i18n support — English, French, German
- 🏷️ Simple
:::answer-boxfenced container syntax - 🔄 Per-box text ↔ code mode toggle
- 🎨 Integrates with any VitePress theme
Install
npm install @matinfo/vitepress-plugin-exercices-form html2pdf.js
html2pdf.jsis a required peer dependency for PDF export.
Quick start
1. Register the markdown and Vite plugins
In .vitepress/config.ts:
import {
exerciceVitePlugin,
exerciseMarkdownPlugin,
} from '@matinfo/vitepress-plugin-exercices-form'
import { defineConfig } from 'vitepress'
export default defineConfig({
markdown: {
config(md) {
md.use(exerciseMarkdownPlugin) // parses :::answer-box fences
},
},
vite: {
plugins: [exerciceVitePlugin()], // handles html2pdf.js SSR exclusion
},
})2. Register components in the theme
In .vitepress/theme/index.ts:
import { enhanceAppWithExercises } from '@matinfo/vitepress-plugin-exercices-form'
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
enhanceAppWithExercises(app, { lang: 'fr' })
},
}3. Write exercise pages
In any .md file:
## Question 1
What is the capital of Switzerland?
:::answer-box
:::
## Question 2
Write a PowerShell script that lists all files:
:::answer-box code
:::4. Add correction-only content
:::answer-box-off
This content is hidden in student documents but visible in corrections.
:::Markdown syntax
| Block | Description |
| --- | --- |
| :::answer-box | Text answer area (resizable) |
| :::answer-box code | Code editor (monospace, tab support) |
| :::answer-box checklist | Checkbox list — one [] sentence per body line |
| :::answer-box radio | Single-choice list (radio buttons) — one [] sentence per body line |
| :::answer-box table H1 \| H2 \| H3 | Table with the given column headers (rows=N optional) |
| :::answer-box-off | Correction-only block — visible in PDF corrections, hidden on student pages |
Checklist
:::answer-box checklist
[] Sentence one
[] Sentence two
:::Each [] text line becomes a checkbox (state saved as a JSON boolean[]). Other body lines are ignored.
In the PDF, items print as ☑ / ☐ followed by the sentence. If nothing is ticked, the locale noAnswer text is printed instead.
Table
:::answer-box table Name | Role | Notes
rows=4
:::Headers come from the opening line, separated by |. rows=N is optional (default 3, max 50). Cells are text inputs (state saved as a JSON string[][]).
The answer table always expands to the full available page width. Cells support multi-line answers (Enter) and auto-grow while typing.
You can also prefill and lock the first column by adding plain body lines:
:::answer-box table Name | Role | Notes
name 1
name 2
name 3
:::In this variant, the first column values (name 1, name 2, name 3) are read-only, while other columns stay editable. The number of rows automatically grows to fit all fixed first-column values.
In the PDF, the table prints as a static table with the same headers and the typed cell values. If every cell is empty, noAnswer is printed instead. A table without headers is not recognised.
Checklists and tables share the box counter (<file>-q<N>) with text/code boxes, so the PDF button, clear button and storage work unchanged. Note: ids are positional, so inserting a box shifts the answers stored for later ones.
Radio (single choice)
:::answer-box radio
[] Option one
[] Option two
[] Option three
:::Each [] text line becomes one radio option (only one can be selected). Other body lines are ignored.
State is saved as a JSON number (the selected option index). In the PDF, items print as ◉ / ◯ followed by the sentence. If no option is selected, the locale noAnswer text is printed instead.
Options
enhanceAppWithExercises(app, options) accepts:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| lang | "en" \| "fr" \| "de" | "en" | UI language |
| storagePrefix | string | "exercise-" | localStorage key prefix |
| defaultMode | "text" \| "code" | "text" | Default editor mode |
| enableModeToggle | boolean | true | Show text/code toggle |
| tabSize | number | 2 | Tab width in code mode |
| dateLocale | string | same as lang | BCP 47 locale for date formatting |
API
// markdown-it plugin — parses :::answer-box fences
function exerciseMarkdownPlugin(md: MarkdownIt): void
// Vite plugin — excludes html2pdf.js from SSR
function exerciceVitePlugin(): Plugin
// registers AnswerBox and ExercisePdfButton Vue components
function enhanceAppWithExercises(app: App, options?: ExercisePluginOptions): void
// returns the locale object for a given language code
function getLocale(lang: string): ExerciseLocale
// built-in locale strings (keys: "en", "fr", "de")
declare const locales: Record<string, ExerciseLocale>
// Pinia store — per-page answer state (use inside Vue components)
function useAnswerStore(): AnswerStoreTypes
interface ExercisePluginOptions {
lang?: 'en' | 'fr' | 'de'
storagePrefix?: string
defaultMode?: 'text' | 'code'
enableModeToggle?: boolean
tabSize?: number
dateLocale?: string
}// Locale data
import { locales } from '@matinfo/vitepress-plugin-exercices-form'Troubleshooting
Failed to resolve module specifier 'html2pdf.js' when generating a PDF
Happens when another Vite plugin overrides optimizeDeps.include, so html2pdf.js is never pre-bundled for the browser. Known case: vitepress-plugin-mermaid (withMermaid). Add it explicitly in your config:
export default withMermaid(
defineConfig({
vite: {
plugins: [exerciceVitePlugin()],
optimizeDeps: { include: ['html2pdf.js'] }, // workaround for withMermaid
},
}),
)Then restart with npx vitepress dev --force to rebuild the Vite cache.
Ecosystem
| Package | Description | | --- | --- | | @matinfo/vitepress-plugin-exercise-parser | Shared Markdown → AST parser | | @matinfo/vitepress-plugin-exercices-form | Runtime interactive answer boxes (this package) | | @matinfo/vitepress-plugin-exercices-pdf | Build-time student + correction PDF generator | | @matinfo/vitepress-plugin-cards-pdf | Build-time card grid PDF generator |
Contributing
Pull requests are welcome — please open an issue first for significant changes.
