cognikit 1.0.2 → 1.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 (124) hide show
  1. package/dist/builders/classification/index.d.ts +24 -0
  2. package/dist/builders/fill-blanks/index.d.ts +48 -0
  3. package/dist/client.js +2574 -2666
  4. package/dist/client.js.map +4 -4
  5. package/dist/core/builders/index.d.ts +48 -0
  6. package/dist/core/index.d.ts +2 -1
  7. package/dist/core/{BaseInteraction.d.ts → interactions/index.d.ts} +11 -12
  8. package/dist/engines/index.d.ts +0 -1
  9. package/dist/engines/tables/index.d.ts +40 -2
  10. package/dist/engines/tables/{implementation/utils.d.ts → utils.d.ts} +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +2570 -2662
  13. package/dist/index.js.map +4 -4
  14. package/dist/interactions/adjacency-table/index.d.ts +6 -7
  15. package/dist/interactions/classification-matrix/index.d.ts +7 -6
  16. package/dist/interactions/fill-blanks/index.d.ts +36 -2
  17. package/dist/interactions/index.d.ts +0 -2
  18. package/dist/interactions/list-recall/index.d.ts +27 -1
  19. package/dist/interactions/lookup-table/index.d.ts +6 -7
  20. package/dist/interactions/mark-the-words/index.d.ts +34 -2
  21. package/dist/interactions/mcq-mrq/index.d.ts +37 -2
  22. package/dist/interactions/nary-choice-table/index.d.ts +7 -13
  23. package/dist/interactions/open-classification/index.d.ts +9 -6
  24. package/dist/interactions/rank-order/index.d.ts +49 -2
  25. package/dist/interactions/registry.d.ts +5 -5
  26. package/dist/interactions/sequential-classification/index.d.ts +8 -6
  27. package/dist/interactions/simultaneous-association/index.d.ts +7 -12
  28. package/dist/{shared/managers → managers}/AnimationsManager.d.ts +6 -1
  29. package/dist/managers/DragAndDropManager.d.ts +37 -0
  30. package/dist/{core/utilities/ProgressTracker.d.ts → managers/ProgressManager.d.ts} +8 -8
  31. package/dist/managers/ScoringManager.d.ts +73 -0
  32. package/dist/managers/index.d.ts +4 -0
  33. package/dist/shared/config.d.ts +1 -1
  34. package/dist/shared/index.d.ts +0 -2
  35. package/dist/shared/utils.d.ts +2 -1
  36. package/dist/shell/builders/index.d.ts +29 -0
  37. package/dist/shell/index.d.ts +2 -1
  38. package/dist/shell/{simple-shell/script.d.ts → interactions/index.d.ts} +8 -7
  39. package/dist/types/Builders.d.ts +17 -0
  40. package/dist/types/Data.d.ts +49 -9
  41. package/dist/types/Global.d.ts +2 -23
  42. package/dist/types/Grading.d.ts +2 -0
  43. package/dist/types/Input.d.ts +12 -16
  44. package/dist/types/Interactions.d.ts +10 -14
  45. package/dist/types/Tables.d.ts +2 -2
  46. package/dist/ui/{misc/block.d.ts → block/index.d.ts} +1 -1
  47. package/dist/ui/{misc/chip/chip.d.ts → chip/index.d.ts} +8 -4
  48. package/dist/ui/{misc/dialog.d.ts → dialog/index.d.ts} +0 -1
  49. package/dist/ui/index.d.ts +4 -1
  50. package/dist/ui/input/index.d.ts +22 -1
  51. package/dist/ui/{misc/media.d.ts → media/index.d.ts} +1 -1
  52. package/package.json +1 -1
  53. package/public/app.js +5874 -8649
  54. package/public/app.js.map +4 -4
  55. package/public/assets/images/landscape.jpg +0 -0
  56. package/public/assets/images/mountains.jpg +0 -0
  57. package/public/builders.html +87 -0
  58. package/public/examples/data.ts +167 -0
  59. package/public/index.html +1008 -779
  60. package/dist/core/utilities/index.d.ts +0 -1
  61. package/dist/engines/tables/implementation/graders.d.ts +0 -23
  62. package/dist/engines/tables/implementation/index.d.ts +0 -4
  63. package/dist/engines/tables/implementation/parsers.d.ts +0 -36
  64. package/dist/engines/tables/implementation/validators.d.ts +0 -9
  65. package/dist/engines/tables/script.d.ts +0 -37
  66. package/dist/engines/text/implementation/graders.d.ts +0 -69
  67. package/dist/engines/text/implementation/index.d.ts +0 -3
  68. package/dist/engines/text/implementation/parsers.d.ts +0 -9
  69. package/dist/engines/text/implementation/validators.d.ts +0 -41
  70. package/dist/engines/text/index.d.ts +0 -2
  71. package/dist/engines/text/script.d.ts +0 -56
  72. package/dist/interactions/categorize-the-words/index.d.ts +0 -2
  73. package/dist/interactions/categorize-the-words/sequential.d.ts +0 -33
  74. package/dist/interactions/categorize-the-words/static.d.ts +0 -22
  75. package/dist/interactions/fill-blanks/sequential.d.ts +0 -32
  76. package/dist/interactions/fill-blanks/static.d.ts +0 -23
  77. package/dist/interactions/list-recall/script.d.ts +0 -25
  78. package/dist/interactions/mark-the-words/sequential.d.ts +0 -34
  79. package/dist/interactions/mark-the-words/static.d.ts +0 -40
  80. package/dist/interactions/mcq-mrq/implementation/grader.d.ts +0 -20
  81. package/dist/interactions/mcq-mrq/implementation/index.d.ts +0 -3
  82. package/dist/interactions/mcq-mrq/implementation/parser.d.ts +0 -21
  83. package/dist/interactions/mcq-mrq/implementation/validator.d.ts +0 -17
  84. package/dist/interactions/mcq-mrq/script.d.ts +0 -37
  85. package/dist/interactions/rank-order/implementation/grader.d.ts +0 -15
  86. package/dist/interactions/rank-order/implementation/index.d.ts +0 -3
  87. package/dist/interactions/rank-order/implementation/parser.d.ts +0 -25
  88. package/dist/interactions/rank-order/implementation/validator.d.ts +0 -3
  89. package/dist/interactions/rank-order/script.d.ts +0 -39
  90. package/dist/interactions/shared/association-implementation/grader.d.ts +0 -3
  91. package/dist/interactions/shared/association-implementation/index.d.ts +0 -1
  92. package/dist/interactions/shared/association-implementation/validator.d.ts +0 -0
  93. package/dist/interactions/shared/classification-implementation/grader.d.ts +0 -3
  94. package/dist/interactions/shared/classification-implementation/index.d.ts +0 -1
  95. package/dist/interactions/shared/classification-implementation/parser.d.ts +0 -11
  96. package/dist/interactions/shared/classification-implementation/validator.d.ts +0 -3
  97. package/dist/interactions/text-transformation/index.d.ts +0 -2
  98. package/dist/interactions/text-transformation/sequential.d.ts +0 -37
  99. package/dist/interactions/text-transformation/static.d.ts +0 -47
  100. package/dist/shared/dsl.d.ts +0 -9
  101. package/dist/shared/managers/index.d.ts +0 -3
  102. package/dist/shared/types.d.ts +0 -25
  103. package/dist/shell/simple-shell/index.d.ts +0 -1
  104. package/dist/types/Text.d.ts +0 -125
  105. package/dist/ui/input/input/index.d.ts +0 -1
  106. package/dist/ui/input/input/input.d.ts +0 -22
  107. package/dist/ui/misc/chip/index.d.ts +0 -1
  108. package/dist/ui/misc/index.d.ts +0 -4
  109. package/public/assets/images/pointing-hand.svg +0 -1
  110. package/public/examples/chip.html +0 -495
  111. package/public/scalable-bare.html +0 -432
  112. package/public/tables-demo.html +0 -534
  113. package/public/text-interactions-demo.html +0 -604
  114. /package/dist/{interactions/list-recall/implementation → builders/list}/index.d.ts +0 -0
  115. /package/dist/{core/utilities/ScoringTracker.d.ts → builders/matching/inde.d.ts} +0 -0
  116. /package/dist/{ui/input/color → builders/ordering}/index.d.ts +0 -0
  117. /package/dist/{ui/input/letter → builders/tables}/index.d.ts +0 -0
  118. /package/dist/{interactions/list-recall/implementation/grader.d.ts → builders/text/index.d.ts} +0 -0
  119. /package/dist/interactions/{list-recall/implementation/parser.d.ts → exp/CategorizeTheWords.d.ts} +0 -0
  120. /package/dist/interactions/{list-recall/implementation/validator.d.ts → exp/DndFillBlanks.d.ts} +0 -0
  121. /package/dist/interactions/{shared/association-implementation/parser.d.ts → exp/TextTransformation.d.ts} +0 -0
  122. /package/dist/{shared/managers → managers}/SoundManager.d.ts +0 -0
  123. /package/dist/ui/{input/color/color-picker.d.ts → exp/color/index.d.ts} +0 -0
  124. /package/dist/ui/{input/letter/letter-picker.d.ts → exp/letter/index.d.ts} +0 -0
