@webwriter/quiz 1.1.5 → 2.0.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 (165) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +778 -0
  3. package/assets/icons/grip-horizontal.svg +8 -0
  4. package/assets/icons/grip-vertical.svg +8 -0
  5. package/assets/icons/truefalse.svg +6 -0
  6. package/custom-elements.json +2779 -0
  7. package/custom.d.ts +5 -1
  8. package/dist/api/index.d.ts +27 -0
  9. package/dist/api/index.js +40 -0
  10. package/dist/migrate.js +1434 -0
  11. package/dist/widgets/webwriter-choice-item.js +4095 -3818
  12. package/dist/widgets/webwriter-choice.js +2179 -5598
  13. package/dist/widgets/webwriter-gap-item.js +3987 -0
  14. package/dist/widgets/webwriter-gap.js +2908 -0
  15. package/dist/widgets/webwriter-mark.js +4347 -3368
  16. package/dist/widgets/webwriter-order-item.js +4038 -3197
  17. package/dist/widgets/webwriter-order.js +2885 -4200
  18. package/dist/widgets/webwriter-pairing-item.js +1070 -2330
  19. package/dist/widgets/webwriter-pairing.js +2968 -4231
  20. package/dist/widgets/webwriter-quiz-hint.js +2469 -0
  21. package/dist/widgets/webwriter-quiz.js +3337 -4364
  22. package/dist/widgets/webwriter-speech.js +6100 -5120
  23. package/dist/widgets/webwriter-task-prompt.js +4787 -2068
  24. package/dist/widgets/webwriter-task.js +2264 -8157
  25. package/dist/widgets/webwriter-text.js +4075 -4132
  26. package/dist/widgets/webwriter-true-false.js +4674 -0
  27. package/editing-config.json +106 -0
  28. package/lit-localize.json +20 -14
  29. package/localization/de.xlf +395 -65
  30. package/localization/es.xlf +396 -61
  31. package/localization/fr.xlf +396 -61
  32. package/localization/generated/de.ts +97 -15
  33. package/localization/generated/es.ts +97 -15
  34. package/localization/generated/fr.ts +98 -16
  35. package/localization/generated/index.d.ts +5 -0
  36. package/localization/generated/it.ts +96 -14
  37. package/localization/generated/locale-codes.js +0 -48
  38. package/localization/generated/nl.ts +99 -17
  39. package/localization/it.xlf +396 -61
  40. package/localization/nl.xlf +396 -61
  41. package/package.json +120 -254
  42. package/scripts/apply-translations.mjs +97 -0
  43. package/src/api/index.ts +72 -0
  44. package/src/lib/artboard.ts +686 -0
  45. package/src/lib/draggable-grid.ts +432 -0
  46. package/src/lib/encrypted-property.ts +202 -0
  47. package/src/lib/hint-popup.ts +152 -0
  48. package/src/lib/utils.ts +97 -0
  49. package/src/lib/webwriter-quirks.ts +121 -0
  50. package/src/migrate/index.ts +622 -0
  51. package/src/migrate/legacy-solution.ts +362 -0
  52. package/src/snippets/order.html +8 -7
  53. package/src/widgets/choice/webwriter-choice-item.ts +316 -0
  54. package/src/widgets/choice/webwriter-choice.ts +328 -0
  55. package/src/widgets/choice/webwriter-true-false.ts +113 -0
  56. package/src/widgets/dnd/webwriter-dnd-draggable.ts +28 -0
  57. package/src/widgets/dnd/webwriter-dnd-dropzone.ts +9 -0
  58. package/src/widgets/dnd/webwriter-dnd.ts +36 -0
  59. package/src/widgets/gap/webwriter-gap-item.ts +250 -0
  60. package/src/widgets/gap/webwriter-gap.ts +447 -0
  61. package/src/widgets/order/webwriter-order-item.ts +243 -0
  62. package/src/widgets/order/webwriter-order.ts +282 -0
  63. package/src/widgets/pairing/webwriter-pairing-item.ts +46 -0
  64. package/src/widgets/pairing/webwriter-pairing.ts +934 -0
  65. package/src/widgets/speech/audio-player.ts +325 -0
  66. package/src/widgets/speech/audio-recorder.ts +285 -0
  67. package/src/widgets/speech/waveform.ts +113 -0
  68. package/src/widgets/speech/webwriter-speech.ts +87 -0
  69. package/src/widgets/webwriter-mark.ts +366 -424
  70. package/src/widgets/webwriter-quiz-hint.ts +36 -0
  71. package/src/widgets/webwriter-quiz.ts +318 -280
  72. package/src/widgets/webwriter-task-prompt.ts +43 -18
  73. package/src/widgets/webwriter-task.ts +212 -551
  74. package/src/widgets/webwriter-text.ts +286 -172
  75. package/tsconfig.api.json +12 -0
  76. package/tsconfig.json +7 -6
  77. package/dist/widgets/webwriter-choice.css +0 -444
  78. package/dist/widgets/webwriter-cloze-gap.css +0 -444
  79. package/dist/widgets/webwriter-cloze-gap.js +0 -5727
  80. package/dist/widgets/webwriter-cloze.css +0 -444
  81. package/dist/widgets/webwriter-cloze.js +0 -3653
  82. package/dist/widgets/webwriter-mark.css +0 -444
  83. package/dist/widgets/webwriter-order.css +0 -444
  84. package/dist/widgets/webwriter-pairing-item.css +0 -444
  85. package/dist/widgets/webwriter-pairing.css +0 -444
  86. package/dist/widgets/webwriter-quiz.css +0 -444
  87. package/dist/widgets/webwriter-task-explainer.css +0 -444
  88. package/dist/widgets/webwriter-task-explainer.js +0 -2769
  89. package/dist/widgets/webwriter-task-hint.css +0 -444
  90. package/dist/widgets/webwriter-task-hint.js +0 -2708
  91. package/dist/widgets/webwriter-task.css +0 -444
  92. package/dist/widgets/webwriter-text.css +0 -444
  93. package/localization/bg.xlf +0 -72
  94. package/localization/cs.xlf +0 -72
  95. package/localization/da.xlf +0 -72
  96. package/localization/el.xlf +0 -72
  97. package/localization/et.xlf +0 -72
  98. package/localization/fi.xlf +0 -72
  99. package/localization/generated/bg.ts +0 -31
  100. package/localization/generated/cs.ts +0 -31
  101. package/localization/generated/da.ts +0 -31
  102. package/localization/generated/el.ts +0 -31
  103. package/localization/generated/et.ts +0 -31
  104. package/localization/generated/fi.ts +0 -31
  105. package/localization/generated/hu.ts +0 -31
  106. package/localization/generated/id.ts +0 -31
  107. package/localization/generated/ja.ts +0 -31
  108. package/localization/generated/ko.ts +0 -31
  109. package/localization/generated/lt.ts +0 -31
  110. package/localization/generated/lv.ts +0 -31
  111. package/localization/generated/nb.ts +0 -31
  112. package/localization/generated/pl.ts +0 -31
  113. package/localization/generated/pt-BR.ts +0 -31
  114. package/localization/generated/pt-PT.ts +0 -31
  115. package/localization/generated/ro.ts +0 -31
  116. package/localization/generated/ru.ts +0 -31
  117. package/localization/generated/sk.ts +0 -31
  118. package/localization/generated/sl.ts +0 -31
  119. package/localization/generated/sv.ts +0 -31
  120. package/localization/generated/tr.ts +0 -31
  121. package/localization/generated/uk.ts +0 -31
  122. package/localization/generated/zh-hans.ts +0 -31
  123. package/localization/hu.xlf +0 -72
  124. package/localization/id.xlf +0 -72
  125. package/localization/ja.xlf +0 -72
  126. package/localization/ko.xlf +0 -72
  127. package/localization/lt.xlf +0 -72
  128. package/localization/lv.xlf +0 -72
  129. package/localization/nb.xlf +0 -72
  130. package/localization/pl.xlf +0 -72
  131. package/localization/pt-BR.xlf +0 -72
  132. package/localization/pt-PT.xlf +0 -72
  133. package/localization/ro.xlf +0 -72
  134. package/localization/ru.xlf +0 -72
  135. package/localization/sk.xlf +0 -72
  136. package/localization/sl.xlf +0 -72
  137. package/localization/sv.xlf +0 -72
  138. package/localization/tr.xlf +0 -72
  139. package/localization/uk.xlf +0 -72
  140. package/localization/zh-hans.xlf +0 -72
  141. package/src/lib/combobox.ts +0 -456
  142. package/src/lib/highlighter-fill.svg +0 -6
  143. package/src/snippets/Beispiel-Choice.html +0 -57
  144. package/src/snippets/Beispiel-Mark.html +0 -14
  145. package/src/snippets/Beispiel-Order.html +0 -52
  146. package/src/snippets/Beispiel-Text.html +0 -20
  147. package/src/snippets/blank.html +0 -1
  148. package/src/snippets/choice.html +0 -8
  149. package/src/snippets/cloze.html +0 -4
  150. package/src/snippets/mark.html +0 -4
  151. package/src/snippets/pairing.html +0 -10
  152. package/src/snippets/speech.html +0 -4
  153. package/src/snippets/text.html +0 -4
  154. package/src/snippets/wordsearch.html +0 -4
  155. package/src/widgets/webwriter-choice-item.ts +0 -257
  156. package/src/widgets/webwriter-choice.ts +0 -353
  157. package/src/widgets/webwriter-cloze-gap.ts +0 -216
  158. package/src/widgets/webwriter-cloze.ts +0 -136
  159. package/src/widgets/webwriter-order-item.ts +0 -441
  160. package/src/widgets/webwriter-order.ts +0 -327
  161. package/src/widgets/webwriter-pairing-item.ts +0 -161
  162. package/src/widgets/webwriter-pairing.ts +0 -193
  163. package/src/widgets/webwriter-speech.ts +0 -273
  164. package/src/widgets/webwriter-task-explainer.ts +0 -43
  165. package/src/widgets/webwriter-task-hint.ts +0 -23
