@lernapps/app-templates 0.0.0-stage → 0.1.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.
Files changed (231) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +195 -2
  3. package/dist/appname-Bh23cUuz.mjs +12 -0
  4. package/dist/assessment-By2GGTJC.mjs +1696 -0
  5. package/dist/block-BK_ohsH1.mjs +82 -0
  6. package/dist/blocks-BmRrD7UY.mjs +149 -0
  7. package/dist/blocks.mjs +4 -0
  8. package/dist/check-BkEl-m-F.mjs +254 -0
  9. package/dist/content-cYBPYg_J.mjs +140 -0
  10. package/dist/contract-PONpxJtd.mjs +52 -0
  11. package/dist/e2e-BiMI-pCL.mjs +89 -0
  12. package/dist/engine-BqF2Vffn.mjs +136 -0
  13. package/dist/explainer-e2e.mjs +1297 -0
  14. package/dist/explainer-islands.mjs +197 -0
  15. package/dist/explainer-plugin.mjs +2131 -0
  16. package/dist/explainer.mjs +1246 -0
  17. package/dist/html-mffkHGHh.mjs +89 -0
  18. package/dist/image-CspJVfyQ.mjs +21 -0
  19. package/dist/interactive-e2e.mjs +140 -0
  20. package/dist/interactive-plugin.mjs +242 -0
  21. package/dist/interactive.mjs +75 -0
  22. package/dist/load-C-0hUaSC.mjs +16947 -0
  23. package/dist/needs-BzEahJ6X.mjs +97 -0
  24. package/dist/pages-9RaaomoO.mjs +81 -0
  25. package/dist/questions-e2e.mjs +1 -0
  26. package/dist/questions-engine.mjs +3 -0
  27. package/dist/questions.mjs +6 -0
  28. package/dist/quiz-BKc8JYtC.mjs +70 -0
  29. package/dist/quiz-e2e.mjs +174 -0
  30. package/dist/quiz-plugin.mjs +266 -0
  31. package/dist/quiz.mjs +246 -0
  32. package/dist/render-BJey0bwl.mjs +540 -0
  33. package/dist/render-BL6fV_NI.mjs +507 -0
  34. package/dist/render-DpRO_RKm.mjs +146 -0
  35. package/dist/result-_eQcamoz.mjs +180 -0
  36. package/dist/tasks-DADUYy52.mjs +74 -0
  37. package/dist/validate-COaZso7u.mjs +1072 -0
  38. package/dist/validate-CxtdeQ5S.mjs +146 -0
  39. package/dist/vue-DO1m6q1R.mjs +40 -0
  40. package/dist/vue-code-BkJjW_I_.mjs +320 -0
  41. package/docs/arc42/01-introduction-and-goals.arc42.md +82 -0
  42. package/docs/arc42/02-constraints.arc42.md +228 -0
  43. package/docs/arc42/03-system-context.arc42.md +118 -0
  44. package/docs/arc42/04-solution-strategy.arc42.md +47 -0
  45. package/docs/arc42/05-building-blocks.arc42.md +307 -0
  46. package/docs/arc42/06-runtime-view.arc42.md +98 -0
  47. package/docs/arc42/07-deployment-view.arc42.md +160 -0
  48. package/docs/arc42/08-concepts.arc42.md +123 -0
  49. package/docs/arc42/09-decisions.arc42.md +246 -0
  50. package/docs/arc42/10-quality-requirements.arc42.md +155 -0
  51. package/docs/arc42/11-risks.arc42.md +113 -0
  52. package/docs/arc42/12-glossary.arc42.md +162 -0
  53. package/docs/arc42/architecture-evidence.md +395 -0
  54. package/explainer/.arc42ignore +3 -0
  55. package/explainer/README.md +54 -0
  56. package/explainer/_gitignore +5 -0
  57. package/explainer/docs/arc42/03-explainer-system-context.arc42.md +35 -0
  58. package/explainer/docs/arc42/05-explainer-building-blocks.arc42.md +659 -0
  59. package/explainer/docs/arc42/06-explainer-runtime-view.arc42.md +105 -0
  60. package/explainer/docs/arc42/07-explainer-deployment-view.arc42.md +34 -0
  61. package/explainer/docs/arc42/08-explainer-concepts.arc42.md +17 -0
  62. package/explainer/docs/arc42/09-explainer-decisions.arc42.md +443 -0
  63. package/explainer/docs/arc42/10-explainer-quality-requirements.arc42.md +54 -0
  64. package/explainer/docs/arc42/11-explainer-risks.arc42.md +17 -0
  65. package/explainer/docs/arc42/12-explainer-glossary.arc42.md +185 -0
  66. package/explainer/e2e/explainer.e2e.ts +3 -0
  67. package/explainer/index.html +11 -0
  68. package/explainer/package.json +18 -0
  69. package/explainer/playwright.config.ts +1 -0
  70. package/explainer/src/app.yaml +20 -0
  71. package/explainer/src/components/ProzentFeld.test.ts +30 -0
  72. package/explainer/src/components/ProzentFeld.ts +121 -0
  73. package/explainer/src/components/ProzentFeld.vue +84 -0
  74. package/explainer/src/images/hunderterfeld-klammern.svg +105 -0
  75. package/explainer/src/main.ts +4 -0
  76. package/explainer/src/prozent/chapter.yaml +18 -0
  77. package/explainer/src/prozent/grundwert.yaml +115 -0
  78. package/explainer/src/prozent/prozentsatz.yaml +99 -0
  79. package/explainer/src/prozent/prozentwert.yaml +119 -0
  80. package/explainer/tsconfig.json +1 -0
  81. package/explainer/vite.config.ts +4 -0
  82. package/interactive/.arc42ignore +5 -0
  83. package/interactive/README.md +28 -0
  84. package/interactive/_gitignore +4 -0
  85. package/interactive/docs/arc42/05-interactive-building-blocks.arc42.md +518 -0
  86. package/interactive/docs/arc42/06-interactive-runtime-view.arc42.md +58 -0
  87. package/interactive/docs/arc42/07-interactive-deployment-view.arc42.md +35 -0
  88. package/interactive/docs/arc42/08-interactive-concepts.arc42.md +52 -0
  89. package/interactive/docs/arc42/09-interactive-decisions.arc42.md +130 -0
  90. package/interactive/docs/arc42/10-interactive-quality-requirements.arc42.md +45 -0
  91. package/interactive/docs/arc42/11-interactive-risks.arc42.md +20 -0
  92. package/interactive/docs/arc42/12-interactive-glossary.arc42.md +80 -0
  93. package/interactive/e2e/interactive.e2e.ts +3 -0
  94. package/interactive/index.html +11 -0
  95. package/interactive/package.json +18 -0
  96. package/interactive/playwright.config.ts +1 -0
  97. package/interactive/src/app.yaml +28 -0
  98. package/interactive/src/core/index.ts +101 -0
  99. package/interactive/src/data/lessons.json +4 -0
  100. package/interactive/src/env.d.ts +6 -0
  101. package/interactive/src/main.ts +6 -0
  102. package/interactive/src/messages/de.json +12 -0
  103. package/interactive/src/playthroughs/grundreihe.yaml +33 -0
  104. package/interactive/src/playthroughs/obere-reihe.yaml +14 -0
  105. package/interactive/src/ui/App.vue +85 -0
  106. package/interactive/tsconfig.json +1 -0
  107. package/interactive/vite.config.ts +4 -0
  108. package/package.json +144 -4
  109. package/quiz/README.md +31 -0
  110. package/quiz/_gitignore +5 -0
  111. package/quiz/docs/arc42/05-quiz-building-blocks.arc42.md +283 -0
  112. package/quiz/docs/arc42/06-quiz-runtime-view.arc42.md +21 -0
  113. package/quiz/docs/arc42/07-quiz-deployment-view.arc42.md +33 -0
  114. package/quiz/docs/arc42/09-quiz-decisions.arc42.md +36 -0
  115. package/quiz/docs/arc42/10-quiz-quality-requirements.arc42.md +53 -0
  116. package/quiz/docs/arc42/11-quiz-risks.arc42.md +19 -0
  117. package/quiz/docs/arc42/12-quiz-glossary.arc42.md +27 -0
  118. package/quiz/e2e/quiz.e2e.ts +3 -0
  119. package/quiz/index.html +11 -0
  120. package/quiz/package.json +18 -0
  121. package/quiz/playwright.config.ts +1 -0
  122. package/quiz/src/images/berge-ordnen.jpg +0 -0
  123. package/quiz/src/images/everest-hoehe.jpg +0 -0
  124. package/quiz/src/images/galapagos.jpg +0 -0
  125. package/quiz/src/images/grand-canyon.jpg +0 -0
  126. package/quiz/src/images/totes-meer.jpg +0 -0
  127. package/quiz/src/images/uluru.jpg +0 -0
  128. package/quiz/src/images/wasserfaelle-laender.jpg +0 -0
  129. package/quiz/src/main.ts +4 -0
  130. package/quiz/src/quiz.json +312 -0
  131. package/quiz/tsconfig.json +1 -0
  132. package/quiz/vite.config.ts +4 -0
  133. package/runtime/explainer/assessment-client.ts +478 -0
  134. package/runtime/explainer/assessment-page.ts +446 -0
  135. package/runtime/explainer/assessment.ts +393 -0
  136. package/runtime/explainer/check-page.ts +520 -0
  137. package/runtime/explainer/components/exercise.ts +409 -0
  138. package/runtime/explainer/components/props.ts +230 -0
  139. package/runtime/explainer/components/read.ts +428 -0
  140. package/runtime/explainer/content.ts +277 -0
  141. package/runtime/explainer/e2e.ts +1474 -0
  142. package/runtime/explainer/exercises.ts +324 -0
  143. package/runtime/explainer/explainer.css +1283 -0
  144. package/runtime/explainer/explainer.ts +838 -0
  145. package/runtime/explainer/explainer.v1.schema.json +1165 -0
  146. package/runtime/explainer/expression.ts +407 -0
  147. package/runtime/explainer/fraction.ts +107 -0
  148. package/runtime/explainer/generated/comparator.ts +31 -0
  149. package/runtime/explainer/generated/question.ts +165 -0
  150. package/runtime/explainer/generated/soundness.ts +280 -0
  151. package/runtime/explainer/generated/texts.ts +257 -0
  152. package/runtime/explainer/generated/types.ts +103 -0
  153. package/runtime/explainer/generated/values.ts +136 -0
  154. package/runtime/explainer/islands.ts +281 -0
  155. package/runtime/explainer/load.ts +457 -0
  156. package/runtime/explainer/messages/de.json +200 -0
  157. package/runtime/explainer/messages/en.json +200 -0
  158. package/runtime/explainer/messages.ts +13 -0
  159. package/runtime/explainer/needs.ts +122 -0
  160. package/runtime/explainer/open-task.ts +235 -0
  161. package/runtime/explainer/outline.ts +299 -0
  162. package/runtime/explainer/plugin.ts +488 -0
  163. package/runtime/explainer/position.ts +62 -0
  164. package/runtime/explainer/prompt.ts +75 -0
  165. package/runtime/explainer/render.ts +686 -0
  166. package/runtime/explainer/result.ts +238 -0
  167. package/runtime/explainer/routes.ts +119 -0
  168. package/runtime/explainer/schema.ts +140 -0
  169. package/runtime/explainer/scope.ts +87 -0
  170. package/runtime/explainer/stuck.ts +57 -0
  171. package/runtime/explainer/tasks.ts +109 -0
  172. package/runtime/explainer/tutor.ts +469 -0
  173. package/runtime/explainer/tutorpage.ts +669 -0
  174. package/runtime/explainer/video.ts +123 -0
  175. package/runtime/interactive/contract.ts +121 -0
  176. package/runtime/interactive/e2e.ts +190 -0
  177. package/runtime/interactive/interactive.css +82 -0
  178. package/runtime/interactive/interactive.ts +115 -0
  179. package/runtime/interactive/interactive.v1.schema.json +146 -0
  180. package/runtime/interactive/load.ts +188 -0
  181. package/runtime/interactive/messages/de.json +14 -0
  182. package/runtime/interactive/messages/en.json +14 -0
  183. package/runtime/interactive/messages.ts +11 -0
  184. package/runtime/interactive/playthrough.v1.schema.json +143 -0
  185. package/runtime/interactive/plugin.ts +266 -0
  186. package/runtime/interactive/render.ts +126 -0
  187. package/runtime/quiz/e2e.ts +233 -0
  188. package/runtime/quiz/engine.ts +242 -0
  189. package/runtime/quiz/messages/de.json +23 -0
  190. package/runtime/quiz/messages/en.json +23 -0
  191. package/runtime/quiz/plugin.ts +150 -0
  192. package/runtime/quiz/quiz.css +154 -0
  193. package/runtime/quiz/quiz.ts +56 -0
  194. package/runtime/quiz/quiz.v1.schema.json +72 -0
  195. package/runtime/quiz/render.ts +173 -0
  196. package/runtime/quiz/validate.ts +108 -0
  197. package/runtime/shared/appname.ts +16 -0
  198. package/runtime/shared/blocks/block.ts +162 -0
  199. package/runtime/shared/blocks/blocks.css +190 -0
  200. package/runtime/shared/blocks/blocks.v1.schema.json +432 -0
  201. package/runtime/shared/blocks/code.ts +26 -0
  202. package/runtime/shared/blocks/figures.ts +49 -0
  203. package/runtime/shared/blocks/image.ts +25 -0
  204. package/runtime/shared/blocks/index.ts +13 -0
  205. package/runtime/shared/blocks/messages/de.json +4 -0
  206. package/runtime/shared/blocks/messages/en.json +4 -0
  207. package/runtime/shared/blocks/render.ts +134 -0
  208. package/runtime/shared/blocks/validate.ts +165 -0
  209. package/runtime/shared/content.ts +197 -0
  210. package/runtime/shared/examples.ts +20 -0
  211. package/runtime/shared/html.ts +10 -0
  212. package/runtime/shared/languages.ts +94 -0
  213. package/runtime/shared/page.css +101 -0
  214. package/runtime/shared/pages.ts +114 -0
  215. package/runtime/shared/questions/e2e.ts +115 -0
  216. package/runtime/shared/questions/engine.ts +209 -0
  217. package/runtime/shared/questions/index.ts +39 -0
  218. package/runtime/shared/questions/messages/de.json +26 -0
  219. package/runtime/shared/questions/messages/en.json +26 -0
  220. package/runtime/shared/questions/question.ts +430 -0
  221. package/runtime/shared/questions/question.v1.schema.json +342 -0
  222. package/runtime/shared/questions/questions.css +192 -0
  223. package/runtime/shared/questions/render.ts +247 -0
  224. package/runtime/shared/questions/validate.ts +518 -0
  225. package/runtime/shared/schema.ts +44 -0
  226. package/runtime/shared/text/check.ts +68 -0
  227. package/runtime/shared/text/formula.ts +69 -0
  228. package/runtime/shared/text/markdown.ts +185 -0
  229. package/runtime/shared/text/text.v1.schema.json +34 -0
  230. package/runtime/shared/vue-code.ts +395 -0
  231. package/runtime/shared/vue.ts +38 -0