package/public/index.html CHANGED
@@ -1,694 +1,789 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
+
3
4
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
6
- <meta name="mobile-web-app-capable" content="yes">
7
- <meta name="apple-mobile-web-app-capable" content="yes">
8
- <meta name="apple-mobile-web-app-status-bar-style" content="default">
9
- <meta name="theme-color" content="#f7f9fc">
10
- <title>CogniKit - Beta Interaction Playground</title>
11
- <style>
12
- * {
13
- margin: 0;
14
- padding: 0;
15
- box-sizing: border-box;
16
- }
17
-
18
- html, body {
19
- width: 100%;
20
- height: 100%;
21
- overflow: hidden;
22
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
23
- -webkit-font-smoothing: antialiased;
24
- -moz-osx-font-smoothing: grayscale;
25
- }
26
-
27
- :root {
28
- --edu-bg: 247 249 252;
29
- --edu-card: 255 255 255;
30
- --edu-ink: 31 41 55;
31
- --edu-second-ink: 71 85 105;
32
- --edu-third-ink: 100 116 139;
33
- --edu-inverted-ink: 248 250 252;
34
- --edu-success: 22 163 74;
35
- --edu-error: 220 38 38;
36
- --edu-warning: 255 222 33;
37
- --edu-neutral: 14 165 233;
38
- --edu-first-accent: 49 120 198;
39
- --edu-second-accent: 245 158 11;
40
- --edu-third-accent: 236 72 153;
41
- --edu-border: 229 231 235;
42
- --edu-muted: 243 244 246;
43
- --edu-radius: 0.375rem;
44
- --edu-shadow-color: 0 0 0;
45
- --edu-pad: 1rem;
46
- --edu-mar: 0;
47
- }
48
-
49
- body {
50
- background: rgb(var(--edu-bg));
51
- display: flex;
52
- flex-direction: column;
53
- height: 100vh;
54
- height: 100dvh;
55
- }
56
-
57
- /* Header */
58
- .header {
59
- display: flex;
60
- align-items: center;
61
- justify-content: space-between;
62
- padding: 1rem 1.5rem;
63
- background: rgb(var(--edu-card));
64
- border-bottom: 2px solid rgb(var(--edu-border));
65
- box-shadow: 0 2px 8px rgba(var(--edu-shadow-color), 0.05);
66
- flex-shrink: 0;
67
- z-index: 100;
68
- }
69
-
70
- .header h1 {
71
- font-size: 1.25rem;
72
- color: rgb(var(--edu-ink));
73
- font-weight: 700;
74
- }
75
-
76
- .header-controls {
77
- display: flex;
78
- gap: 0.75rem;
79
- }
80
-
81
- .icon-button {
82
- background: rgb(var(--edu-card));
83
- border: 2px solid rgb(var(--edu-border));
84
- border-radius: 8px;
85
- width: 44px;
86
- height: 44px;
87
- display: flex;
88
- align-items: center;
89
- justify-content: center;
90
- cursor: pointer;
91
- transition: all 0.2s;
92
- color: rgb(var(--edu-ink));
93
- font-size: 1.2rem;
94
- }
95
-
96
- .icon-button:hover {
97
- border-color: rgb(var(--edu-first-accent));
98
- background: rgba(var(--edu-first-accent), 0.1);
99
- }
100
-
101
- .icon-button:active {
102
- transform: scale(0.95);
103
- }
104
-
105
- /* Main viewport */
106
- .main-viewport {
107
- flex: 1;
108
- display: flex;
109
- align-items: center;
110
- justify-content: center;
111
- padding: 1.5rem;
112
- overflow: hidden;
113
- }
114
-
115
- .interaction-container {
116
- width: 100%;
117
- height: 100%;
118
- max-width: 1200px;
119
- display: flex;
120
- flex-direction: column;
121
- }
122
-
123
- edu-window {
124
- width: 100%;
125
- height: 100%;
126
- display: block;
127
- }
128
-
129
- /* Modal */
130
- .modal-overlay {
131
- position: fixed;
132
- top: 0;
133
- left: 0;
134
- right: 0;
135
- bottom: 0;
136
- background: rgba(0, 0, 0, 0.5);
137
- backdrop-filter: blur(4px);
138
- display: none;
139
- align-items: center;
140
- justify-content: center;
141
- z-index: 1000;
142
- padding: 1rem;
143
- }
144
-
145
- .modal-overlay.active {
146
- display: flex;
147
- }
148
-
149
- .modal {
150
- background: rgb(var(--edu-card));
151
- border-radius: 16px;
152
- box-shadow: 0 20px 60px rgba(var(--edu-shadow-color), 0.3);
153
- max-width: 600px;
154
- width: 100%;
155
- max-height: 90vh;
156
- max-height: 90dvh;
157
- overflow: hidden;
158
- display: flex;
159
- flex-direction: column;
160
- }
161
-
162
- .modal-header {
163
- padding: 1.5rem;
164
- border-bottom: 2px solid rgb(var(--edu-border));
165
- display: flex;
166
- align-items: center;
167
- justify-content: space-between;
168
- }
169
-
170
- .modal-header h2 {
171
- font-size: 1.25rem;
172
- color: rgb(var(--edu-ink));
173
- }
174
-
175
- .modal-close {
176
- background: none;
177
- border: none;
178
- font-size: 1.5rem;
179
- cursor: pointer;
180
- color: rgb(var(--edu-second-ink));
181
- width: 32px;
182
- height: 32px;
183
- display: flex;
184
- align-items: center;
185
- justify-content: center;
186
- border-radius: 6px;
187
- transition: all 0.2s;
188
- }
189
-
190
- .modal-close:hover {
191
- background: rgba(var(--edu-error), 0.1);
192
- color: rgb(var(--edu-error));
193
- }
194
-
195
- .modal-body {
196
- padding: 1.5rem;
197
- overflow-y: auto;
198
- flex: 1;
199
- }
200
-
201
- .form-group {
202
- margin-bottom: 1.5rem;
203
- }
204
-
205
- .form-group label {
206
- display: block;
207
- font-weight: 600;
208
- color: rgb(var(--edu-ink));
209
- margin-bottom: 0.5rem;
210
- font-size: 0.95rem;
211
- }
212
-
213
- .form-group select,
214
- .form-group input,
215
- .form-group textarea {
216
- width: 100%;
217
- padding: 0.75rem;
218
- border: 2px solid rgb(var(--edu-border));
219
- border-radius: 8px;
220
- background: rgb(var(--edu-bg));
221
- color: rgb(var(--edu-ink));
222
- font-size: 0.95rem;
223
- font-family: inherit;
224
- transition: all 0.2s;
225
- }
226
-
227
- .form-group textarea {
228
- font-family: 'Monaco', 'Courier New', monospace;
229
- min-height: 150px;
230
- resize: vertical;
231
- }
232
-
233
- .form-group select:focus,
234
- .form-group input:focus,
235
- .form-group textarea:focus {
236
- outline: none;
237
- border-color: rgb(var(--edu-first-accent));
238
- background: rgb(var(--edu-card));
239
- }
240
-
241
- .modal-footer {
242
- padding: 1.5rem;
243
- border-top: 2px solid rgb(var(--edu-border));
244
- display: flex;
245
- gap: 1rem;
246
- justify-content: flex-end;
247
- }
248
-
249
- .btn {
250
- padding: 0.75rem 1.5rem;
251
- border-radius: 8px;
252
- border: 2px solid rgb(var(--edu-border));
253
- background: rgb(var(--edu-card));
254
- color: rgb(var(--edu-ink));
255
- font-weight: 600;
256
- font-size: 0.95rem;
257
- cursor: pointer;
258
- transition: all 0.2s;
259
- min-height: 44px;
260
- }
261
-
262
- .btn:hover {
263
- border-color: rgb(var(--edu-first-accent));
264
- background: rgba(var(--edu-first-accent), 0.1);
265
- }
266
-
267
- .btn:active {
268
- transform: scale(0.98);
269
- }
270
-
271
- .btn-primary {
272
- background: rgb(var(--edu-first-accent));
273
- border-color: rgb(var(--edu-first-accent));
274
- color: rgb(var(--edu-inverted-ink));
275
- }
276
-
277
- .btn-primary:hover {
278
- opacity: 0.9;
279
- }
280
-
281
- .form-help {
282
- font-size: 0.85rem;
283
- color: rgb(var(--edu-second-ink));
284
- margin-top: 0.5rem;
285
- }
286
-
287
- /* Fullscreen mode */
288
- body.fullscreen .header {
289
- display: none;
290
- }
291
-
292
- body.fullscreen .main-viewport {
293
- padding: 0;
294
- }
295
-
296
- body.fullscreen .interaction-container {
297
- max-width: none;
298
- }
299
-
300
- /* Mobile styles */
301
- @media (max-width: 768px) {
302
- .header {
303
- padding: 0.75rem 1rem;
304
- }
305
-
306
- .header h1 {
307
- font-size: 1rem;
308
- }
309
-
310
- .icon-button {
311
- width: 40px;
312
- height: 40px;
313
- font-size: 1rem;
314
- }
315
-
316
- .main-viewport {
317
- padding: 1rem;
318
- }
319
-
320
- .modal {
321
- border-radius: 12px;
322
- max-height: 95vh;
323
- max-height: 95dvh;
324
- }
325
-
326
- .modal-header,
327
- .modal-body,
328
- .modal-footer {
329
- padding: 1rem;
330
- }
331
-
332
- .form-group textarea {
333
- min-height: 120px;
334
- }
335
- }
336
-
337
- @media (max-width: 480px) {
338
- .main-viewport {
339
- padding: 0.5rem;
340
- }
341
-
342
- .modal-footer {
343
- flex-direction: column;
344
- }
345
-
346
- .btn {
347
- width: 100%;
348
- }
349
- }
350
- </style>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport"
7
+ content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
8
+ <meta name="mobile-web-app-capable" content="yes">
9
+ <meta name="apple-mobile-web-app-capable" content="yes">
10
+ <meta name="apple-mobile-web-app-status-bar-style" content="default">
11
+ <meta name="theme-color" content="#f7f9fc">
12
+ <title>CogniKit - Beta Interaction Playground</title>
13
+ <style>
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ html,
21
+ body {
22
+ width: 100%;
23
+ height: 100%;
24
+ overflow: hidden;
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
26
+ -webkit-font-smoothing: antialiased;
27
+ -moz-osx-font-smoothing: grayscale;
28
+ }
29
+
30
+ :root {
31
+ --edu-bg: 247 249 252;
32
+ --edu-card: 255 255 255;
33
+ --edu-ink: 31 41 55;
34
+ --edu-second-ink: 71 85 105;
35
+ --edu-third-ink: 100 116 139;
36
+ --edu-inverted-ink: 248 250 252;
37
+ --edu-success: 22 163 74;
38
+ --edu-error: 220 38 38;
39
+ --edu-warning: 255 222 33;
40
+ --edu-neutral: 14 165 233;
41
+ --edu-first-accent: 49 120 198;
42
+ --edu-border: 229 231 235;
43
+ --edu-muted: 243 244 246;
44
+ }
45
+
46
+ body {
47
+ background: rgb(var(--edu-bg));
48
+ display: flex;
49
+ flex-direction: column;
50
+ height: 100vh;
51
+ height: 100dvh;
52
+ }
53
+
54
+ /* Header */
55
+ .header {
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: space-between;
59
+ padding: 1rem 1.5rem;
60
+ background: rgb(var(--edu-card));
61
+ border-bottom: 2px solid rgb(var(--edu-border));
62
+ box-shadow: 0 2px 8px rgba(var(--edu-shadow-color), 0.05);
63
+ flex-shrink: 0;
64
+ z-index: 100;
65
+ }
66
+
67
+ .header h1 {
68
+ font-size: 1.25rem;
69
+ color: rgb(var(--edu-ink));
70
+ font-weight: 700;
71
+ }
72
+
73
+ .header-controls {
74
+ display: flex;
75
+ gap: 0.75rem;
76
+ }
77
+
78
+ .icon-button {
79
+ background: rgb(var(--edu-card));
80
+ border: 2px solid rgb(var(--edu-border));
81
+ border-radius: 8px;
82
+ width: 44px;
83
+ height: 44px;
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ cursor: pointer;
88
+ transition: all 0.2s;
89
+ color: rgb(var(--edu-ink));
90
+ font-size: 1.2rem;
91
+ }
92
+
93
+ .icon-button:hover {
94
+ border-color: rgb(var(--edu-first-accent));
95
+ background: rgba(var(--edu-first-accent), 0.1);
96
+ }
97
+
98
+ .icon-button:active {
99
+ transform: scale(0.95);
100
+ }
101
+
102
+ /* Main viewport */
103
+ .main-viewport {
104
+ flex: 1;
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ padding: 1.5rem;
109
+ overflow: hidden;
110
+ }
111
+
112
+ .interaction-container {
113
+ width: 100%;
114
+ height: 100%;
115
+ max-width: 1200px;
116
+ display: flex;
117
+ flex-direction: column;
118
+ }
119
+
120
+ edu-window {
121
+ width: 100%;
122
+ height: 100%;
123
+ display: block;
124
+ }
125
+
126
+ /* Modal */
127
+ .modal-overlay {
128
+ position: fixed;
129
+ top: 0;
130
+ left: 0;
131
+ right: 0;
132
+ bottom: 0;
133
+ background: rgba(0, 0, 0, 0.5);
134
+ backdrop-filter: blur(4px);
135
+ display: none;
136
+ align-items: center;
137
+ justify-content: center;
138
+ z-index: 1000;
139
+ padding: 1rem;
140
+ }
141
+
142
+ .modal-overlay.active {
143
+ display: flex;
144
+ }
145
+
146
+ .modal {
147
+ background: rgb(var(--edu-card));
148
+ border-radius: 16px;
149
+ box-shadow: 0 20px 60px rgba(var(--edu-shadow-color), 0.3);
150
+ max-width: 600px;
151
+ width: 100%;
152
+ max-height: 90vh;
153
+ max-height: 90dvh;
154
+ overflow: hidden;
155
+ display: flex;
156
+ flex-direction: column;
157
+ }
158
+
159
+ .modal-header {
160
+ padding: 1.5rem;
161
+ border-bottom: 2px solid rgb(var(--edu-border));
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: space-between;
165
+ }
166
+
167
+ .modal-header h2 {
168
+ font-size: 1.25rem;
169
+ color: rgb(var(--edu-ink));
170
+ }
171
+
172
+ .modal-close {
173
+ background: none;
174
+ border: none;
175
+ font-size: 1.5rem;
176
+ cursor: pointer;
177
+ color: rgb(var(--edu-second-ink));
178
+ width: 32px;
179
+ height: 32px;
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ border-radius: 6px;
184
+ transition: all 0.2s;
185
+ }
186
+
187
+ .modal-close:hover {
188
+ background: rgba(var(--edu-error), 0.1);
189
+ color: rgb(var(--edu-error));
190
+ }
191
+
192
+ .modal-body {
193
+ padding: 1.5rem;
194
+ overflow-y: auto;
195
+ flex: 1;
196
+ }
197
+
198
+ .form-group {
199
+ margin-bottom: 1.5rem;
200
+ }
201
+
202
+ .form-group label {
203
+ display: block;
204
+ font-weight: 600;
205
+ color: rgb(var(--edu-ink));
206
+ margin-bottom: 0.5rem;
207
+ font-size: 0.95rem;
208
+ }
209
+
210
+ .form-group select,
211
+ .form-group input,
212
+ .form-group textarea {
213
+ width: 100%;
214
+ padding: 0.75rem;
215
+ border: 2px solid rgb(var(--edu-border));
216
+ border-radius: 8px;
217
+ background: rgb(var(--edu-bg));
218
+ color: rgb(var(--edu-ink));
219
+ font-size: 0.95rem;
220
+ font-family: inherit;
221
+ transition: all 0.2s;
222
+ }
223
+
224
+ .form-group textarea {
225
+ font-family: 'Monaco', 'Courier New', monospace;
226
+ min-height: 150px;
227
+ resize: vertical;
228
+ }
229
+
230
+ .form-group select:focus,
231
+ .form-group input:focus,
232
+ .form-group textarea:focus {
233
+ outline: none;
234
+ border-color: rgb(var(--edu-first-accent));
235
+ background: rgb(var(--edu-card));
236
+ }
237
+
238
+ .modal-footer {
239
+ padding: 1.5rem;
240
+ border-top: 2px solid rgb(var(--edu-border));
241
+ display: flex;
242
+ gap: 1rem;
243
+ justify-content: flex-end;
244
+ }
245
+
246
+ .btn {
247
+ padding: 0.75rem 1.5rem;
248
+ border-radius: 8px;
249
+ border: 2px solid rgb(var(--edu-border));
250
+ background: rgb(var(--edu-card));
251
+ color: rgb(var(--edu-ink));
252
+ font-weight: 600;
253
+ font-size: 0.95rem;
254
+ cursor: pointer;
255
+ transition: all 0.2s;
256
+ min-height: 44px;
257
+ }
258
+
259
+ .btn:hover {
260
+ border-color: rgb(var(--edu-first-accent));
261
+ background: rgba(var(--edu-first-accent), 0.1);
262
+ }
263
+
264
+ .btn:active {
265
+ transform: scale(0.98);
266
+ }
267
+
268
+ .btn-primary {
269
+ background: rgb(var(--edu-first-accent));
270
+ border-color: rgb(var(--edu-first-accent));
271
+ color: rgb(var(--edu-inverted-ink));
272
+ }
273
+
274
+ .btn-primary:hover {
275
+ opacity: 0.9;
276
+ }
277
+
278
+ .form-help {
279
+ font-size: 0.85rem;
280
+ color: rgb(var(--edu-second-ink));
281
+ margin-top: 0.5rem;
282
+ }
283
+
284
+ /* Fullscreen mode */
285
+ body.fullscreen .header {
286
+ display: none;
287
+ }
288
+
289
+ body.fullscreen .main-viewport {
290
+ padding: 0;
291
+ }
292
+
293
+ body.fullscreen .interaction-container {
294
+ max-width: none;
295
+ }
296
+
297
+ /* Mobile styles */
298
+ @media (max-width: 768px) {
299
+ .header {
300
+ padding: 0.75rem 1rem;
301
+ }
302
+
303
+ .header h1 {
304
+ font-size: 1rem;
305
+ }
306
+
307
+ .icon-button {
308
+ width: 40px;
309
+ height: 40px;
310
+ font-size: 1rem;
311
+ }
312
+
313
+ .main-viewport {
314
+ padding: 1rem;
315
+ }
316
+
317
+ .modal {
318
+ border-radius: 12px;
319
+ max-height: 95vh;
320
+ max-height: 95dvh;
321
+ }
322
+
323
+ .modal-header,
324
+ .modal-body,
325
+ .modal-footer {
326
+ padding: 1rem;
327
+ }
328
+
329
+ .form-group textarea {
330
+ min-height: 120px;
331
+ }
332
+ }
333
+
334
+ @media (max-width: 480px) {
335
+ .main-viewport {
336
+ padding: 0.5rem;
337
+ }
338
+
339
+ .modal-footer {
340
+ flex-direction: column;
341
+ }
342
+
343
+ .btn {
344
+ width: 100%;
345
+ }
346
+ }
347
+ </style>
351
348
  </head>
