@curio-sd/e-module-builder 0.2.0 → 0.3.0
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/LICENSE +7 -7
- package/README.md +267 -267
- package/build.mjs +1 -4
- package/package.json +4 -4
- package/src/js/inleveropdracht.js +8 -30
package/LICENSE
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Copyright 2026 Curio Software Developer
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
-
|
|
5
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
-
|
|
7
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
Copyright 2026 Curio Software Developer
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,267 +1,267 @@
|
|
|
1
|
-
# `@curio-sd/e-module-builder`
|
|
2
|
-
|
|
3
|
-
A CLI build tool for creating interactive e-learning modules. It processes a structured `content/` directory of Markdown files and produces a Vite-powered, single-page-style site with theory pages, quizzes, exercises, and assignments — one per week.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install --save-dev @curio-sd/e-module-builder
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Add these scripts to your project's `package.json`:
|
|
12
|
-
|
|
13
|
-
```json
|
|
14
|
-
{
|
|
15
|
-
"scripts": {
|
|
16
|
-
"dev": "e-module-builder dev",
|
|
17
|
-
"build": "e-module-builder build",
|
|
18
|
-
"preview": "e-module-builder preview"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Commands
|
|
24
|
-
|
|
25
|
-
| Command | Description |
|
|
26
|
-
| ------- | ----------- |
|
|
27
|
-
| `dev` | Start dev server at `localhost:5173`, watches `content/` and hot-reloads |
|
|
28
|
-
| `build` | Production build to `dist/` |
|
|
29
|
-
| `preview` | Locally preview the `dist/` build |
|
|
30
|
-
|
|
31
|
-
## Project structure
|
|
32
|
-
|
|
33
|
-
Your project only needs a `content/` directory. Everything else (`src/data/`, `pages/`, `index.html`) is generated automatically.
|
|
34
|
-
|
|
35
|
-
```txt
|
|
36
|
-
content/
|
|
37
|
-
module.md ← module metadata (name, weeks, language, exercise mode)
|
|
38
|
-
week1/
|
|
39
|
-
theory.md ← theory content (Markdown + YAML frontmatter)
|
|
40
|
-
quiz.md ← mid-week quiz questions
|
|
41
|
-
assignment.md ← hand-in assignment
|
|
42
|
-
exercises/
|
|
43
|
-
_meta.md ← exercise set metadata (week, title, color)
|
|
44
|
-
1.md ← exercise 1
|
|
45
|
-
2.md ← exercise 2
|
|
46
|
-
…
|
|
47
|
-
week2/ … weekN/ ← same structure
|
|
48
|
-
exams/
|
|
49
|
-
theory-exam.md ← final theory exam (optional)
|
|
50
|
-
practical-exam.md ← final practical exam (optional)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Content file formats
|
|
54
|
-
|
|
55
|
-
### `content/module.md`
|
|
56
|
-
|
|
57
|
-
```yaml
|
|
58
|
-
---
|
|
59
|
-
name: CSS Grid
|
|
60
|
-
subtitle: E-module
|
|
61
|
-
weeks: 4
|
|
62
|
-
language: nl
|
|
63
|
-
exerciseMode: interactive # or: external
|
|
64
|
-
description: Learn CSS Grid from the ground up.
|
|
65
|
-
youtube: https://www.youtube.com/watch?v=...
|
|
66
|
-
logoAlt: My module logo
|
|
67
|
-
algemeen:
|
|
68
|
-
- I can explain the difference between Flexbox and Grid
|
|
69
|
-
---
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
| Field | Required | Description |
|
|
73
|
-
| ----- | -------- | ----------- |
|
|
74
|
-
| `name` | yes | Module title |
|
|
75
|
-
| `weeks` | yes | Number of weeks to include (limits which `weekN/` dirs are processed) |
|
|
76
|
-
| `exerciseMode` | yes | `interactive` (Monaco editor) or `external` (link-out) |
|
|
77
|
-
| `language` | no | UI language, default `nl` |
|
|
78
|
-
| `subtitle` | no | Shown below the title |
|
|
79
|
-
| `description` | no | Short module description |
|
|
80
|
-
| `youtube` | no | Intro video URL |
|
|
81
|
-
| `algemeen` | no | General learning outcomes added to the checklist |
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
### `content/weekN/theory.md`
|
|
86
|
-
|
|
87
|
-
YAML frontmatter + Markdown body. The body supports standard Markdown, syntax-highlighted code blocks, and custom block-level elements (see [Custom elements](#custom-elements)).
|
|
88
|
-
|
|
89
|
-
```yaml
|
|
90
|
-
---
|
|
91
|
-
week: 1
|
|
92
|
-
title: The building blocks
|
|
93
|
-
goal: You understand what CSS Grid is and when to use it.
|
|
94
|
-
accent: indigo # Tailwind color name used as the week's accent color
|
|
95
|
-
summary: Short summary shown on the home page.
|
|
96
|
-
leeruitkomsten:
|
|
97
|
-
- I can explain what a grid container is
|
|
98
|
-
- I can define columns with grid-template-columns
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
Markdown content here…
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
### `content/weekN/quiz.md`
|
|
107
|
-
|
|
108
|
-
```yaml
|
|
109
|
-
---
|
|
110
|
-
title: Mid-week quiz — Week 1
|
|
111
|
-
passScore: 70
|
|
112
|
-
questions:
|
|
113
|
-
- id: q1
|
|
114
|
-
question: What does display:grid do?
|
|
115
|
-
options:
|
|
116
|
-
- Creates a flex container
|
|
117
|
-
- Activates CSS Grid on the element
|
|
118
|
-
correct: 1
|
|
119
|
-
explanation: display:grid activates CSS Grid.
|
|
120
|
-
---
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
### `content/weekN/assignment.md`
|
|
126
|
-
|
|
127
|
-
The Markdown **body** is split on blank lines: the first paragraph becomes the `case`, the rest becomes the `assignment` description.
|
|
128
|
-
|
|
129
|
-
```yaml
|
|
130
|
-
---
|
|
131
|
-
week: 1
|
|
132
|
-
title: Build a page layout
|
|
133
|
-
subtitle: Practical assignment
|
|
134
|
-
client: Acme Corp
|
|
135
|
-
deliverables:
|
|
136
|
-
- A working HTML/CSS page
|
|
137
|
-
criteria:
|
|
138
|
-
- Grid is used for the overall layout
|
|
139
|
-
maxPoints: 10
|
|
140
|
-
tips:
|
|
141
|
-
- Start with the grid container
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
Case description paragraph.
|
|
145
|
-
|
|
146
|
-
Assignment instructions paragraph.
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
### `content/weekN/exercises/_meta.md`
|
|
152
|
-
|
|
153
|
-
```yaml
|
|
154
|
-
---
|
|
155
|
-
week: 1
|
|
156
|
-
title: CSS Grid exercises
|
|
157
|
-
color: indigo
|
|
158
|
-
mode: interactive # optional, overrides module-level exerciseMode for this set
|
|
159
|
-
---
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
### `content/weekN/exercises/N.md`
|
|
165
|
-
|
|
166
|
-
Each file is a single exercise defined entirely in YAML frontmatter.
|
|
167
|
-
|
|
168
|
-
**Text/instructions exercise:**
|
|
169
|
-
|
|
170
|
-
```yaml
|
|
171
|
-
---
|
|
172
|
-
type: text
|
|
173
|
-
title: Columns
|
|
174
|
-
description: |
|
|
175
|
-
Create a grid with two equal columns.
|
|
176
|
-
---
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Interactive (Monaco editor) exercise:**
|
|
180
|
-
|
|
181
|
-
```yaml
|
|
182
|
-
---
|
|
183
|
-
type: interactive
|
|
184
|
-
title: Add a gap
|
|
185
|
-
starterHtml: "<div class='grid'>…</div>"
|
|
186
|
-
starterCss: ".grid { display: grid; }"
|
|
187
|
-
solution: ".grid { display: grid; gap: 16px; }"
|
|
188
|
-
---
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
**External exercise (link-out):**
|
|
192
|
-
|
|
193
|
-
```yaml
|
|
194
|
-
---
|
|
195
|
-
type: external
|
|
196
|
-
title: Grid Garden
|
|
197
|
-
url: https://cssgridgarden.com
|
|
198
|
-
---
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
### `content/exams/theory-exam.md` and `practical-exam.md`
|
|
204
|
-
|
|
205
|
-
Same structure as `quiz.md`. Practical exam questions may include a `preview` field with `css` and `html` for a live CSS preview alongside the question.
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## Custom elements in Markdown
|
|
210
|
-
|
|
211
|
-
Theory pages support these custom block elements in Markdown:
|
|
212
|
-
|
|
213
|
-
| Element | Purpose |
|
|
214
|
-
| ------- | ------- |
|
|
215
|
-
| `<x-callout>` | Highlighted note block. Add `type="warning"` for warnings. |
|
|
216
|
-
| `<x-card title="…">` | Content card with a title. |
|
|
217
|
-
| `<x-compare>` / `<x-compare-item title="…">` | Side-by-side comparison columns. |
|
|
218
|
-
| `<x-nav label="…">` | Bottom navigation links (one Markdown link per line). |
|
|
219
|
-
|
|
220
|
-
Example:
|
|
221
|
-
|
|
222
|
-
```markdown
|
|
223
|
-
<x-callout type="warning">
|
|
224
|
-
Watch out: only **direct children** of the grid container become grid items.
|
|
225
|
-
</x-callout>
|
|
226
|
-
|
|
227
|
-
<x-compare>
|
|
228
|
-
<x-compare-item title="Flexbox — one direction">
|
|
229
|
-
|
|
230
|
-
Use for components: navbars, button rows.
|
|
231
|
-
|
|
232
|
-
</x-compare-item>
|
|
233
|
-
<x-compare-item title="Grid — two directions">
|
|
234
|
-
|
|
235
|
-
Use for full page layouts.
|
|
236
|
-
|
|
237
|
-
</x-compare-item>
|
|
238
|
-
</x-compare>
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## What gets generated
|
|
244
|
-
|
|
245
|
-
The build pipeline runs before Vite and produces:
|
|
246
|
-
|
|
247
|
-
| Output | Source |
|
|
248
|
-
| ------ | ------ |
|
|
249
|
-
| `src/data/manifest.json` | `module.md` + all week frontmatter |
|
|
250
|
-
| `src/data/theory-weekN.json` | `weekN/theory.md` |
|
|
251
|
-
| `src/data/tussentoets-weekN.json` | `weekN/quiz.md` |
|
|
252
|
-
| `src/data/exercises/weekN.json` | `weekN/exercises/` |
|
|
253
|
-
| `src/data/inleveropdracht-weekN.json` | `weekN/assignment.md` |
|
|
254
|
-
| `src/data/checklist.json` | `leeruitkomsten` from all weeks |
|
|
255
|
-
| `src/data/toets-theorie.json` | `exams/theory-exam.md` |
|
|
256
|
-
| `src/data/toets-praktijk.json` | `exams/practical-exam.md` |
|
|
257
|
-
| `pages/weekN-theorie.html` | generated from template |
|
|
258
|
-
| `pages/weekN-oefeningen.html` | generated from template |
|
|
259
|
-
| `pages/weekN-toets.html` | generated from template |
|
|
260
|
-
| `pages/weekN-oefening.html` | generated from template |
|
|
261
|
-
| `pages/weekN-inleveropdracht.html` | generated from template |
|
|
262
|
-
| `pages/checklist.html` | generated from template |
|
|
263
|
-
| `pages/toets-theorie.html` | generated from template |
|
|
264
|
-
| `pages/toets-praktijk.html` | generated from template |
|
|
265
|
-
| `index.html` | generated from template |
|
|
266
|
-
|
|
267
|
-
In `dev` mode, changes to `content/` trigger an automatic rebuild and browser reload.
|
|
1
|
+
# `@curio-sd/e-module-builder`
|
|
2
|
+
|
|
3
|
+
A CLI build tool for creating interactive e-learning modules. It processes a structured `content/` directory of Markdown files and produces a Vite-powered, single-page-style site with theory pages, quizzes, exercises, and assignments — one per week.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-dev @curio-sd/e-module-builder
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Add these scripts to your project's `package.json`:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "e-module-builder dev",
|
|
17
|
+
"build": "e-module-builder build",
|
|
18
|
+
"preview": "e-module-builder preview"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
| Command | Description |
|
|
26
|
+
| ------- | ----------- |
|
|
27
|
+
| `dev` | Start dev server at `localhost:5173`, watches `content/` and hot-reloads |
|
|
28
|
+
| `build` | Production build to `dist/` |
|
|
29
|
+
| `preview` | Locally preview the `dist/` build |
|
|
30
|
+
|
|
31
|
+
## Project structure
|
|
32
|
+
|
|
33
|
+
Your project only needs a `content/` directory. Everything else (`src/data/`, `pages/`, `index.html`) is generated automatically.
|
|
34
|
+
|
|
35
|
+
```txt
|
|
36
|
+
content/
|
|
37
|
+
module.md ← module metadata (name, weeks, language, exercise mode)
|
|
38
|
+
week1/
|
|
39
|
+
theory.md ← theory content (Markdown + YAML frontmatter)
|
|
40
|
+
quiz.md ← mid-week quiz questions
|
|
41
|
+
assignment.md ← hand-in assignment
|
|
42
|
+
exercises/
|
|
43
|
+
_meta.md ← exercise set metadata (week, title, color)
|
|
44
|
+
1.md ← exercise 1
|
|
45
|
+
2.md ← exercise 2
|
|
46
|
+
…
|
|
47
|
+
week2/ … weekN/ ← same structure
|
|
48
|
+
exams/
|
|
49
|
+
theory-exam.md ← final theory exam (optional)
|
|
50
|
+
practical-exam.md ← final practical exam (optional)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Content file formats
|
|
54
|
+
|
|
55
|
+
### `content/module.md`
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
---
|
|
59
|
+
name: CSS Grid
|
|
60
|
+
subtitle: E-module
|
|
61
|
+
weeks: 4
|
|
62
|
+
language: nl
|
|
63
|
+
exerciseMode: interactive # or: external
|
|
64
|
+
description: Learn CSS Grid from the ground up.
|
|
65
|
+
youtube: https://www.youtube.com/watch?v=...
|
|
66
|
+
logoAlt: My module logo
|
|
67
|
+
algemeen:
|
|
68
|
+
- I can explain the difference between Flexbox and Grid
|
|
69
|
+
---
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| Field | Required | Description |
|
|
73
|
+
| ----- | -------- | ----------- |
|
|
74
|
+
| `name` | yes | Module title |
|
|
75
|
+
| `weeks` | yes | Number of weeks to include (limits which `weekN/` dirs are processed) |
|
|
76
|
+
| `exerciseMode` | yes | `interactive` (Monaco editor) or `external` (link-out) |
|
|
77
|
+
| `language` | no | UI language, default `nl` |
|
|
78
|
+
| `subtitle` | no | Shown below the title |
|
|
79
|
+
| `description` | no | Short module description |
|
|
80
|
+
| `youtube` | no | Intro video URL |
|
|
81
|
+
| `algemeen` | no | General learning outcomes added to the checklist |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### `content/weekN/theory.md`
|
|
86
|
+
|
|
87
|
+
YAML frontmatter + Markdown body. The body supports standard Markdown, syntax-highlighted code blocks, and custom block-level elements (see [Custom elements](#custom-elements)).
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
---
|
|
91
|
+
week: 1
|
|
92
|
+
title: The building blocks
|
|
93
|
+
goal: You understand what CSS Grid is and when to use it.
|
|
94
|
+
accent: indigo # Tailwind color name used as the week's accent color
|
|
95
|
+
summary: Short summary shown on the home page.
|
|
96
|
+
leeruitkomsten:
|
|
97
|
+
- I can explain what a grid container is
|
|
98
|
+
- I can define columns with grid-template-columns
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
Markdown content here…
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### `content/weekN/quiz.md`
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
---
|
|
110
|
+
title: Mid-week quiz — Week 1
|
|
111
|
+
passScore: 70
|
|
112
|
+
questions:
|
|
113
|
+
- id: q1
|
|
114
|
+
question: What does display:grid do?
|
|
115
|
+
options:
|
|
116
|
+
- Creates a flex container
|
|
117
|
+
- Activates CSS Grid on the element
|
|
118
|
+
correct: 1
|
|
119
|
+
explanation: display:grid activates CSS Grid.
|
|
120
|
+
---
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### `content/weekN/assignment.md`
|
|
126
|
+
|
|
127
|
+
The Markdown **body** is split on blank lines: the first paragraph becomes the `case`, the rest becomes the `assignment` description.
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
---
|
|
131
|
+
week: 1
|
|
132
|
+
title: Build a page layout
|
|
133
|
+
subtitle: Practical assignment
|
|
134
|
+
client: Acme Corp
|
|
135
|
+
deliverables:
|
|
136
|
+
- A working HTML/CSS page
|
|
137
|
+
criteria:
|
|
138
|
+
- Grid is used for the overall layout
|
|
139
|
+
maxPoints: 10
|
|
140
|
+
tips:
|
|
141
|
+
- Start with the grid container
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
Case description paragraph.
|
|
145
|
+
|
|
146
|
+
Assignment instructions paragraph.
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `content/weekN/exercises/_meta.md`
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
---
|
|
155
|
+
week: 1
|
|
156
|
+
title: CSS Grid exercises
|
|
157
|
+
color: indigo
|
|
158
|
+
mode: interactive # optional, overrides module-level exerciseMode for this set
|
|
159
|
+
---
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### `content/weekN/exercises/N.md`
|
|
165
|
+
|
|
166
|
+
Each file is a single exercise defined entirely in YAML frontmatter.
|
|
167
|
+
|
|
168
|
+
**Text/instructions exercise:**
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
---
|
|
172
|
+
type: text
|
|
173
|
+
title: Columns
|
|
174
|
+
description: |
|
|
175
|
+
Create a grid with two equal columns.
|
|
176
|
+
---
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Interactive (Monaco editor) exercise:**
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
---
|
|
183
|
+
type: interactive
|
|
184
|
+
title: Add a gap
|
|
185
|
+
starterHtml: "<div class='grid'>…</div>"
|
|
186
|
+
starterCss: ".grid { display: grid; }"
|
|
187
|
+
solution: ".grid { display: grid; gap: 16px; }"
|
|
188
|
+
---
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**External exercise (link-out):**
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
---
|
|
195
|
+
type: external
|
|
196
|
+
title: Grid Garden
|
|
197
|
+
url: https://cssgridgarden.com
|
|
198
|
+
---
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### `content/exams/theory-exam.md` and `practical-exam.md`
|
|
204
|
+
|
|
205
|
+
Same structure as `quiz.md`. Practical exam questions may include a `preview` field with `css` and `html` for a live CSS preview alongside the question.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Custom elements in Markdown
|
|
210
|
+
|
|
211
|
+
Theory pages support these custom block elements in Markdown:
|
|
212
|
+
|
|
213
|
+
| Element | Purpose |
|
|
214
|
+
| ------- | ------- |
|
|
215
|
+
| `<x-callout>` | Highlighted note block. Add `type="warning"` for warnings. |
|
|
216
|
+
| `<x-card title="…">` | Content card with a title. |
|
|
217
|
+
| `<x-compare>` / `<x-compare-item title="…">` | Side-by-side comparison columns. |
|
|
218
|
+
| `<x-nav label="…">` | Bottom navigation links (one Markdown link per line). |
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
|
|
222
|
+
```markdown
|
|
223
|
+
<x-callout type="warning">
|
|
224
|
+
Watch out: only **direct children** of the grid container become grid items.
|
|
225
|
+
</x-callout>
|
|
226
|
+
|
|
227
|
+
<x-compare>
|
|
228
|
+
<x-compare-item title="Flexbox — one direction">
|
|
229
|
+
|
|
230
|
+
Use for components: navbars, button rows.
|
|
231
|
+
|
|
232
|
+
</x-compare-item>
|
|
233
|
+
<x-compare-item title="Grid — two directions">
|
|
234
|
+
|
|
235
|
+
Use for full page layouts.
|
|
236
|
+
|
|
237
|
+
</x-compare-item>
|
|
238
|
+
</x-compare>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## What gets generated
|
|
244
|
+
|
|
245
|
+
The build pipeline runs before Vite and produces:
|
|
246
|
+
|
|
247
|
+
| Output | Source |
|
|
248
|
+
| ------ | ------ |
|
|
249
|
+
| `src/data/manifest.json` | `module.md` + all week frontmatter |
|
|
250
|
+
| `src/data/theory-weekN.json` | `weekN/theory.md` |
|
|
251
|
+
| `src/data/tussentoets-weekN.json` | `weekN/quiz.md` |
|
|
252
|
+
| `src/data/exercises/weekN.json` | `weekN/exercises/` |
|
|
253
|
+
| `src/data/inleveropdracht-weekN.json` | `weekN/assignment.md` |
|
|
254
|
+
| `src/data/checklist.json` | `leeruitkomsten` from all weeks |
|
|
255
|
+
| `src/data/toets-theorie.json` | `exams/theory-exam.md` |
|
|
256
|
+
| `src/data/toets-praktijk.json` | `exams/practical-exam.md` |
|
|
257
|
+
| `pages/weekN-theorie.html` | generated from template |
|
|
258
|
+
| `pages/weekN-oefeningen.html` | generated from template |
|
|
259
|
+
| `pages/weekN-toets.html` | generated from template |
|
|
260
|
+
| `pages/weekN-oefening.html` | generated from template |
|
|
261
|
+
| `pages/weekN-inleveropdracht.html` | generated from template |
|
|
262
|
+
| `pages/checklist.html` | generated from template |
|
|
263
|
+
| `pages/toets-theorie.html` | generated from template |
|
|
264
|
+
| `pages/toets-praktijk.html` | generated from template |
|
|
265
|
+
| `index.html` | generated from template |
|
|
266
|
+
|
|
267
|
+
In `dev` mode, changes to `content/` trigger an automatic rebuild and browser reload.
|
package/build.mjs
CHANGED
|
@@ -128,15 +128,12 @@ for (const weekDir of activeWeeks) {
|
|
|
128
128
|
|
|
129
129
|
// assignment.md → src/data/inleveropdracht-weekN.json
|
|
130
130
|
const hwMd = readMd(path.join(dir, 'assignment.md'))
|
|
131
|
-
const hwBody = hwMd.content.trim()
|
|
132
|
-
const hwParas = hwBody.split(/\n\n+/)
|
|
133
131
|
const hwOut = {
|
|
134
132
|
week: hwMd.data.week ?? weekNum,
|
|
135
133
|
title: hwMd.data.title,
|
|
136
134
|
subtitle: hwMd.data.subtitle ?? '',
|
|
137
135
|
client: hwMd.data.client ?? '',
|
|
138
|
-
|
|
139
|
-
assignment: hwParas.slice(1).join('\n\n'),
|
|
136
|
+
html: marked.parse(hwMd.content ?? ''),
|
|
140
137
|
deliverables: hwMd.data.deliverables ?? [],
|
|
141
138
|
criteria: hwMd.data.criteria ?? [],
|
|
142
139
|
maxPoints: hwMd.data.maxPoints ?? 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curio-sd/e-module-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A tool for building e-modules for Curio SD",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"highlight.js": "^11.11.1",
|
|
30
30
|
"marked": "^18.0.5",
|
|
31
31
|
"marked-highlight": "^2.2.4",
|
|
32
|
-
"monaco-editor": "^0.
|
|
32
|
+
"monaco-editor": "^0.53.0",
|
|
33
33
|
"tailwindcss": "^4.1.8",
|
|
34
|
-
"vite": "^
|
|
34
|
+
"vite": "^8.0.16",
|
|
35
35
|
"yaml": "^2.9.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"vitest": "^
|
|
38
|
+
"vitest": "^4.1.8"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -8,7 +8,7 @@ export function initInleveropdracht(data) {
|
|
|
8
8
|
const container = document.querySelector('[data-inleveropdracht]')
|
|
9
9
|
if (!container) return
|
|
10
10
|
|
|
11
|
-
const state = getItem(storageKey(data.week), { criteria: {},
|
|
11
|
+
const state = getItem(storageKey(data.week), { criteria: {}, submitted: false })
|
|
12
12
|
|
|
13
13
|
const criteriaHtml = data.criteria
|
|
14
14
|
.map((c) => {
|
|
@@ -41,16 +41,15 @@ export function initInleveropdracht(data) {
|
|
|
41
41
|
<section class="card mb-6">
|
|
42
42
|
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Casus</p>
|
|
43
43
|
<h2 class="mt-2 text-xl font-medium text-zinc-900">${data.client}</h2>
|
|
44
|
-
|
|
44
|
+
</section>
|
|
45
|
+
|
|
46
|
+
<section class="card prose-inleveropdracht mb-6">
|
|
47
|
+
${data.html}
|
|
45
48
|
</section>
|
|
46
49
|
|
|
47
50
|
<section class="card mb-6">
|
|
48
|
-
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">
|
|
49
|
-
<
|
|
50
|
-
<div class="mt-6">
|
|
51
|
-
<p class="text-sm font-medium text-zinc-900">Inleveren</p>
|
|
52
|
-
<ul class="mt-2 list-inside list-disc space-y-1">${deliverablesHtml}</ul>
|
|
53
|
-
</div>
|
|
51
|
+
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Inleveren</p>
|
|
52
|
+
<ul class="mt-2 list-inside list-disc space-y-1">${deliverablesHtml}</ul>
|
|
54
53
|
</section>
|
|
55
54
|
|
|
56
55
|
<section class="card mb-6">
|
|
@@ -64,18 +63,6 @@ export function initInleveropdracht(data) {
|
|
|
64
63
|
<div data-criteria-list>${criteriaHtml}</div>
|
|
65
64
|
</section>
|
|
66
65
|
|
|
67
|
-
<section class="card mb-6">
|
|
68
|
-
<label class="block">
|
|
69
|
-
<p class="text-sm font-medium text-zinc-900">Notities / link naar je bestanden</p>
|
|
70
|
-
<p class="mt-1 text-sm text-zinc-500">Bijv. GitHub-link, Google Drive, of een korte toelichting voor je docent.</p>
|
|
71
|
-
<textarea
|
|
72
|
-
data-inleveropdracht-notes
|
|
73
|
-
class="mt-3 min-h-[120px] w-full border border-zinc-200 bg-white p-4 text-sm text-zinc-700 focus:border-zinc-900 focus:outline-none"
|
|
74
|
-
placeholder="Plak hier je inlever-link of notities..."
|
|
75
|
-
>${state.notes || ''}</textarea>
|
|
76
|
-
</label>
|
|
77
|
-
</section>
|
|
78
|
-
|
|
79
66
|
<section class="card-muted mb-6">
|
|
80
67
|
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Tips</p>
|
|
81
68
|
<ul class="mt-3 list-inside list-disc space-y-1">${tipsHtml}</ul>
|
|
@@ -89,7 +76,7 @@ export function initInleveropdracht(data) {
|
|
|
89
76
|
`
|
|
90
77
|
|
|
91
78
|
const saveState = (updates) => {
|
|
92
|
-
const current = getItem(storageKey(data.week), { criteria: {},
|
|
79
|
+
const current = getItem(storageKey(data.week), { criteria: {}, submitted: false })
|
|
93
80
|
setItem(storageKey(data.week), { ...current, ...updates })
|
|
94
81
|
}
|
|
95
82
|
|
|
@@ -115,14 +102,8 @@ export function initInleveropdracht(data) {
|
|
|
115
102
|
})
|
|
116
103
|
})
|
|
117
104
|
|
|
118
|
-
const notesEl = container.querySelector('[data-inleveropdracht-notes]')
|
|
119
|
-
notesEl?.addEventListener('input', () => {
|
|
120
|
-
saveState({ notes: notesEl.value })
|
|
121
|
-
})
|
|
122
|
-
|
|
123
105
|
container.querySelector('[data-export-inleveropdracht]')?.addEventListener('click', async () => {
|
|
124
106
|
const criteria = getItem(storageKey(data.week), {}).criteria || {}
|
|
125
|
-
const notes = notesEl?.value || ''
|
|
126
107
|
const lines = [
|
|
127
108
|
data.subtitle,
|
|
128
109
|
data.title,
|
|
@@ -136,9 +117,6 @@ export function initInleveropdracht(data) {
|
|
|
136
117
|
const mark = criteria[c.id] ? '[x]' : '[ ]'
|
|
137
118
|
lines.push(` ${mark} (${c.points}p) ${c.text}`)
|
|
138
119
|
}
|
|
139
|
-
if (notes) {
|
|
140
|
-
lines.push('', 'Notities:', notes)
|
|
141
|
-
}
|
|
142
120
|
try {
|
|
143
121
|
await navigator.clipboard.writeText(lines.join('\n'))
|
|
144
122
|
const btn = container.querySelector('[data-export-inleveropdracht]')
|