package/package.json CHANGED
@@ -1,260 +1,126 @@
1
1
  {
2
- "name": "@webwriter/quiz",
3
- "version": "1.1.5",
4
- "description": "Add interactive tasks (multiple choice, order, free text, highlighting, or speech input). Make a quiz out of multiple tasks.",
5
- "keywords": [
6
- "webwriter-widget",
7
- "widget-practical",
8
- "widget-lang-en",
9
- "widget-lang-de",
10
- "widget-lang-bg",
11
- "widget-lang-cs",
12
- "widget-lang-da",
13
- "widget-lang-el",
14
- "widget-lang-es",
15
- "widget-lang-et",
16
- "widget-lang-fi",
17
- "widget-lang-fr",
18
- "widget-lang-hu",
19
- "widget-lang-id",
20
- "widget-lang-it",
21
- "widget-lang-ja",
22
- "widget-lang-ko",
23
- "widget-lang-lt",
24
- "widget-lang-lv",
25
- "widget-lang-nb",
26
- "widget-lang-nl",
27
- "widget-lang-pl",
28
- "widget-lang-pt",
29
- "widget-lang-ro",
30
- "widget-lang-ru",
31
- "widget-lang-sk",
32
- "widget-lang-sl",
33
- "widget-lang-sv",
34
- "widget-lang-tr",
35
- "widget-lang-uk",
36
- "widget-lang-zh",
37
- "isced2011-1",
38
- "isced2011-2",
39
- "isced2011-3",
40
- "isced2011-4",
41
- "isced2011-5",
42
- "isced2011-6",
43
- "isced2011-7",
44
- "isced2011-8",
45
- "quiz",
46
- "choice",
47
- "test"
48
- ],
49
- "license": "MIT",
50
- "author": "Frederic Salmen <frederic@fsalmen.de>",
51
- "scripts": {
52
- "prepare": "npx @webwriter/build",
53
- "dev": "npx @webwriter/build dev",
54
- "localize": "lit-localize extract && npx @webwriter/build localize && lit-localize build"
2
+ "name": "@webwriter/quiz",
3
+ "version": "2.0.0",
4
+ "description": "Add interactive tasks (multiple choice, order, free text, highlighting, or speech input). Make a quiz out of multiple tasks.",
5
+ "keywords": [
6
+ "webwriter-widget",
7
+ "widget-practical",
8
+ "widget-lang-en",
9
+ "widget-lang-de",
10
+ "widget-lang-fr",
11
+ "widget-lang-es",
12
+ "widget-lang-it",
13
+ "widget-lang-nl",
14
+ "isced2011-1",
15
+ "isced2011-2",
16
+ "isced2011-3",
17
+ "isced2011-4",
18
+ "isced2011-5",
19
+ "isced2011-6",
20
+ "isced2011-7",
21
+ "isced2011-8",
22
+ "quiz",
23
+ "choice",
24
+ "test"
25
+ ],
26
+ "license": "MIT",
27
+ "author": "Frederic Salmen <frederic@fsalmen.de>",
28
+ "repository": "https://github.com/webwriter-app/quiz",
29
+ "scripts": {
30
+ "prepublishOnly": "npx @webwriter/build && npm run build:api",
31
+ "build:api": "tsc -p tsconfig.api.json",
32
+ "dev": "npx @webwriter/build dev",
33
+ "localize": "npx @lit/localize-tools extract && npx @lit/localize-tools build",
34
+ "localize:apply": "node scripts/apply-translations.mjs"
35
+ },
36
+ "dependencies": {
37
+ "@shoelace-style/shoelace": "^2.20.1",
38
+ "@webwriter/lit": "^2",
39
+ "bootstrap-icons": "^1.13.1",
40
+ "lit": "^3"
41
+ },
42
+ "devDependencies": {
43
+ "@lit/localize-tools": "^0.8",
44
+ "typescript": "~5.9.0"
45
+ },
46
+ "exports": {
47
+ "./api": {
48
+ "types": "./dist/api/index.d.ts",
49
+ "source": "./src/api/index.ts",
50
+ "import": "./dist/api/index.js",
51
+ "default": "./dist/api/index.js"
55
52
  },
56
- "repository": "https://github.com/webwriter-app/quiz",
57
- "dependencies": {
58
- "@shoelace-style/shoelace": "^2.13.1",
59
- "@shopify/draggable": "^1.1.3",
60
- "@webwriter/lit": "^2.1.2",
61
- "bootstrap-icons": "^1.11.3",
62
- "interactjs": "^1.10.26",
63
- "lit": "^3.2.0",
64
- "minimasonry": "^1.3.2",
65
- "sortablejs": "^1.15.2"
53
+ "./widgets/webwriter-quiz.*": {
54
+ "source": "./src/widgets/webwriter-quiz.ts",
55
+ "default": "./dist/widgets/webwriter-quiz.*"
66
56
  },
67
- "devDependencies": {
68
- "@lit/localize": "^0.12.2",
69
- "@lit/localize-tools": "^0.8.0",
70
- "@types/minimasonry": "^1.3.5",
71
- "@types/sortablejs": "^1.15.7"
57
+ "./widgets/webwriter-task.*": {
58
+ "source": "./src/widgets/webwriter-task.ts",
59
+ "default": "./dist/widgets/webwriter-task.*"
72
60
  },
73
- "peerDependencies": {
74
- "@webwriter/phet-simulation": "^1.0.0"
61
+ "./widgets/webwriter-task-prompt.*": {
62
+ "source": "./src/widgets/webwriter-task-prompt.ts",
63
+ "default": "./dist/widgets/webwriter-task-prompt.*"
75
64
  },
76
- "exports": {
77
- "./widgets/webwriter-quiz.*": {
78
- "source": "./src/widgets/webwriter-quiz.ts",
79
- "default": "./dist/widgets/webwriter-quiz.*"
80
- },
81
- "./snippets/blank.html": "./src/snippets/blank.html",
82
- "./snippets/choice.html": "./src/snippets/choice.html",
83
- "./snippets/order.html": "./src/snippets/order.html",
84
- "./snippets/text.html": "./src/snippets/text.html",
85
- "./snippets/mark.html": "./src/snippets/mark.html",
86
- "./snippets/speech.html": "./src/snippets/speech.html",
87
- "./snippets/beispiel-choice.html": "./src/snippets/Beispiel-Choice.html",
88
- "./snippets/beispiel-order.html": "./src/snippets/Beispiel-Order.html",
89
- "./snippets/beispiel-text.html": "./src/snippets/Beispiel-Text.html",
90
- "./snippets/beispiel-mark.html": "./src/snippets/Beispiel-Mark.html",
91
- "./widgets/webwriter-task.*": {
92
- "source": "./src/widgets/webwriter-task.ts",
93
- "default": "./dist/widgets/webwriter-task.*"
94
- },
95
- "./widgets/webwriter-task-prompt.*": {
96
- "source": "./src/widgets/webwriter-task-prompt.ts",
97
- "default": "./dist/widgets/webwriter-task-prompt.*"
98
- },
99
- "./widgets/webwriter-task-hint.*": {
100
- "source": "./src/widgets/webwriter-task-hint.ts",
101
- "default": "./dist/widgets/webwriter-task-hint.*"
102
- },
103
- "./widgets/webwriter-task-explainer.*": {
104
- "source": "./src/widgets/webwriter-task-explainer.ts",
105
- "default": "./dist/widgets/webwriter-task-explainer.*"
106
- },
107
- "./widgets/webwriter-choice.*": {
108
- "source": "./src/widgets/webwriter-choice.ts",
109
- "default": "./dist/widgets/webwriter-choice.*"
110
- },
111
- "./widgets/webwriter-choice-item.*": {
112
- "source": "./src/widgets/webwriter-choice-item.ts",
113
- "default": "./dist/widgets/webwriter-choice-item.*"
114
- },
115
- "./widgets/webwriter-order.*": {
116
- "source": "./src/widgets/webwriter-order.ts",
117
- "default": "./dist/widgets/webwriter-order.*"
118
- },
119
- "./widgets/webwriter-order-item.*": {
120
- "source": "./src/widgets/webwriter-order-item.ts",
121
- "default": "./dist/widgets/webwriter-order-item.*"
122
- },
123
- "./widgets/webwriter-pairing.*": {
124
- "source": "./src/widgets/webwriter-pairing.ts",
125
- "default": "./dist/widgets/webwriter-pairing.*"
126
- },
127
- "./widgets/webwriter-pairing-item.*": {
128
- "source": "./src/widgets/webwriter-pairing-item.ts",
129
- "default": "./dist/widgets/webwriter-pairing-item.*"
130
- },
131
- "./widgets/webwriter-text.*": {
132
- "source": "./src/widgets/webwriter-text.ts",
133
- "default": "./dist/widgets/webwriter-text.*"
134
- },
135
- "./widgets/webwriter-mark.*": {
136
- "source": "./src/widgets/webwriter-mark.ts",
137
- "default": "./dist/widgets/webwriter-mark.*"
138
- },
139
- "./widgets/webwriter-speech.*": {
140
- "source": "./src/widgets/webwriter-speech.ts",
141
- "default": "./dist/widgets/webwriter-speech.*"
142
- },
143
- "./widgets/webwriter-cloze.*": {
144
- "source": "./src/widgets/webwriter-cloze.ts",
145
- "default": "./dist/widgets/webwriter-cloze.*"
146
- },
147
- "./widgets/webwriter-cloze-gap.*": {
148
- "source": "./src/widgets/webwriter-cloze-gap.ts",
149
- "default": "./dist/widgets/webwriter-cloze-gap.*"
150
- },
151
- "./package.json": "./package.json",
152
- "./icon": "./icon.svg"
65
+ "./widgets/webwriter-quiz-hint.*": {
66
+ "source": "./src/widgets/webwriter-quiz-hint.ts",
67
+ "default": "./dist/widgets/webwriter-quiz-hint.*"
153
68
  },
154
- "editingConfig": {
155
- ".": {
156
- "label": {
157
- "_": "Quiz"
158
- },
159
- "description": {
160
- "de": "Fügen Sie interaktive Aufgaben hinzu (Multiple Choice, Reihenfolge, freier Text, Hervorhebung oder Spracheingabe). Erstellen Sie ein Quiz aus mehreren Aufgaben."
161
- }
162
- },
163
- "./widgets/webwriter-choice-item": {
164
- "group": "",
165
- "selectable": false,
166
- "draggable": false,
167
- "isolating": false,
168
- "content": "(p | flow)+",
169
- "uninsertable": true
170
- },
171
- "./widgets/webwriter-choice": {
172
- "group": "answer",
173
- "content": "webwriter-choice-item+",
174
- "uninsertable": true
175
- },
176
- "./widgets/webwriter-order-item": {
177
- "group": "",
178
- "selectable": false,
179
- "draggable": true,
180
- "isolating": false,
181
- "content": "(p | flow)+",
182
- "uninsertable": true
183
- },
184
- "./widgets/webwriter-order": {
185
- "group": "answer",
186
- "content": "webwriter-order-item+",
187
- "uninsertable": true
188
- },
189
- "./widgets/webwriter-pairing": {
190
- "group": "answer",
191
- "isolating": true,
192
- "content": "webwriter-pairing-item+",
193
- "uninsertable": true
194
- },
195
- "./widgets/webwriter-pairing-item": {
196
- "group": "",
197
- "draggable": true,
198
- "isolating": true,
199
- "content": "picture | flow",
200
- "uninsertable": true
201
- },
202
- "./widgets/webwriter-text": {
203
- "group": "answer",
204
- "uninsertable": true
205
- },
206
- "./widgets/webwriter-mark": {
207
- "group": "answer inlinecontainer",
208
- "defining": true,
209
- "isolating": true,
210
- "content": "(text | br | wbr)*",
211
- "marks": "",
212
- "uninsertable": true
213
- },
214
- "./widgets/webwriter-speech": {
215
- "group": "answer",
216
- "uninsertable": true
217
- },
218
- "./widgets/webwriter-cloze": {
219
- "group": "answer",
220
- "isolating": true,
221
- "content": "p",
222
- "uninsertable": true
223
- },
224
- "./widgets/webwriter-cloze-gap": {
225
- "group": "phrasing",
226
- "inline": true,
227
- "isolating": false,
228
- "uninsertable": true
229
- },
230
- "./widgets/webwriter-task": {
231
- "isolating": true,
232
- "content": "webwriter-task-prompt webwriter-task-hint? answer",
233
- "uninsertable": true
234
- },
235
- "./widgets/webwriter-task-prompt": {
236
- "group": "",
237
- "defining": true,
238
- "content": "(p | flow)+",
239
- "uninsertable": true
240
- },
241
- "./widgets/webwriter-task-hint": {
242
- "group": "containerinline",
243
- "defining": true,
244
- "isolating": true,
245
- "content": "p",
246
- "uninsertable": true
247
- },
248
- "./widgets/webwriter-task-explainer": {
249
- "group": "",
250
- "defining": true,
251
- "isolating": true,
252
- "content": "(p | flow)+",
253
- "uninsertable": true
254
- },
255
- "./widgets/webwriter-quiz": {
256
- "content": "webwriter-task*",
257
- "uninsertable": true
258
- }
259
- }
260
- }
69
+ "./widgets/webwriter-true-false.*": {
70
+ "source": "./src/widgets/choice/webwriter-true-false.ts",
71
+ "default": "./dist/widgets/webwriter-true-false.*"
72
+ },
73
+ "./widgets/webwriter-choice.*": {
74
+ "source": "./src/widgets/choice/webwriter-choice.ts",
75
+ "default": "./dist/widgets/webwriter-choice.*"
76
+ },
77
+ "./widgets/webwriter-choice-item.*": {
78
+ "source": "./src/widgets/choice/webwriter-choice-item.ts",
79
+ "default": "./dist/widgets/webwriter-choice-item.*"
80
+ },
81
+ "./widgets/webwriter-order.*": {
82
+ "source": "./src/widgets/order/webwriter-order.ts",
83
+ "default": "./dist/widgets/webwriter-order.*"
84
+ },
85
+ "./widgets/webwriter-order-item.*": {
86
+ "source": "./src/widgets/order/webwriter-order-item.ts",
87
+ "default": "./dist/widgets/webwriter-order-item.*"
88
+ },
89
+ "./widgets/webwriter-speech.*": {
90
+ "source": "./src/widgets/speech/webwriter-speech.ts",
91
+ "default": "./dist/widgets/webwriter-speech.*"
92
+ },
93
+ "./widgets/webwriter-text.*": {
94
+ "source": "./src/widgets/webwriter-text.ts",
95
+ "default": "./dist/widgets/webwriter-text.*"
96
+ },
97
+ "./widgets/webwriter-mark.*": {
98
+ "source": "./src/widgets/webwriter-mark.ts",
99
+ "default": "./dist/widgets/webwriter-mark.*"
100
+ },
101
+ "./widgets/webwriter-gap.*": {
102
+ "source": "./src/widgets/gap/webwriter-gap.ts",
103
+ "default": "./dist/widgets/webwriter-gap.*"
104
+ },
105
+ "./widgets/webwriter-gap-item.*": {
106
+ "source": "./src/widgets/gap/webwriter-gap-item.ts",
107
+ "default": "./dist/widgets/webwriter-gap-item.*"
108
+ },
109
+ "./widgets/webwriter-pairing.*": {
110
+ "source": "./src/widgets/pairing/webwriter-pairing.ts",
111
+ "default": "./dist/widgets/webwriter-pairing.*"
112
+ },
113
+ "./widgets/webwriter-pairing-item.*": {
114
+ "source": "./src/widgets/pairing/webwriter-pairing-item.ts",
115
+ "default": "./dist/widgets/webwriter-pairing-item.*"
116
+ },
117
+ "./migrate.js": {
118
+ "source": "./src/migrate/index.ts",
119
+ "default": "./dist/migrate.js"
120
+ },
121
+ "./icon": "./icon.svg",
122
+ "./editing-config.json": "./editing-config.json",
123
+ "./custom-elements.json": "./custom-elements.json"
124
+ },
125
+ "customElements": "custom-elements.json"
126
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Merges translations from JSON into the XLIFF interchange files.
4
+ *
5
+ * Usage: node scripts/apply-translations.mjs <dir>
6
+ *
7
+ * Expects <dir> to contain `translations-<locale>.json` files, each mapping
8
+ * trans-unit id -> translated string, for the locales in lit-localize.json.
9
+ * Only fills targets that are missing or empty; existing targets are left
10
+ * alone, so hand-corrected translations survive a re-run.
11
+ *
12
+ * Run `npx @lit/localize-tools extract` before this, and
13
+ * `npx @lit/localize-tools build` after.
14
+ */
15
+
16
+ import { promises as fs } from "fs";
17
+ import { join } from "path";
18
+
19
+ const XML_ESCAPES = { "&": "&amp;", "<": "&lt;", ">": "&gt;" };
20
+ const escapeXml = text => text.replace(/[&<>]/g, c => XML_ESCAPES[c]);
21
+
22
+ /** Placeholders are `<x id="0" equiv-text="${...}"/>` in the source; translators write them back as `${...}`. */
23
+ function encodeTarget(source, translation) {
24
+ const placeholders = [...source.matchAll(/<x id="(\d+)" equiv-text="([^"]*)"\/>/g)];
25
+ let target = escapeXml(translation);
26
+ for (const [, id, equivText] of placeholders) {
27
+ // Tolerate a placeholder wrapped in braces (`{${x}}`) — a common way for the
28
+ // interpolation to come back doubled — before checking it is present at all.
29
+ target = target.replaceAll(`{${equivText}}`, () => equivText);
30
+ // equiv-text is already XML-escaped in the source; compare against the escaped translation
31
+ if (!target.includes(equivText)) {
32
+ throw new Error(`missing placeholder ${equivText} in translation ${JSON.stringify(translation)}`);
33
+ }
34
+ // function replacement: `$` in the placeholder must not be read as a substitution pattern
35
+ target = target.replace(equivText, () => `<x id="${id}" equiv-text="${equivText}"/>`);
36
+ }
37
+ if (placeholders.length && /[{}]/.test(target.replace(/<x [^>]*\/>/g, ""))) {
38
+ throw new Error(`stray brace left around placeholder in ${JSON.stringify(translation)}`);
39
+ }
40
+ return target;
41
+ }
42
+
43
+ async function applyLocale(xliffDir, locale, translations) {
44
+ const path = join(xliffDir, `${locale}.xlf`);
45
+ const xliff = await fs.readFile(path, "utf8");
46
+ let filled = 0;
47
+ let skipped = 0;
48
+ const missing = [];
49
+
50
+ const updated = xliff.replace(/<trans-unit id="([^"]+)">([\s\S]*?)<\/trans-unit>/g, (unit, id, body) => {
51
+ const translation = translations[id];
52
+ if (translation === undefined) {
53
+ missing.push(id);
54
+ return unit;
55
+ }
56
+ if (/<target[ >]/.test(body)) {
57
+ skipped++;
58
+ return unit;
59
+ }
60
+ const source = body.match(/<source>([\s\S]*?)<\/source>/)?.[1] ?? "";
61
+ const target = encodeTarget(source, translation);
62
+ filled++;
63
+ return unit.replace(/(<source>[\s\S]*?<\/source>)/, `$1\n <target>${target}</target>`);
64
+ });
65
+
66
+ await fs.writeFile(path, updated, "utf8");
67
+ return { filled, skipped, missing };
68
+ }
69
+
70
+ const dir = process.argv[2];
71
+ if (!dir) {
72
+ console.error("usage: node scripts/apply-translations.mjs <dir-with-translations-*.json>");
73
+ process.exit(1);
74
+ }
75
+
76
+ const { targetLocales, interchange } = JSON.parse(await fs.readFile("./lit-localize.json", "utf8"));
77
+ let failed = false;
78
+
79
+ for (const locale of targetLocales) {
80
+ const jsonPath = join(dir, `translations-${locale}.json`);
81
+ let translations;
82
+ try {
83
+ translations = JSON.parse(await fs.readFile(jsonPath, "utf8"));
84
+ } catch {
85
+ console.error(`${locale}: no ${jsonPath}, skipping`);
86
+ failed = true;
87
+ continue;
88
+ }
89
+ const { filled, skipped, missing } = await applyLocale(interchange.xliffDir, locale, translations);
90
+ console.log(`${locale}: filled ${filled}, kept ${skipped} existing, ${missing.length} untranslated`);
91
+ if (missing.length) {
92
+ console.error(` missing ids: ${missing.join(", ")}`);
93
+ failed = true;
94
+ }
95
+ }
96
+
97
+ process.exit(failed ? 1 : 0);
@@ -0,0 +1,72 @@
1
+ export type QuizType = {
2
+ packageId: string;
3
+ widgetPosition?: number;
4
+ id: string;
5
+ icon: string;
6
+ getName(): string;
7
+ createInstance(): HTMLElement & IWebWriterQuizType;
8
+ };
9
+
10
+ export interface IWebWriterQuizType {
11
+ checkValidity(): boolean;
12
+ reset(): void;
13
+ checkAnswer?(detailedFeedback: boolean): number;
14
+ }
15
+
16
+ type WebwriterQuizRegistry = {
17
+ version: number;
18
+ quizTypes: QuizType[];
19
+ listeners: ((types: readonly QuizType[]) => void)[];
20
+ };
21
+
22
+ declare global {
23
+ interface Window {
24
+ __wwQuizRegistry__: WebwriterQuizRegistry;
25
+ }
26
+ }
27
+
28
+ function getRegistry(): WebwriterQuizRegistry {
29
+ window.__wwQuizRegistry__ ??= { version: 1, quizTypes: [], listeners: [] };
30
+ return window.__wwQuizRegistry__;
31
+ }
32
+
33
+ export function getQuizTypes(): readonly QuizType[] {
34
+ return [...getRegistry().quizTypes];
35
+ }
36
+
37
+ export function registerQuizType(type: QuizType): void {
38
+ const registry = getRegistry();
39
+
40
+ let i = 0;
41
+
42
+ // First, order by package id
43
+ // The @webwriter/quiz package is always first, regardless of its package id
44
+ if (type.packageId !== "@webwriter/quiz") {
45
+ while (i < registry.quizTypes.length) {
46
+ if (registry.quizTypes[i].packageId.localeCompare(type.packageId) >= 0) break;
47
+ i++;
48
+ }
49
+ }
50
+
51
+ // Then, order by widget position (ascending), and then by widget id (ascending)
52
+ const newPosition = type.widgetPosition ?? Infinity;
53
+ while (i < registry.quizTypes.length && registry.quizTypes[i].packageId === type.packageId) {
54
+ const currentPosition = registry.quizTypes[i].widgetPosition ?? Infinity;
55
+ if (currentPosition > newPosition) break;
56
+ // Use id as a tiebreaker (alphabetical order)
57
+ if (currentPosition === newPosition && registry.quizTypes[i].id.localeCompare(type.id) >= 0) break;
58
+ i++;
59
+ }
60
+
61
+ registry.quizTypes.splice(i, 0, type);
62
+
63
+ const quizTypes = getQuizTypes();
64
+ registry.listeners.forEach(cb => cb(quizTypes));
65
+ }
66
+
67
+ export function onQuizTypesChanged(cb: (types: readonly QuizType[]) => void): () => void {
68
+ const registry = getRegistry();
69
+ cb(getQuizTypes());
70
+ registry.listeners.push(cb);
71
+ return () => registry.listeners = registry.listeners.filter(l => l !== cb);
72
+ }