@@ -0,0 +1,659 @@
1
+ # Building Blocks
2
+
3
+ The explainer's extension of the generic architecture (`docs/arc42/` at the root of `lernapps/app-templates`): an
4
+ explainer of chapters of pages with an exercise on every page, whose build step, rendering, validation, client module,
5
+ islands, generated exercises, tutor handoff, task numbers and tests the runtime `runtime/explainer/` ships, and whose
6
+ creator writes the content as YAML, data files and, only where no block fits, Vue components under the component
7
+ contract (`explainer-only-content`, `prefer-built-in-block`); `src/main.ts` stays as generated. It reuses the quiz's
8
+ question types for its quiz-type exercises. Every page is complete without JavaScript
9
+ (`qg-explainer-readable-without-js`). The creator's assistant follows the skill `lernapps-explainer` (`if-skills`). An
10
+ explainer app writes no architecture of its own: the content schema fixes its structure.
11
+
12
+ ## Level 1 (explainer)
13
+
14
+ ```arc42
15
+ :::diagram
16
+ id: building-blocks-explainer-level-1
17
+ view: building-block
18
+ notation: mermaid
19
+ :::
20
+ ```
21
+
22
+ ```mermaid
23
+ flowchart TB
24
+ bb-tooling["Tooling helpers"]
25
+ bb-archetype-runtime["Explainer runtime"]
26
+ subgraph bb-runtime["Shared runtime"]
27
+ bb-blocks["Content blocks"]
28
+ bb-questions["Questions"]
29
+ end
30
+ bb-explainer-content["Content"]
31
+ bb-explainer-data["Data files (optional)"]
32
+ bb-explainer-components["Vue components (optional)"]
33
+ bb-archetype-runtime -->|"if-shared-runtime"| bb-runtime
34
+ bb-archetype-runtime -->|"if-questions"| bb-questions
35
+ bb-archetype-runtime -->|"if-blocks"| bb-blocks
36
+ bb-archetype-runtime -->|"if-component-rules"| bb-component-rules
37
+ bb-archetype-runtime -->|"if-preset"| bb-tooling
38
+ ```
39
+
40
+ ## Level 2: the explainer runtime
41
+
42
+ ```arc42
43
+ :::diagram
44
+ id: building-blocks-explainer-runtime
45
+ view: building-block
46
+ notation: mermaid
47
+ :::
48
+ ```
49
+
50
+ ```mermaid
51
+ flowchart TB
52
+ subgraph bb-archetype-runtime["Explainer runtime"]
53
+ bb-build["Build step"]
54
+ bb-validation["Content validation"]
55
+ bb-render["Page rendering"]
56
+ bb-shell["Client module"]
57
+ bb-islands["Component islands"]
58
+ bb-generated-exercises["Generated exercises"]
59
+ bb-open-tasks["Open tasks"]
60
+ bb-scope["Scope"]
61
+ bb-tutor-handoff["Tutor handoff"]
62
+ bb-task-numbers["Task numbers"]
63
+ bb-video["Video block"]
64
+ bb-self-assessment["Self-assessment"]
65
+ bb-e2e["End-to-end tests"]
66
+ bb-runtime-texts["Runtime texts"]
67
+ end
68
+ bb-explainer-content["Content"]
69
+ bb-explainer-data["Data files"]
70
+ bb-explainer-components["Vue components"]
71
+ bb-tooling["Tooling helpers"]
72
+ bb-build -->|"if-archetype-content"| bb-explainer-content
73
+ bb-build -->|"if-exercise-logic"| bb-explainer-components
74
+ bb-build -->|"if-tutor-files"| bb-tutor-handoff
75
+ bb-validation -->|"if-archetype-content"| bb-explainer-content
76
+ bb-validation -->|"if-data-files"| bb-explainer-data
77
+ bb-render -->|"if-archetype-content"| bb-explainer-content
78
+ bb-render -->|"if-vue-component"| bb-explainer-components
79
+ bb-render -->|"if-runtime-texts"| bb-runtime-texts
80
+ bb-shell -->|"if-task-numbers"| bb-task-numbers
81
+ bb-shell -->|"if-tutor-files"| bb-tutor-handoff
82
+ bb-shell -->|"if-runtime-texts"| bb-runtime-texts
83
+ bb-shell -->|"if-device-storage"| bb-tooling
84
+ bb-shell -->|"if-a11y"| bb-tooling
85
+ bb-islands -->|"if-vue-component"| bb-explainer-components
86
+ bb-islands -->|"if-task-numbers"| bb-task-numbers
87
+ bb-generated-exercises -->|"if-archetype-content"| bb-explainer-content
88
+ bb-open-tasks -->|"if-archetype-content"| bb-explainer-content
89
+ bb-scope -->|"if-archetype-content"| bb-explainer-content
90
+ bb-generated-exercises -->|"if-data-files"| bb-explainer-data
91
+ bb-tutor-handoff -->|"if-archetype-content"| bb-explainer-content
92
+ bb-tutor-handoff -->|"if-exercise-logic"| bb-explainer-components
93
+ bb-tutor-handoff -->|"if-task-numbers"| bb-task-numbers
94
+ bb-video -->|"if-runtime-texts"| bb-runtime-texts
95
+ bb-video -->|"if-device-storage"| bb-tooling
96
+ bb-build -->|"if-self-assessment"| bb-self-assessment
97
+ bb-shell -->|"if-self-assessment"| bb-self-assessment
98
+ bb-self-assessment -->|"if-archetype-content"| bb-explainer-content
99
+ bb-self-assessment -->|"if-tutor-files"| bb-tutor-handoff
100
+ bb-self-assessment -->|"if-task-numbers"| bb-task-numbers
101
+ bb-self-assessment -->|"if-runtime-texts"| bb-runtime-texts
102
+ bb-self-assessment -->|"if-device-storage"| bb-tooling
103
+ bb-e2e -->|"if-archetype-content"| bb-explainer-content
104
+ bb-e2e -->|"if-runtime-texts"| bb-runtime-texts
105
+ ```
106
+
107
+ ## Explainer runtime
108
+
109
+ Built. The runtime of every explainer, which the app does not change: it validates and renders the content into
110
+ static pages, writes the tutor pages, makes the exercises live in the browser and brings the end-to-end tests.
111
+
112
+ ```arc42
113
+ :::building-block
114
+ id: bb-archetype-runtime
115
+ title: Explainer runtime
116
+ path: runtime/explainer
117
+ technology: TypeScript, Vite plugin, Vue
118
+ requires: if-shared-runtime, if-preset, if-questions, if-blocks, if-component-rules
119
+ implements: concept-archetype-extension
120
+ :::
121
+ ```
122
+
123
+ ### Build step
124
+
125
+ Built. The plugin `explainer()` reads and validates the content, runs the component rules and the property test of
126
+ each generated exercise and exercise component over fixed seeds, and only then writes every page in each language,
127
+ the tutor pages where the tutor is on, and the 404 page; `vp dev` serves the same pages.
128
+
129
+ ```arc42
130
+ :::building-block
131
+ id: bb-build
132
+ title: Build step
133
+ parent: bb-archetype-runtime
134
+ path: runtime/explainer/plugin.ts
135
+ technology: Vite plugin
136
+ requires: if-archetype-content, if-component-rules, if-exercise-logic, if-tutor-files, if-self-assessment
137
+ :::
138
+ ```
139
+
140
+ #### Build plugin interface
141
+
142
+ The app's vite.config.ts adds `explainer()` to the tooling preset.
143
+
144
+ ```arc42
145
+ :::interface
146
+ id: if-build-plugin
147
+ title: explainer() Vite plugin
148
+ provider: bb-build
149
+ protocol: TypeScript import, @lernapps/app-templates/explainer/plugin
150
+ path: runtime/explainer/plugin.ts
151
+ :::
152
+ ```
153
+
154
+ ### Content validation
155
+
156
+ Built. Validates every content file against its part of the schema, then what a schema cannot say (complete order
157
+ lists, depth, unique ids, complete languages, restricted Markdown, formulas in the command list, sources and
158
+ licences, links to pages and sections, an exercise on every page, components and their props), one line per problem
159
+ with file and line.
160
+
161
+ ```arc42
162
+ :::building-block
163
+ id: bb-validation
164
+ title: Content validation
165
+ parent: bb-archetype-runtime
166
+ path: runtime/explainer/load.ts
167
+ technology: Ajv, JSON Schema, YAML
168
+ requires: if-archetype-content, if-data-files, if-component-rules, if-questions
169
+ :::
170
+ ```
171
+
172
+ ### Page rendering
173
+
174
+ Built. Renders every YAML page to static HTML in each language: text in restricted Markdown, formulas as MathML,
175
+ figures as SVG, tables, timelines, code, videos as links (`bb-video`), the exercises with their solution folded (a generated one with three worked
176
+ tasks), the navigation and the glossary, and each Vue component's island as the server renderer drew it, with its
177
+ `nojs` text. Without JavaScript every page is complete.
178
+
179
+ ```arc42
180
+ :::building-block
181
+ id: bb-render
182
+ title: Page rendering
183
+ parent: bb-archetype-runtime
184
+ path: runtime/explainer/render.ts
185
+ technology: TypeScript, Temml, highlight.js, Vue server renderer
186
+ requires: if-archetype-content, if-vue-component, if-runtime-texts, if-questions
187
+ implements: concept-readable-without-js, concept-accessibility
188
+ :::
189
+ ```
190
+
191
+ #### Pages interface
192
+
193
+ Learners, teachers and parents read the explainer at `lernapps.net/<repo>/` (and `/<language>/`): the start page, a
194
+ page per chapter, the content pages at `<chapter>/<page>/` and the glossary, with `?<id>=<n>` for the task number of
195
+ an exercise; the catalog entry and its topics link there.
196
+
197
+ ```arc42
198
+ :::interface
199
+ id: if-pages
200
+ title: Explainer pages at lernapps.net/<repo>/
201
+ provider: bb-render
202
+ protocol: HTML pages over HTTPS
203
+ path: runtime/explainer/routes.ts
204
+ :::
205
+ ```
206
+
207
+ ### Client module
208
+
209
+ Built. Makes a page's exercises live: answered before the solution shows, the reason of a wrong answer, "Neue
210
+ Aufgabe", the task numbers in the address; the tutor button; and starts the videos (`bb-video`). It keeps only the
211
+ learner's chosen way to the tutor and their choice to load videos directly on the device, and the needs the learner chose for the tutor (`dec-tutor-needs`), for every explainer or
212
+ for this app only; the last result of a self-assessment is kept by `bb-self-assessment`, which it starts.
213
+
214
+ ```arc42
215
+ :::building-block
216
+ id: bb-shell
217
+ title: Client module
218
+ parent: bb-archetype-runtime
219
+ path: runtime/explainer/explainer.ts
220
+ technology: TypeScript, DOM
221
+ requires: if-questions, if-task-numbers, if-tutor-files, if-runtime-texts, if-device-storage, if-a11y, if-self-assessment
222
+ implements: concept-accessibility, concept-determinism
223
+ :::
224
+ ```
225
+
226
+ #### Start interface
227
+
228
+ The app's `src/main.ts` imports the explainer's style and calls `startExplainer()`.
229
+
230
+ ```arc42
231
+ :::interface
232
+ id: if-start
233
+ title: startExplainer()
234
+ provider: bb-shell
235
+ protocol: TypeScript import, @lernapps/app-templates/explainer
236
+ path: runtime/explainer/explainer.ts
237
+ :::
238
+ ```
239
+
240
+ ### Component islands
241
+
242
+ Built. Only on a page with a component: loads Vue, hydrates each component's island with the props of the page's
243
+ language, gives an exercise component the seed of its task number, takes the answer it emits, checks it on "Prüfen"
244
+ with its logic module, and shows the feedback (`dec-explainer-checks`). On a self-assessment's page it hydrates the
245
+ components once the self-assessment put them into the page, shows the task the run draws, and judges each answer
246
+ silently, without "Prüfen", for "Auswerten" (`dec-self-assessment`).
247
+
248
+ ```arc42
249
+ :::building-block
250
+ id: bb-islands
251
+ title: Component islands
252
+ parent: bb-archetype-runtime
253
+ path: runtime/explainer/islands.ts
254
+ technology: Vue hydration
255
+ requires: if-vue-component, if-task-numbers
256
+ :::
257
+ ```
258
+
259
+ ### Generated exercises
260
+
261
+ Built. Exercises drawn from a seed (`generated`), the explainer's own notation: the same code gives three worked tasks
262
+ at build, runs the property test over fixed seeds and draws a live task in the browser, so both agree; their answers
263
+ are checked through the question types' checker.
264
+ The texts of a quiz-type exercise are plain text in which `$…$` is a formula, rendered as MathML at build and checked
265
+ like every formula.
266
+
267
+ ```arc42
268
+ :::building-block
269
+ id: bb-generated-exercises
270
+ title: Generated exercises
271
+ parent: bb-archetype-runtime
272
+ path: runtime/explainer/generated
273
+ technology: TypeScript, exact fractions
274
+ requires: if-archetype-content, if-data-files, if-questions
275
+ implements: concept-determinism
276
+ :::
277
+ ```
278
+
279
+ ### Open tasks
280
+
281
+ Built. An open task (`open-task`) is answered in the learner's own words, typed or written on paper and photographed,
282
+ then compared with the points of a good answer, which the learner ticks: all right, some partly right (`teilweise`),
283
+ none wrong, the missed points named P1, P2 …; the model answer opens after the learner's own answer
284
+ (`dec-open-task`). Each content point names its source, a section of the app, which the build checks as it checks every
285
+ link. The same client part runs on a content page and in a self-assessment, where the ticks are the answer.
286
+
287
+ ```arc42
288
+ :::building-block
289
+ id: bb-open-tasks
290
+ title: Open tasks
291
+ parent: bb-archetype-runtime
292
+ path: runtime/explainer/open-task.ts
293
+ technology: TypeScript, DOM
294
+ requires: if-archetype-content
295
+ :::
296
+ ```
297
+
298
+ ### Scope
299
+
300
+ Built, optional per app, required by lernapps/practice. The scope is the whole learning path for a tutor, written by
301
+ the creator's assistant in its own words from the material: `scope` in each page's YAML says what the page teaches,
302
+ optionally `scope` in a chapter's what it is about, and `scope` in `src/app.yaml` what the app is about and what
303
+ does not belong to the scope (`dec-scope`). With the app's, the build requires a page's on every page, and
304
+ writes a scope per content page (`<chapter>/<page>/scope/`: its text and what does not belong to the scope), per chapter
305
+ (`<chapter>/scope/`: all its pages) and for the whole app (`scope/`), in each language, each linking one level up, as
306
+ Markdown and a page for no menu and no search engine; every tutor page links its page's scope.
307
+
308
+ ```arc42
309
+ :::building-block
310
+ id: bb-scope
311
+ title: Scope
312
+ parent: bb-archetype-runtime
313
+ path: runtime/explainer/scope.ts
314
+ technology: TypeScript, Markdown
315
+ requires: if-archetype-content
316
+ :::
317
+ ```
318
+
319
+ ### Tutor handoff
320
+
321
+ Built, optional per app. No model runs in the browser: after the learner's click the app hands over to the learner's
322
+ own assistant on one of two ways (`dec-tutor-page`), once the learner has switched an assistant on, on the start page
323
+ or beside the title of a content page, kept on the device (`dec-tutor-assistant`). Both ways name one address, the
324
+ page's tutor page `<chapter>/<page>/tutor/`, with where the learner is in its query: the page's task numbers as its
325
+ own address has them (`?<id>=<n>`); way B adds the exercise (`uebung`) and its section (`abschnitt`). Way A, a bar
326
+ beside the title of each content page, "Mit <name> lernen", opens the assistant with one sentence that asks for help
327
+ with the page, and the tutor page tells the tutor to lead through the page first. Way B, "Hilfe" ("Warum?" after a wrong answer) in the row of "Prüfen" and "Lösung" of every exercise, adds the
328
+ task, the input with its tries and the app's message word for word; without a chat it opens the assistant with them
329
+ (B1), on a page a tutor opened it copies them for the running chat (B2). "Zurück zum Tutor", at the top and the end of a
330
+ page a tutor opened, leads back with the page's tutor page and the result of each task of the session on the
331
+ clipboard. A chat assistant fetches only addresses its user wrote into the conversation, so the one
332
+ address the tutor needs comes in the prompt or in a line the learner pastes. The tutor page is the one source of what
333
+ the tutor gets: a short visible guide, then the page's content and its exercises with every task number, a
334
+ `generated` one once as its template with a table of each number's inputs and outputs (the values, the solution, what
335
+ each typical mistake gives), an exercise component with its `about` and the same table from its exercise logic
336
+ (`dec-component-table`), or without one each number's task and solution from its `describe` as far as they fit into
337
+ 64 kB; its Markdown lies beside it for copying. Under "Besondere Anforderungen" it ends with a short section for each
338
+ need a learner can choose, which holds when the query names it in `bedarf=` (`dec-tutor-needs`). No
339
+ prompt and no line carries a rule for the tutor, and the app writes no `llms.txt`.
340
+
341
+ ```arc42
342
+ :::building-block
343
+ id: bb-tutor-handoff
344
+ title: Tutor handoff
345
+ parent: bb-archetype-runtime
346
+ path: runtime/explainer/tutor.ts
347
+ technology: TypeScript, static Markdown
348
+ requires: if-archetype-content, if-exercise-logic, if-task-numbers
349
+ implements: concept-tutor
350
+ :::
351
+ ```
352
+
353
+ #### Tutor pages and the learner's lines
354
+
355
+ The start prompt of way A, the lines of "Hilfe" (way B) and the result line of "Zurück zum Tutor" name the page's
356
+ tutor page with the page's task numbers, those of way B also with `uebung` and `abschnitt`, those that start a chat
357
+ (way A, B1) also with the learner's needs in `bedarf`, absolute; the start prompt adds "Meine Muttersprache ist …"
358
+ when `andere-muttersprache` is chosen; the lines of way B and the result line
359
+ add the task, the input, the app's message word for word and how far the learner got. A chat assistant reads the
360
+ tutor page without running JavaScript; its Markdown (`index.md`) is the text "Ein anderer: Text kopieren" copies. The
361
+ page ids, exercise ids, the query parameters, the ids of the needs, the tutor page's structure, the result line and the lines of way B are
362
+ a contract with every tutor.
363
+
364
+ ```arc42
365
+ :::interface
366
+ id: if-tutor-files
367
+ title: Tutor pages, start prompt and the learner's lines
368
+ provider: bb-tutor-handoff
369
+ protocol: Prompt text (provider link or clipboard), result line and the lines of way B (clipboard), static HTML and Markdown over HTTPS
370
+ path: runtime/explainer/tutorpage.ts
371
+ :::
372
+ ```
373
+
374
+ ### Task numbers
375
+
376
+ Built: the distinct tasks of a seeded exercise numbered 1 to N, the one identifier learner, tutor and address
377
+ (`?<id>=<n>`) share.
378
+
379
+ ```arc42
380
+ :::building-block
381
+ id: bb-task-numbers
382
+ title: Task numbers
383
+ parent: bb-archetype-runtime
384
+ path: runtime/explainer/tasks.ts
385
+ technology: TypeScript
386
+ implements: concept-determinism
387
+ :::
388
+ ```
389
+
390
+ #### Task numbers interface
391
+
392
+ The client module, the islands and the tutor handoff turn a task number into its seed and back.
393
+
394
+ ```arc42
395
+ :::interface
396
+ id: if-task-numbers
397
+ title: Task numbers and their seeds
398
+ provider: bb-task-numbers
399
+ protocol: TypeScript import
400
+ path: runtime/explainer/tasks.ts
401
+ :::
402
+ ```
403
+
404
+ ### Video block
405
+
406
+ Built. A YouTube video behind two clicks (`dec-explainer-video-two-click`): the page rendering writes the video as a
407
+ figure with its title as a link to youtube.com, its channel and a note that playing it sends data, among them the IP
408
+ address, to YouTube/Google; nothing loads from another host. The client module shows "Video abspielen", which puts
409
+ the player of youtube-nocookie.com into the figure, and "Videos immer direkt laden", a standing choice kept on the
410
+ device until the learner clears it. The tutor page names the video in its section as a link with its title and
411
+ channel, as it names a figure by its alt text.
412
+
413
+ ```arc42
414
+ :::building-block
415
+ id: bb-video
416
+ title: Video block
417
+ parent: bb-archetype-runtime
418
+ path: runtime/explainer/video.ts
419
+ technology: TypeScript, DOM, iframe
420
+ requires: if-runtime-texts, if-device-storage
421
+ implements: concept-readable-without-js
422
+ :::
423
+ ```
424
+
425
+ #### Video player request
426
+
427
+ The one request to another host an explainer makes on its own: the address of a video's player,
428
+ `https://www.youtube-nocookie.com/embed/<id>`, in an iframe named by the video's title, with the app's origin as its
429
+ only referrer; sent after the learner's click, or on opening the page once they chose to load videos directly.
430
+
431
+ ```arc42
432
+ :::interface
433
+ id: if-video-player
434
+ title: Video player request to youtube-nocookie.com
435
+ provider: bb-video
436
+ protocol: HTML iframe over HTTPS, after a click
437
+ path: runtime/explainer/video.ts
438
+ :::
439
+ ```
440
+
441
+ ### Self-assessment
442
+
443
+ Built. An optional self-assessment (`dec-self-assessment`), declared per chapter: `selfAssessment` in its
444
+ `chapter.yaml`, its pages with the exercises it asks, each by `/<chapter>/<page>/#<exercise>` of the chapter (a
445
+ generated one, a question type or an exercise component). The app's is every chapter's in the order of the
446
+ chapters (`einschaetzung/`), a chapter's its own (`<chapter>/einschaetzung/`; in an app with one chapter, the app's is
447
+ the chapter's). The validation checks each reference, that it is of the chapter and that an exercise is listed once;
448
+ the build notes a page of it with fewer than two or more than five exercises. The page rendering writes its page and
449
+ the offer on the start page or the chapter's page, every part of them in a `<template>` until the client module puts it
450
+ into the page, and its tutor page `<assessment>/tutor/`, which makes the tutor a guide (`dec-self-assessment-tutor`).
451
+ In the browser a run draws a new task of each generated exercise and exercise component (the islands show a
452
+ component's task and judge its answer silently, `bb-islands`), shows the pages one by one without "Prüfen", checks
453
+ all exercises together at "Auswerten" and shows for each content page in its scope whether the learner probably knows
454
+ it, should look at it again or should read it; a page with no exercise in it is not asked. The last result stays on the
455
+ device, one per self-assessment (`dec-self-assessment-result`); a run a tutor opened keeps nothing and ends in "Zurück
456
+ zum Tutor".
457
+
458
+ ```arc42
459
+ :::building-block
460
+ id: bb-self-assessment
461
+ title: Self-assessment
462
+ parent: bb-archetype-runtime
463
+ path: runtime/explainer/assessment.ts
464
+ technology: TypeScript, DOM, static HTML and Markdown
465
+ requires: if-archetype-content, if-tutor-files, if-task-numbers, if-questions, if-runtime-texts, if-device-storage
466
+ implements: concept-tutor
467
+ :::
468
+ ```
469
+
470
+ #### Self-assessment interface
471
+
472
+ The build step writes the page, the offer and the tutor page of each self-assessment (`assessment-page.ts`); the client
473
+ module starts it (`assessment-client.ts`). Its addresses, the query of its tutor page (`?a1=17-falsch-F1&a2=richtig&a3=teilweise-P2`:
474
+ each exercise by its key in order, its task number, `richtig`, `teilweise`, `falsch` or `offen`, the typical mistake,
475
+ the points a partly right answer missed), the lines of
476
+ its prompt and of "Zurück zum Tutor", and the result kept under `self-assessment` or `self-assessment:<chapter>` are a
477
+ contract with every tutor and with the result kept on a device.
478
+
479
+ ```arc42
480
+ :::interface
481
+ id: if-self-assessment
482
+ title: Self-assessment page, its tutor page and the result kept on the device
483
+ provider: bb-self-assessment
484
+ protocol: Static HTML and Markdown over HTTPS, prompt text (provider link or clipboard), JSON in localStorage
485
+ path: runtime/explainer/assessment.ts
486
+ :::
487
+ ```
488
+
489
+ ### End-to-end tests
490
+
491
+ Built. `explainerTests()` derives from the content what every page must show, checks it without JavaScript in every
492
+ language, and with JavaScript one exercise per type and page and every component.
493
+
494
+ ```arc42
495
+ :::building-block
496
+ id: bb-e2e
497
+ title: End-to-end tests
498
+ parent: bb-archetype-runtime
499
+ path: runtime/explainer/e2e.ts
500
+ technology: Playwright
501
+ requires: if-archetype-content, if-runtime-texts, if-questions
502
+ :::
503
+ ```
504
+
505
+ #### Tests interface
506
+
507
+ The app's `e2e/explainer.e2e.ts` only calls `explainerTests()`.
508
+
509
+ ```arc42
510
+ :::interface
511
+ id: if-e2e-tests
512
+ title: explainerTests()
513
+ provider: bb-e2e
514
+ protocol: TypeScript import, @lernapps/app-templates/explainer/e2e
515
+ path: runtime/explainer/e2e.ts
516
+ :::
517
+ ```
518
+
519
+ ### Runtime texts
520
+
521
+ Built. The explainer's own learner texts in German and English; an app may declare only these languages.
522
+
523
+ ```arc42
524
+ :::building-block
525
+ id: bb-runtime-texts
526
+ title: Runtime texts
527
+ parent: bb-archetype-runtime
528
+ path: runtime/explainer/messages.ts
529
+ technology: JSON per language
530
+ implements: concept-languages
531
+ :::
532
+ ```
533
+
534
+ #### Runtime texts interface
535
+
536
+ The rendering, the client module and the tests take the explainer's texts by key and language.
537
+
538
+ ```arc42
539
+ :::interface
540
+ id: if-runtime-texts
541
+ title: Explainer texts by language
542
+ provider: bb-runtime-texts
543
+ protocol: TypeScript import, JSON
544
+ path: runtime/explainer/messages
545
+ :::
546
+ ```
547
+
548
+ ## Content
549
+
550
+ Built in the template, written by the creator: `src/app.yaml` and a folder per chapter with `chapter.yaml` and its
551
+ pages of blocks, each page with an exercise; optionally the scope for the tutor in them (`bb-scope`).
552
+
553
+ ```arc42
554
+ :::building-block
555
+ id: bb-explainer-content
556
+ title: Content
557
+ technology: YAML, restricted Markdown, LaTeX subset
558
+ implements: concept-languages
559
+ path: explainer/src/app.yaml
560
+ :::
561
+ ```
562
+
563
+ ### Content interface
564
+
565
+ The explainer's content: build, validation, rendering, generated exercises, tutor pages and tests read it only through
566
+ its schema.
567
+
568
+ ```arc42
569
+ :::interface
570
+ id: if-archetype-content
571
+ title: Content YAML (explainer.v1 schema)
572
+ provider: bb-explainer-content
573
+ protocol: YAML, JSON Schema
574
+ path: runtime/explainer/explainer.v1.schema.json
575
+ :::
576
+ ```
577
+
578
+ ## Data files
579
+
580
+ Optional, not in the template: `src/data/<name>.yaml`, lists of records a timeline or a generated exercise draws
581
+ from, so each fact is written once.
582
+
583
+ ```arc42
584
+ :::ignore H014 optional and not in the template; an app that adds it sets its path in its own architecture
585
+ :::
586
+ :::building-block
587
+ id: bb-explainer-data
588
+ title: Data files
589
+ technology: YAML
590
+ :::
591
+ ```
592
+
593
+ ### Data files interface
594
+
595
+ A block names a data file by its name (`data: ereignisse`, `from: { data: elemente }`).
596
+
597
+ ```arc42
598
+ :::ignore H014 optional and not in the template; an app that adds it sets its path in its own architecture
599
+ :::
600
+ :::interface
601
+ id: if-data-files
602
+ title: Data files src/data/<name>.yaml
603
+ provider: bb-explainer-data
604
+ protocol: YAML records
605
+ :::
606
+ ```
607
+
608
+ ## Vue components
609
+
610
+ Optional: `src/components/<Name>.vue` with its test, for what no block can express, under the component rules and
611
+ within a fixed size each (`dec-explainer-component-size`); an exercise component adds its logic module. The template
612
+ has one as an example, `ProzentFeld` on the page `prozent/prozentwert`: a field of G squares of which the learner
613
+ colours p %, with the exercise declared for the tutor (`dec-component-table`), so that a hand test shows how a tutor
614
+ deals with its typical mistakes.
615
+
616
+ ```arc42
617
+ :::building-block
618
+ id: bb-explainer-components
619
+ title: Vue components
620
+ path: explainer/src/components
621
+ technology: Vue single-file components, TypeScript
622
+ implements: concept-component-rules
623
+ :::
624
+ ```
625
+
626
+ ### Component contract
627
+
628
+ A `vue-component` block names the component with its typed props and a `nojs` text; an exercise component is the
629
+ input of its task: it takes the prop `seed`, emits `answer` with the learner's answer each time it changes, may take
630
+ the prop `checked`, and draws no "Prüfen" of its own, which the explainer draws under it (`dec-explainer-checks`).
631
+
632
+ ```arc42
633
+ :::interface
634
+ id: if-vue-component
635
+ title: Component contract (props, nojs, answer)
636
+ provider: bb-explainer-components
637
+ path: explainer/src/components/ProzentFeld.vue
638
+ protocol: Vue props and events, YAML block
639
+ :::
640
+ ```
641
+
642
+ ### Exercise logic module
643
+
644
+ An exercise component's `<Name>.ts` exports `generate`, `check`, `solution`, `wrong`, `reason`, `describe` and
645
+ `about`: the exercise contract, which the build tests over fixed seeds, the islands call on "Prüfen" and the tutor
646
+ pages read (the task and its solution as text
647
+ in each language, and what the exercise is for a tutor). For its table of tasks it adds `template`, `params`,
648
+ `mistakes` and `show`, all four or none (`dec-component-table`); the build checks them against `check` for every task
649
+ number and gives a hint for a component without them.
650
+
651
+ ```arc42
652
+ :::interface
653
+ id: if-exercise-logic
654
+ title: Exercise logic module (exercise contract)
655
+ provider: bb-explainer-components
656
+ path: explainer/src/components/ProzentFeld.ts
657
+ protocol: TypeScript module exports
658
+ :::
659
+ ```