349
+
352
350
  <body>
353
- <!-- Header -->
354
- <div class="header">
355
- <h1>🎯 CogniKit Beta</h1>
356
- <div class="header-controls">
357
- <button class="icon-button" id="settings-btn" title="Settings">⚙️</button>
358
- <button class="icon-button" id="config-btn" title="Configure Interaction">🎨</button>
359
- <button class="icon-button" id="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
360
- </div>
361
- </div>
362
-
363
- <!-- Main Viewport -->
364
- <div class="main-viewport">
365
- <div class="interaction-container">
366
- <edu-window id="interaction-viewport"></edu-window>
367
- </div>
368
- </div>
369
-
370
- <!-- Settings Modal -->
371
- <div class="modal-overlay" id="settings-modal">
372
- <div class="modal">
373
- <div class="modal-header">
374
- <h2>⚙️ Settings</h2>
375
- <button class="modal-close" id="settings-close">×</button>
376
- </div>
377
- <div class="modal-body">
378
- <div class="form-group">
379
- <label for="variant-select">Variant Style</label>
380
- <select id="variant-select">
381
- <option value="elegant" selected>Elegant</option>
382
- <option value="playful">Playful</option>
383
- <option value="outline">Outline</option>
384
- <option value="letter">Letter</option>
385
- <option value="sign">Sign</option>
386
- <option value="minimal">Minimal</option>
387
- <option value="glass">Glass</option>
388
- <option value="card">Card</option>
389
- <option value="empty">Empty</option>
390
- </select>
391
- </div>
392
-
393
- <div class="form-group">
394
- <label for="theme-select">Theme</label>
395
- <select id="theme-select"></select>
396
- <div class="form-help">Uses Cognikit theme presets directly in the preview.</div>
397
- </div>
398
-
399
- <div class="form-group">
400
- <label for="accent-color">Accent Color</label>
401
- <input type="color" id="accent-color" value="#67CB90">
402
- <div class="form-help">Color customization not yet implemented</div>
403
- </div>
404
- </div>
405
- <div class="modal-footer">
406
- <button class="btn" id="settings-cancel">Cancel</button>
407
- <button class="btn btn-primary" id="settings-apply">Apply</button>
408
- </div>
409
- </div>
410
- </div>
411
-
412
- <!-- Configuration Modal -->
413
- <div class="modal-overlay" id="config-modal">
414
- <div class="modal">
415
- <div class="modal-header">
416
- <h2>🎨 Configure Interaction</h2>
417
- <button class="modal-close" id="config-close">×</button>
418
- </div>
419
- <div class="modal-body">
420
- <div class="form-group">
421
- <label for="interaction-type">Interaction Type</label>
422
- <select id="interaction-type">
423
- <option value="SequentialClassification">Sequential Classification</option>
424
- <option value="SimultaneousAssociation">Simultaneous Association</option>
425
- <option value="OpenClassification">Open Classification</option>
426
- <option value="RankOrder">Rank Order</option>
427
- <option value="ListRecall">List Recall</option>
428
- <option value="MCQ" selected>MCQ/MRQ</option>
429
- </select>
430
- </div>
431
-
432
- <div class="form-group">
433
- <label for="interaction-heading">Heading</label>
434
- <input type="text" id="interaction-heading" value="Butterfly Life Cycle" placeholder="Enter heading...">
435
- </div>
436
-
437
- <div class="form-group">
438
- <label for="interaction-timer">Timer (seconds)</label>
439
- <input type="number" id="interaction-timer" value="120" min="0" placeholder="0 for no timer">
440
- </div>
441
-
442
- <div class="form-group">
443
- <label for="data-json">Data (JSON)</label>
444
- <textarea id="data-json" placeholder="Enter JSON data...">{
445
- "type": "seriation",
446
- "items": ["Egg", "Caterpillar", "Chrysalis", "Butterfly", "@:butterfly"]
447
- }</textarea>
448
- <div class="form-help">Must be valid JSON format</div>
449
- </div>
450
-
451
- <div class="form-group">
452
- <label for="assets-yaml">Assets (YAML)</label>
453
- <textarea id="assets-yaml" placeholder="Enter YAML assets...">butterfly:
454
- type: image
455
- url: https://images.unsplash.com/photo-1533048324814-79b0a31982f1?q=80&w=768</textarea>
456
- <div class="form-help">Optional. Leave empty if no assets needed</div>
457
- </div>
458
- </div>
459
- <div class="modal-footer">
460
- <button class="btn" id="config-cancel">Cancel</button>
461
- <button class="btn btn-primary" id="config-apply">Render Interaction</button>
462
- </div>
463
- </div>
464
- </div>
465
-
466
- <script type="module">
467
- import {
468
- OpenClassification,
469
- SequentialClassification,
470
- SimultaneousAssociation,
471
- RankOrder,
472
- ListRecall,
473
- MCQ,
474
- getCognikitThemePresets,
475
- parseYamlAssets,
476
- setCognikitTheme,
477
- validateAndNormalizeAssets
478
- } from './app.js';
479
-
480
- // State
481
- let currentVariant = 'elegant';
482
- let currentTheme = 'default-light';
483
- let currentInteraction = null;
484
- const themePresets = getCognikitThemePresets();
485
-
486
- // DOM Elements
487
- const viewport = document.getElementById('interaction-viewport');
488
- const settingsBtn = document.getElementById('settings-btn');
489
- const configBtn = document.getElementById('config-btn');
490
- const fullscreenBtn = document.getElementById('fullscreen-btn');
491
- const settingsModal = document.getElementById('settings-modal');
492
- const configModal = document.getElementById('config-modal');
493
-
494
- // Modal Controls
495
- const settingsClose = document.getElementById('settings-close');
496
- const settingsCancel = document.getElementById('settings-cancel');
497
- const settingsApply = document.getElementById('settings-apply');
498
- const configClose = document.getElementById('config-close');
499
- const configCancel = document.getElementById('config-cancel');
500
- const configApply = document.getElementById('config-apply');
501
-
502
- // Form Elements
503
- const variantSelect = document.getElementById('variant-select');
504
- const themeSelect = document.getElementById('theme-select');
505
- const accentColor = document.getElementById('accent-color');
506
- const interactionType = document.getElementById('interaction-type');
507
- const interactionHeading = document.getElementById('interaction-heading');
508
- const interactionTimer = document.getElementById('interaction-timer');
509
- const dataJson = document.getElementById('data-json');
510
- const assetsYaml = document.getElementById('assets-yaml');
511
-
512
- // Modal Management
513
- function openModal(modal) {
514
- modal.classList.add('active');
515
- }
516
-
517
- function closeModal(modal) {
518
- modal.classList.remove('active');
519
- }
520
-
521
- function rgbTripletToCss(rgbTriplet) {
522
- return `rgb(${rgbTriplet.trim().split(/\s+/).join(', ')})`;
523
- }
524
-
525
- function rgbTripletToHex(rgbTriplet) {
526
- const [r, g, b] = rgbTriplet.trim().split(/\s+/).map(Number);
527
- return `#${[r, g, b].map((value) => value.toString(16).padStart(2, '0')).join('')}`;
528
- }
529
-
530
- function syncThemeMeta(themeName) {
531
- const theme = themePresets[themeName];
532
- const metaTheme = document.querySelector('meta[name="theme-color"]');
533
- if (theme && metaTheme) {
534
- metaTheme.setAttribute('content', rgbTripletToCss(theme["--edu-bg"]));
535
- }
536
- }
537
-
538
- function applyTheme(themeName) {
539
- currentTheme = themeName;
540
- setCognikitTheme(themeName);
541
- syncThemeMeta(themeName);
542
-
543
- const theme = themePresets[themeName];
544
- if (theme) {
545
- accentColor.value = rgbTripletToHex(theme["--edu-first-accent"]);
546
- }
547
- }
548
-
549
- function populateThemes() {
550
- themeSelect.innerHTML = Object.keys(themePresets).map((themeName) => {
551
- const label = themeName
552
- .split('-')
553
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
554
- .join(' ');
555
- const selected = themeName === currentTheme ? 'selected' : '';
556
- return `<option value="${themeName}" ${selected}>${label}</option>`;
557
- }).join('');
558
- }
559
-
560
- accentColor.addEventListener('change', (e) => {
561
- const color = e.target.value;
562
- // Convert hex to RGB
563
- const r = parseInt(color.substr(1, 2), 16);
564
- const g = parseInt(color.substr(3, 2), 16);
565
- const b = parseInt(color.substr(5, 2), 16);
566
- document.documentElement.style.setProperty('--edu-first-accent', `${r} ${g} ${b}`);
567
- });
568
-
569
- // Event Listeners - Modal Toggles
570
- settingsBtn.addEventListener('click', () => openModal(settingsModal));
571
- configBtn.addEventListener('click', () => openModal(configModal));
572
-
573
- settingsClose.addEventListener('click', () => closeModal(settingsModal));
574
- settingsCancel.addEventListener('click', () => closeModal(settingsModal));
575
-
576
- configClose.addEventListener('click', () => closeModal(configModal));
577
- configCancel.addEventListener('click', () => closeModal(configModal));
578
-
579
- // Close modal on overlay click
580
- settingsModal.addEventListener('click', (e) => {
581
- if (e.target === settingsModal) closeModal(settingsModal);
582
- });
583
- configModal.addEventListener('click', (e) => {
584
- if (e.target === configModal) closeModal(configModal);
585
- });
586
-
587
- // Settings Apply
588
- settingsApply.addEventListener('click', () => {
589
- currentVariant = variantSelect.value;
590
- applyTheme(themeSelect.value);
591
- if (viewport) {
592
- currentInteraction.onVariantChange(currentVariant);
593
- viewport.setAttribute('variant', currentVariant);
594
- }
595
- closeModal(settingsModal);
596
- });
597
-
598
- // Fullscreen Toggle
599
- fullscreenBtn.addEventListener('click', () => {
600
- document.body.classList.toggle('fullscreen');
601
- fullscreenBtn.textContent = document.body.classList.contains('fullscreen') ? '⛶' : '⛶';
602
- });
603
-
604
- // Interaction Type Data Templates
605
- const dataTemplates = {
606
- SequentialClassification: {
607
- data: {
608
- type: 'classification',
609
- categories: [
610
- { label: 'Ocean', items: ['Dolphin', 'Shark', 'Octopus'] },
611
- { label: 'Land', items: ['Lion', 'Elephant', 'Tiger'] },
612
- { label: 'Air', items: ['Eagle', 'Parrot', 'Hawk'] }
613
- ]
614
- },
615
- assets: ''
616
- },
617
- SimultaneousAssociation: {
618
- data: {
619
- type: 'association',
620
- pairs: [
621
- { left: 'Dog', right: 'Barks' },
622
- { left: 'Cat', right: 'Meows' },
623
- { left: 'Bird', right: 'Chirps' },
624
- { left: '@:cow', right: 'Moos' },
625
- { left: 'Lion', right: 'Roars' }
626
- ],
627
- distractors: ['quacks', 'oinks']
628
- },
629
- assets: 'cow:\n type: image\n url: https://images.unsplash.com/photo-1595365691689-6b7b4e1970cf?q=80&w=765&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'
630
- },
631
- OpenClassification: {
632
- data: {
633
- type: 'classification',
634
- categories: [
635
- { label: 'Mammals', items: ['Dog', 'Cat', 'Elephant', 'wolf'] },
636
- { label: 'Birds', items: ['Eagle', 'Sparrow'] }
637
- ],
638
- distractors: ['@:soup', '@:strawberry']
639
- },
640
- assets: `
351
+ <!-- Header -->
352
+ <div class="header">
353
+ <h1>🎯 CogniKit Beta</h1>
354
+ <div class="header-controls">
355
+ <button class="icon-button" id="settings-btn" title="Settings">⚙️</button>
356
+ <button class="icon-button" id="config-btn" title="Configure Interaction">🎨</button>
357
+ <button class="icon-button" id="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
358
+ </div>
359
+ </div>
360
+
361
+ <!-- Main Viewport -->
362
+ <div class="main-viewport">
363
+ <div class="interaction-container">
364
+ <edu-window id="interaction-viewport"></edu-window>
365
+ </div>
366
+ </div>
367
+
368
+ <!-- Settings Modal -->
369
+ <div class="modal-overlay" id="settings-modal">
370
+ <div class="modal">
371
+ <div class="modal-header">
372
+ <h2>⚙️ Settings</h2>
373
+ <button class="modal-close" id="settings-close">×</button>
374
+ </div>
375
+ <div class="modal-body">
376
+ <div class="form-group">
377
+ <label for="variant-select">Variant Style</label>
378
+ <select id="variant-select">
379
+ <option value="elegant" selected>Elegant</option>
380
+ <option value="playful">Playful</option>
381
+ <option value="outline">Outline</option>
382
+ <option value="letter">Letter</option>
383
+ <option value="sign">Sign</option>
384
+ <option value="minimal">Minimal</option>
385
+ <option value="glass">Glass</option>
386
+ <option value="card">Card</option>
387
+ <option value="empty">Empty</option>
388
+ </select>
389
+ </div>
390
+
391
+ <div class="form-group">
392
+ <label for="theme-select">Theme</label>
393
+ <select id="theme-select"></select>
394
+ <div class="form-help">Uses Cognikit theme presets directly in the preview.
395
+ </div>
396
+ </div>
397
+
398
+ <div class="form-group">
399
+ <label for="accent-color">Accent Color</label>
400
+ <input type="color" id="accent-color" value="#67CB90">
401
+ <div class="form-help">Color customization not yet implemented</div>
402
+ </div>
403
+ </div>
404
+ <div class="modal-footer">
405
+ <button class="btn" id="settings-cancel">Cancel</button>
406
+ <button class="btn btn-primary" id="settings-apply">Apply</button>
407
+ </div>
408
+ </div>
409
+ </div>
410
+
411
+ <!-- Configuration Modal -->
412
+ <div class="modal-overlay" id="config-modal">
413
+ <div class="modal">
414
+ <div class="modal-header">
415
+ <h2>🎨 Configure Interaction</h2>
416
+ <button class="modal-close" id="config-close">×</button>
417
+ </div>
418
+ <div class="modal-body">
419
+ <div class="form-group">
420
+ <label for="interaction-type">Interaction Type</label>
421
+ <select id="interaction-type">
422
+ <option value="SequentialClassification">Sequential Classification
423
+ </option>
424
+ <option value="SimultaneousAssociation">Simultaneous Association
425
+ </option>
426
+ <option value="OpenClassification">Open Classification</option>
427
+ <option value="MarkTheWords">Mark The Words</option>
428
+ <option value="RankOrder">Rank Order</option>
429
+ <option value="ListRecall">List Recall</option>
430
+ <option value="ClassificationMatrix">Classification Matrix</option>
431
+ <option value="NaryChoice">Nary Choice Table</option>
432
+ <option value="MCQ" selected>MCQ/MRQ</option>
433
+ <option value="FillBlanks">Fill Blanks</option>
434
+ </select>
435
+ </div>
436
+
437
+ <div class="form-group">
438
+ <label for="interaction-heading">Heading</label>
439
+ <input type="text" id="interaction-heading" value="Butterfly Life Cycle"
440
+ placeholder="Enter heading...">
441
+ </div>
442
+
443
+ <div class="form-group">
444
+ <label for="interaction-timer">Timer (seconds)</label>
445
+ <input type="number" id="interaction-timer" value="120" min="0"
446
+ placeholder="0 for no timer">
447
+ </div>
448
+
449
+ <div class="form-group">
450
+ <label for="data-json">Data (JSON)</label>
451
+ <textarea id="data-json" placeholder="Enter JSON data...">{
452
+ "type": "seriation",
453
+ "items": ["Egg", "Caterpillar", "Chrysalis", "Butterfly", "@:butterfly"]
454
+ }</textarea>
455
+ <div class="form-help">Must be valid JSON format</div>
456
+ </div>
457
+
458
+ <div class="form-group">
459
+ <label for="assets-yaml">Assets (YAML)</label>
460
+ <textarea id="assets-yaml" placeholder="Enter YAML assets...">butterfly:
461
+ type: image
462
+ url:
463
+ https://images.unsplash.com/photo-1533048324814-79b0a31982f1?q=80&w=768</textarea>
464
+ <div class="form-help">Optional. Leave empty if no assets needed</div>
465
+ </div>
466
+ </div>
467
+ <div class="modal-footer">
468
+ <button class="btn" id="config-cancel">Cancel</button>
469
+ <button class="btn btn-primary" id="config-apply">Render Interaction</button>
470
+ </div>
471
+ </div>
472
+ </div>
473
+
474
+ <script type="module">
475
+ import {
476
+ OpenClassification,
477
+ SequentialClassification,
478
+ SimultaneousAssociation,
479
+ MarkTheWords,
480
+ RankOrder,
481
+ ListRecall,
482
+ ClassificationMatrix,
483
+ NaryChoiceTable,
484
+ MCQ,
485
+ FillBlanks,
486
+ getCognikitThemePresets,
487
+ parseYamlAssets,
488
+ setCognikitTheme,
489
+ validateAndNormalizeAssets,
490
+ configureCognikit
491
+ } from './app.js';
492
+
493
+ // State
494
+ let currentVariant = 'elegant';
495
+ let currentTheme = 'default-light';
496
+ let currentInteraction = null;
497
+ const themePresets = getCognikitThemePresets();
498
+ const STORAGE_KEY = 'cognikit:playground:settings';
499
+
500
+ // DOM Elements
501
+ const viewport = document.getElementById('interaction-viewport');
502
+ const settingsBtn = document.getElementById('settings-btn');
503
+ const configBtn = document.getElementById('config-btn');
504
+ const fullscreenBtn = document.getElementById('fullscreen-btn');
505
+ const settingsModal = document.getElementById('settings-modal');
506
+ const configModal = document.getElementById('config-modal');
507
+
508
+ // Modal Controls
509
+ const settingsClose = document.getElementById('settings-close');
510
+ const settingsCancel = document.getElementById('settings-cancel');
511
+ const settingsApply = document.getElementById('settings-apply');
512
+ const configClose = document.getElementById('config-close');
513
+ const configCancel = document.getElementById('config-cancel');
514
+ const configApply = document.getElementById('config-apply');
515
+
516
+ // Form Elements
517
+ const variantSelect = document.getElementById('variant-select');
518
+ const themeSelect = document.getElementById('theme-select');
519
+ const accentColor = document.getElementById('accent-color');
520
+ const interactionType = document.getElementById('interaction-type');
521
+ const interactionHeading = document.getElementById('interaction-heading');
522
+ const interactionTimer = document.getElementById('interaction-timer');
523
+ const dataJson = document.getElementById('data-json');
524
+ const assetsYaml = document.getElementById('assets-yaml');
525
+
526
+ function getStoredSettings() {
527
+ try {
528
+ const raw = localStorage.getItem(STORAGE_KEY);
529
+ return raw ? JSON.parse(raw) : {};
530
+ } catch {
531
+ return {};
532
+ }
533
+ }
534
+
535
+ function persistSettings(partial = {}) {
536
+ const next = {
537
+ ...getStoredSettings(),
538
+ ...partial
539
+ };
540
+
541
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
542
+ }
543
+
544
+ configureCognikit({
545
+ audioBaseUrl: '/assets/audio/'
546
+ });
547
+
548
+ // Modal Management
549
+ function openModal(modal) {
550
+ modal.classList.add('active');
551
+ }
552
+
553
+ function closeModal(modal) {
554
+ modal.classList.remove('active');
555
+ }
556
+
557
+ function rgbTripletToCss(rgbTriplet) {
558
+ return `rgb(${rgbTriplet.trim().split(/\s+/).join(', ')})`;
559
+ }
560
+
561
+ function rgbTripletToHex(rgbTriplet) {
562
+ const [r, g, b] = rgbTriplet.trim().split(/\s+/).map(Number);
563
+ return `#${[r, g, b].map((value) => value.toString(16).padStart(2, '0')).join('')}`;
564
+ }
565
+
566
+ function syncThemeMeta(themeName) {
567
+ const theme = themePresets[themeName];
568
+ const metaTheme = document.querySelector('meta[name="theme-color"]');
569
+ if (theme && metaTheme) {
570
+ metaTheme.setAttribute('content', rgbTripletToCss(theme["--edu-bg"]));
571
+ }
572
+ }
573
+
574
+ function applyTheme(themeName) {
575
+ currentTheme = themeName;
576
+ setCognikitTheme(themeName);
577
+ syncThemeMeta(themeName);
578
+
579
+ const theme = themePresets[themeName];
580
+ if (theme) {
581
+ accentColor.value = rgbTripletToHex(theme["--edu-first-accent"]);
582
+ }
583
+ }
584
+
585
+ function applyAccent(hexColor) {
586
+ if (!hexColor || !/^#[0-9a-fA-F]{6}$/.test(hexColor)) return;
587
+
588
+ const r = parseInt(hexColor.slice(1, 3), 16);
589
+ const g = parseInt(hexColor.slice(3, 5), 16);
590
+ const b = parseInt(hexColor.slice(5, 7), 16);
591
+
592
+ accentColor.value = hexColor;
593
+ document.documentElement.style.setProperty('--edu-first-accent', `${r} ${g} ${b}`);
594
+ }
595
+
596
+ function syncTemplateForm(type) {
597
+ const template = dataTemplates[type];
598
+ if (!template) return;
599
+
600
+ let dataToSerialize = template.data;
601
+ if (template.data.answerKey instanceof Map) {
602
+ dataToSerialize = {
603
+ ...template.data,
604
+ answerKey: Object.fromEntries(template.data.answerKey)
605
+ };
606
+ }
607
+
608
+ dataJson.value = JSON.stringify(dataToSerialize, null, 2);
609
+ assetsYaml.value = template.assets;
610
+ }
611
+
612
+ function populateThemes() {
613
+ themeSelect.innerHTML = Object.keys(themePresets).map((themeName) => {
614
+ const label = themeName
615
+ .split('-')
616
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
617
+ .join(' ');
618
+ const selected = themeName === currentTheme ? 'selected' : '';
619
+ return `<option value="${themeName}" ${selected}>${label}</option>`;
620
+ }).join('');
621
+ }
622
+
623
+ accentColor.addEventListener('change', (e) => {
624
+ const color = e.target.value;
625
+ applyAccent(color);
626
+ persistSettings({accent: color});
627
+ });
628
+
629
+ // Event Listeners - Modal Toggles
630
+ settingsBtn.addEventListener('click', () => openModal(settingsModal));
631
+ configBtn.addEventListener('click', () => openModal(configModal));
632
+
633
+ settingsClose.addEventListener('click', () => closeModal(settingsModal));
634
+ settingsCancel.addEventListener('click', () => closeModal(settingsModal));
635
+
636
+ configClose.addEventListener('click', () => closeModal(configModal));
637
+ configCancel.addEventListener('click', () => closeModal(configModal));
638
+
639
+ // Close modal on overlay click
640
+ settingsModal.addEventListener('click', (e) => {
641
+ if (e.target === settingsModal) closeModal(settingsModal);
642
+ });
643
+ configModal.addEventListener('click', (e) => {
644
+ if (e.target === configModal) closeModal(configModal);
645
+ });
646
+
647
+ // Settings Apply
648
+ settingsApply.addEventListener('click', () => {
649
+ currentVariant = variantSelect.value;
650
+ applyTheme(themeSelect.value);
651
+ if (viewport) {
652
+ currentInteraction.onVariantChange(currentVariant);
653
+ viewport.setAttribute('variant', currentVariant);
654
+ }
655
+ persistSettings({
656
+ variant: currentVariant,
657
+ theme: currentTheme,
658
+ accent: accentColor.value
659
+ });
660
+ closeModal(settingsModal);
661
+ });
662
+
663
+ // Fullscreen Toggle
664
+ fullscreenBtn.addEventListener('click', () => {
665
+ document.body.classList.toggle('fullscreen');
666
+ fullscreenBtn.textContent = document.body.classList.contains('fullscreen') ? '⛶' : '⛶';
667
+ });
668
+
669
+ // Interaction Type Data Templates
670
+ const dataTemplates = {
671
+ SequentialClassification: {
672
+ data: {
673
+ type: 'classification',
674
+ categories: [
675
+ {label: 'Ocean', items: ['Dolphin', 'Shark', 'Octopus']},
676
+ {label: 'Land', items: ['Lion', 'Elephant', 'Tiger']},
677
+ {label: 'Air', items: ['Eagle', 'Parrot', 'Hawk']}
678
+ ]
679
+ },
680
+ assets: ''
681
+ },
682
+ SimultaneousAssociation: {
683
+ data: {
684
+ type: 'association',
685
+ pairs: [
686
+ {left: 'Dog', right: 'Barks'},
687
+ {left: 'Cat', right: 'Meows'},
688
+ {left: 'Bird', right: 'Chirps'},
689
+ {left: '@:cow', right: 'Moos'},
690
+ {left: 'Lion', right: 'Roars'}
691
+ ],
692
+ distractors: ['quacks', 'oinks'],
693
+ columnNames: {left: "Animals", right: "Onomatopoeia"}
694
+ },
695
+ assets: 'cow:\n type: image\n url: https://images.unsplash.com/photo-1595365691689-6b7b4e1970cf?q=80&w=765&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'
696
+ },
697
+ OpenClassification: {
698
+ data: {
699
+ type: 'classification',
700
+ categories: [
701
+ {label: 'Mammals', items: ['Dog', 'Cat', 'Elephant', 'wolf']},
702
+ {label: 'Birds', items: ['Eagle', 'Sparrow']},
703
+ {label: 'Invalid', items: ['@:soup', '@:strawberry']}
704
+ ],
705
+ },
706
+ assets: `
641
707
  strawberry:\n type: image\n url: https://images.unsplash.com/photo-1622921491193-345ffb510f6f?q=80&w=1171&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
642
708
  soup:\n type: image\n url: https://plus.unsplash.com/premium_photo-1669831178095-005ed789250a?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
643
709
  `,
644
- },
645
- RankOrder: {
646
- data: {
647
- type: 'seriation',
648
- items: ['Bronze Age', 'Iron Age', 'Middle Ages', 'Renaissance', 'Industrial Revolution', 'Information Age']
649
- },
650
- assets: ''
651
- },
652
- ListRecall: {
653
- data: {
654
- type: 'seriation',
655
- items: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
656
- },
657
- assets: ''
658
- },
659
- MCQ: {
660
- data: {
661
- type: 'recognition',
662
- data: [
663
- {
664
- question: '@:paris',
665
- correctOptions: ['Paris'],
666
- options: ['London', 'Berlin', 'Madrid']
667
- },
668
- {
669
- question: 'Which of these are programming languages?',
670
- correctOptions: ['Python', 'JavaScript', 'Java'],
671
- options: ['HTML', 'CSS']
672
- },
673
- {
674
- question: '@:exampleHtml',
675
- correctOptions: ['4'],
676
- options: ['3', '5', '6']
677
- },
678
- {
679
- question: '@:movements',
680
- correctOptions: ['Dancing'],
681
- options: ['Sleeping', 'Cooking', 'Kissing']
682
710
  },
683
- {
684
- question: '@:laugh',
685
- correctOptions: ["laughing"],
686
- options: ['Crying', 'Shouting']
687
- }
688
- ]
689
- },
690
- assets:
691
- `
711
+ MarkTheWords: {
712
+ data: {
713
+ type: 'mark-the-words',
714
+ slides: [
715
+ {
716
+ parts: [
717
+ 'In', 'the', 'garden', ',', 'sweet', 'strawberries', 'grow', 'near',
718
+ 'crunchy', 'celery', '.', 'The', 'basket', 'is', 'filled', 'with',
719
+ 'ripe', 'peaches', ',', 'tart', 'lemons', ',', 'and', 'fresh',
720
+ 'spinach', '.', 'We', 'also', 'picked', 'blueberries', 'and',
721
+ 'some', 'bitter', 'kale', 'for', 'the', 'salad', '.'
722
+ ],
723
+ targets: [5, 17, 20, 29]
724
+ },
725
+ {
726
+ parts: [
727
+ 'In', 'the', 'garden', ',', 'sweet', 'strawberries', 'grow', 'near',
728
+ 'crunchy', 'celery', '.', 'The', 'basket', 'is', 'filled', 'with',
729
+ 'ripe', 'peaches', ',', 'tart', 'lemons', ',', 'and', 'fresh',
730
+ 'spinach', '.', 'We', 'also', 'picked', 'blueberries', 'and',
731
+ 'some', 'bitter', 'kale', 'for', 'the', 'salad', '.'
732
+ ],
733
+ targets: [0, 1, 2, 3]
734
+
735
+ }
736
+ ]
737
+ },
738
+ assets: ''
739
+ },
740
+ RankOrder: {
741
+ data: {
742
+ type: 'seriation',
743
+ items: ['Bronze Age', 'Iron Age', 'Middle Ages', 'Renaissance', 'Industrial Revolution', 'Information Age']
744
+ },
745
+ assets: ''
746
+ },
747
+ ListRecall: {
748
+ data: {
749
+ type: 'seriation',
750
+ items: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
751
+ },
752
+ assets: ''
753
+ },
754
+ MCQ: {
755
+ data: {
756
+ type: 'recognition',
757
+ items: [
758
+ {
759
+ question: '@:paris',
760
+ correctOptions: ['Paris'],
761
+ options: ['London', 'Berlin', 'Madrid']
762
+ },
763
+ {
764
+ question: 'Which of these are programming languages?',
765
+ correctOptions: ['Python', 'JavaScript', 'Java'],
766
+ options: ['HTML', 'CSS']
767
+ },
768
+ {
769
+ question: '@:exampleHtml',
770
+ correctOptions: ['4'],
771
+ options: ['3', '5', '6']
772
+ },
773
+ {
774
+ question: '@:movements',
775
+ correctOptions: ['Dancing'],
776
+ options: ['Sleeping', 'Cooking', 'Kissing']
777
+ },
778
+ {
779
+ question: '@:laugh',
780
+ correctOptions: ["laughing"],
781
+ options: ['Crying', 'Shouting']
782
+ }
783
+ ]
784
+ },
785
+ assets:
786
+ `
692
787
  paris:\n type: image\n url: https://plus.unsplash.com/premium_photo-1718035557075-5111d9d906d2?q=80&w=1171&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\n dialog: true
693
788
  exampleHtml:
694
789
  type: html
@@ -698,102 +793,236 @@ exampleHtml:
698
793
  movements:\n type: video\n url: https://www.pexels.com/download/video/6548176\n dialog: false
699
794
 
700
795
  laugh:\n type: audio\n url: https://www.soundjay.com/human/sounds/man-laughing-01.mp3\n dialog: true
701
- `
702
- }
703
- };
704
-
705
- dataJson.value = JSON.stringify(dataTemplates[interactionType.value].data);
706
- assetsYaml.value = dataTemplates[interactionType.value].assets;
707
-
708
- // Update form when interaction type changes
709
- interactionType.addEventListener('change', (e) => {
710
- const template = dataTemplates[e.target.value];
711
- if (template) {
712
- // Convert Map to object for JSON serialization
713
- let dataToSerialize = template.data;
714
- if (template.data.answerKey instanceof Map) {
715
- dataToSerialize = {
716
- ...template.data,
717
- answerKey: Object.fromEntries(template.data.answerKey)
718
- };
719
- }
720
- dataJson.value = JSON.stringify(dataToSerialize, null, 2);
721
- assetsYaml.value = template.assets;
722
- }
723
- });
724
-
725
- // Render Interaction
726
- configApply.addEventListener('click', () => {
727
- try {
728
- let data = JSON.parse(dataJson.value);
729
-
730
- if (data.answerKey && typeof data.answerKey === 'object') {
731
- data.answerKey = new Map(Object.entries(data.answerKey));
732
- }
733
-
734
- let assets = null;
735
- const yamlContent = assetsYaml.value.trim();
736
- if (yamlContent) {
737
- const parsedAssets = parseYamlAssets(yamlContent);
738
- assets = validateAndNormalizeAssets(parsedAssets);
739
- }
740
-
741
- const config = {
742
- variant: currentVariant,
743
- shuffle: true,
744
- animationsEnabled: true,
745
- prompt: "Complete the quizz",
746
- promptData: "https://plus.unsplash.com/premium_photo-1718035557075-5111d9d906d2?q=80&w=1171&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
747
- promptModality: 'image',
748
- timer: 60,
749
- attemptLimit: 2
750
- };
751
-
752
- const type = interactionType.value;
753
- let interaction;
754
-
755
- switch (type) {
756
- case 'SequentialClassification':
757
- interaction = new SequentialClassification(data, config, assets);
758
- break;
759
- case 'SimultaneousAssociation':
760
- interaction = new SimultaneousAssociation(data, config, assets);
761
- break;
762
- case 'OpenClassification':
763
- interaction = new OpenClassification(data, config, assets);
764
- break;
765
- case 'RankOrder':
766
- interaction = new RankOrder(data, config, assets);
767
- break;
768
- case 'ListRecall':
769
- interaction = new ListRecall(data, config, assets);
770
- break;
771
- case 'MCQ':
772
- interaction = new MCQ(data, config, assets);
773
- break;
774
- default:
775
- throw new Error('Unknown interaction type');
776
- }
777
-
778
- // Clear any previous state and set up new interaction
779
- // The shell now reads everything from interaction.config
780
- viewport.setInteraction(interaction);
781
- currentInteraction = interaction;
782
- closeModal(configModal);
783
-
784
- console.log('✅ Interaction rendered successfully');
785
- } catch (error) {
786
- console.error('❌ Error rendering interaction:', error);
787
- alert('Error: ' + error.message);
788
- }
789
- });
790
-
791
- // Initialize with default interaction
792
- window.addEventListener('DOMContentLoaded', () => {
793
- populateThemes();
794
- applyTheme(currentTheme);
795
- configApply.click();
796
- });
797
- </script>
796
+ `
797
+ },
798
+ ClassificationMatrix: {
799
+ data: {
800
+ rows: [
801
+ "Talk with my colleagues",
802
+ "Eat",
803
+ "Report Attendance",
804
+ "Complete my exams",
805
+ "Basketball Club",
806
+ "Sleep",
807
+ "Watch TV"
808
+ ],
809
+ cols: [
810
+ "work",
811
+ "school",
812
+ "home"
813
+ ],
814
+ answerKey: {
815
+ "work": [
816
+ "Talk with my colleagues",
817
+ "Eat",
818
+ "Report Attendance"
819
+ ],
820
+ "school": [
821
+ "Eat",
822
+ "Complete my exams",
823
+ "Basketball Club"
824
+ ],
825
+ "home": [
826
+ "Eat",
827
+ "Sleep",
828
+ "Watch TV"
829
+ ]
830
+ }
831
+ },
832
+ assets: null
833
+ },
834
+ FillBlanks: {
835
+ data: {
836
+ type: "fill-the-blanks",
837
+ items: [
838
+ {
839
+ parts: ["I", "am", "a", "beautiful", "person", "that", "loves", "2", "party", "all", "night."],
840
+ items: [
841
+ {id: "text-one-a", ref: "tx", expectedValue: {id: "text-one-a", type: "text", value: ["a"]}},
842
+ {id: "text-one-b", ref: "nm", expectedValue: {id: "text-one-b", type: "number", targets: [2]}},
843
+ ],
844
+ targets: [2, 7]
845
+ },
846
+ {
847
+ parts: ["She", "is", "a", "beautiful", "person", "that", "loves", "2", "party", "all", "night."],
848
+ items: [
849
+ {id: "text-one-c", ref: "tx", expectedValue: {id: "text-one-c", type: "text", value: ["a"]}},
850
+ {id: "text-one-d", ref: "nm", expectedValue: {id: "text-one-d", type: "number", targets: [2]}},
851
+ ],
852
+ targets: [2, 7]
853
+ },
854
+
855
+ ],
856
+ sequential: false
857
+ },
858
+ assets: null
859
+ },
860
+ NaryChoice: {
861
+ data: {
862
+ type: "nary-choice-table",
863
+ cols: [
864
+ "Asia",
865
+ "Europe",
866
+ "Africa",
867
+ "South America"
868
+ ],
869
+ rows: [
870
+ "Japan",
871
+ "Brazil",
872
+ "Kenya",
873
+ "Spain",
874
+ "Venezuela",
875
+ "China",
876
+ "Italy"
877
+ ],
878
+ answerKey: {
879
+ "Asia": [
880
+ "Japan",
881
+ "China"
882
+ ],
883
+ "Africa": [
884
+ "Kenya"
885
+ ],
886
+ "Europe": [
887
+ "Spain",
888
+ "Italy"
889
+ ],
890
+ "South America": [
891
+ "Brazil",
892
+ "Venezuela"
893
+ ]
894
+ }
895
+ },
896
+ assets: null
897
+ }
898
+ }
899
+
900
+ syncTemplateForm(interactionType.value);
901
+
902
+ // Update form when interaction type changes
903
+ interactionType.addEventListener('change', (e) => {
904
+ syncTemplateForm(e.target.value);
905
+ persistSettings({interactionType: e.target.value});
906
+ });
907
+
908
+ // Render Interaction
909
+ configApply.addEventListener('click', () => {
910
+ try {
911
+ let data = JSON.parse(dataJson.value);
912
+
913
+ if (data.answerKey && typeof data.answerKey === 'object') {
914
+ data.answerKey = new Map(Object.entries(data.answerKey));
915
+ }
916
+
917
+ let assets = null;
918
+ const yamlContent = assetsYaml.value.trim();
919
+ if (yamlContent) {
920
+ const parsedAssets = parseYamlAssets(yamlContent);
921
+ assets = validateAndNormalizeAssets(parsedAssets);
922
+ }
923
+
924
+ const config = {
925
+ variant: currentVariant,
926
+ shuffle: true,
927
+ animationsEnabled: true,
928
+ prompt: "Complete the quizz",
929
+ promptData: "https://plus.unsplash.com/premium_photo-1718035557075-5111d9d906d2?q=80&w=1171&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
930
+ promptModality: 'image',
931
+ timer: 131,
932
+ attemptLimit: 3,
933
+ points: 25,
934
+ immediateFeedback: true,
935
+ wallpaper: "mountains",
936
+ soundEnabled: true
937
+ };
938
+
939
+ const type = interactionType.value;
940
+ let interaction;
941
+
942
+ switch (type) {
943
+ case 'SequentialClassification':
944
+ interaction = new SequentialClassification(data, config, assets);
945
+ break;
946
+ case 'SimultaneousAssociation':
947
+ interaction = new SimultaneousAssociation(data, config, assets);
948
+ break;
949
+ case 'OpenClassification':
950
+ interaction = new OpenClassification(data, config, assets);
951
+ break;
952
+ case 'MarkTheWords':
953
+ interaction = new MarkTheWords(data, config);
954
+ break;
955
+ case 'RankOrder':
956
+ interaction = new RankOrder(data, config, assets);
957
+ break;
958
+ case 'ListRecall':
959
+ interaction = new ListRecall(data, config, assets);
960
+ break;
961
+ case 'ClassificationMatrix':
962
+ interaction = new ClassificationMatrix(data, config, assets);
963
+ break;
964
+ case 'NaryChoice':
965
+ interaction = new NaryChoiceTable(data, config, assets);
966
+ break;
967
+ case 'MCQ':
968
+ interaction = new MCQ(data, config, assets);
969
+ break;
970
+ case 'FillBlanks':
971
+ interaction = new FillBlanks(data, config);
972
+ break;
973
+ default:
974
+ throw new Error('Unknown interaction type');
975
+ }
976
+
977
+ // Clear any previous state and set up new interaction
978
+ // The shell now reads everything from interaction.config
979
+ viewport.setInteraction(interaction);
980
+ currentInteraction = interaction;
981
+ persistSettings({
982
+ interactionType: type,
983
+ theme: currentTheme,
984
+ accent: accentColor.value,
985
+ variant: currentVariant
986
+ });
987
+ closeModal(configModal);
988
+
989
+ console.log('✅ Interaction rendered successfully');
990
+ } catch (error) {
991
+ console.error('❌ Error rendering interaction:', error);
992
+ alert('Error: ' + error.message);
993
+ }
994
+ });
995
+
996
+ // Initialize with default interaction
997
+ window.addEventListener('DOMContentLoaded', () => {
998
+ const stored = getStoredSettings();
999
+
1000
+ if (stored.variant) {
1001
+ currentVariant = stored.variant;
1002
+ variantSelect.value = stored.variant;
1003
+ }
1004
+
1005
+ if (stored.interactionType && dataTemplates[stored.interactionType]) {
1006
+ interactionType.value = stored.interactionType;
1007
+ }
1008
+
1009
+ populateThemes();
1010
+ if (stored.theme && themePresets[stored.theme]) {
1011
+ currentTheme = stored.theme;
1012
+ }
1013
+
1014
+ themeSelect.value = currentTheme;
1015
+ applyTheme(currentTheme);
1016
+
1017
+ if (stored.accent) {
1018
+ applyAccent(stored.accent);
1019
+ }
1020
+
1021
+ syncTemplateForm(interactionType.value);
1022
+ viewport.setAttribute('variant', currentVariant);
1023
+ configApply.click();
1024
+ });
1025
+ </script>
798
1026
  </body>
1027
+
799
1028
  </html>