@windward/core 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/components/Content/Blocks/Accordion.vue +1 -1
- package/components/Content/Blocks/BlockQuote.vue +1 -1
- package/components/Content/Blocks/ClickableIcons.vue +4 -3
- package/components/Content/Blocks/Email.vue +1 -1
- package/components/Content/Blocks/FileDownload.vue +2 -2
- package/components/Content/Blocks/Image.vue +140 -0
- package/components/Content/Blocks/OpenResponse.vue +419 -5
- package/components/Content/Blocks/ScenarioChoice.vue +1 -1
- package/components/Content/Blocks/Tab.vue +1 -1
- package/components/Content/Blocks/UserUpload.vue +1 -1
- package/components/Content/Blocks/Video.vue +361 -22
- package/components/Settings/ClickableIconsSettings.vue +3 -3
- package/components/Settings/ImageSettings.vue +26 -0
- package/components/Settings/OpenResponseSettings.vue +59 -0
- package/components/Settings/VideoSettings/SourcePicker.vue +40 -32
- package/components/utils/ContentViewer.vue +180 -1
- package/i18n/en-US/components/content/blocks/open_response.ts +18 -0
- package/i18n/en-US/components/settings/open_response.ts +5 -0
- package/i18n/es-ES/components/content/blocks/open_response.ts +18 -0
- package/i18n/es-ES/components/settings/open_response.ts +5 -0
- package/i18n/sv-SE/components/content/blocks/open_response.ts +18 -0
- package/i18n/sv-SE/components/settings/open_response.ts +5 -0
- package/package.json +2 -2
- package/test/Components/Settings/ClickableIconsSettings.spec.js +1 -1
|
@@ -74,12 +74,17 @@
|
|
|
74
74
|
}}
|
|
75
75
|
</v-alert>
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
<!-- Display ALL linked captions when inheriting -->
|
|
78
|
+
<div v-if="sourceInherit && hasLinkedCaptions">
|
|
79
|
+
<div v-for="(caption, index) in linkedCaptions" :key="caption.file_asset_id || index" class="mb-2">
|
|
80
|
+
<ContentBlockAsset
|
|
81
|
+
:value="caption"
|
|
82
|
+
:assets="assets"
|
|
83
|
+
:label="getCaptionLabel(caption)"
|
|
84
|
+
disabled
|
|
85
|
+
></ContentBlockAsset>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
83
88
|
|
|
84
89
|
<ContentBlockAsset
|
|
85
90
|
v-if="!sourceInherit"
|
|
@@ -169,19 +174,12 @@ export default {
|
|
|
169
174
|
data() {
|
|
170
175
|
return {
|
|
171
176
|
sourceInherit: true,
|
|
172
|
-
linkedCaptions:
|
|
177
|
+
linkedCaptions: [],
|
|
173
178
|
}
|
|
174
179
|
},
|
|
175
180
|
computed: {
|
|
176
181
|
hasLinkedCaptions() {
|
|
177
|
-
return this.linkedCaptions
|
|
178
|
-
},
|
|
179
|
-
linkedCaptionsName() {
|
|
180
|
-
return _.get(
|
|
181
|
-
this.linkedCaptions,
|
|
182
|
-
'asset.name',
|
|
183
|
-
_.get(this.linkedCaptions, 'asset.public_url', '???')
|
|
184
|
-
)
|
|
182
|
+
return this.linkedCaptions && this.linkedCaptions.length > 0
|
|
185
183
|
},
|
|
186
184
|
isFromUrl() {
|
|
187
185
|
if (
|
|
@@ -225,7 +223,7 @@ export default {
|
|
|
225
223
|
methods: {
|
|
226
224
|
onSourceChange(file, rawFile) {
|
|
227
225
|
this.linkedCaptions = this.getLinkedCaptions(rawFile)
|
|
228
|
-
this.sourceInherit = this.
|
|
226
|
+
this.sourceInherit = this.hasLinkedCaptions
|
|
229
227
|
|
|
230
228
|
this.$emit('change:source', file, rawFile)
|
|
231
229
|
},
|
|
@@ -238,26 +236,36 @@ export default {
|
|
|
238
236
|
onUpdateAssets(assets) {
|
|
239
237
|
this.$emit('update:assets', assets)
|
|
240
238
|
},
|
|
239
|
+
/**
|
|
240
|
+
* Get ALL linked captions (not just the first one)
|
|
241
|
+
* @param {Object} file - The video file object
|
|
242
|
+
* @returns {Array} Array of VTT caption files
|
|
243
|
+
*/
|
|
241
244
|
getLinkedCaptions(file) {
|
|
242
245
|
if (!file) {
|
|
243
|
-
return
|
|
246
|
+
return []
|
|
244
247
|
}
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return (
|
|
254
|
-
ext === 'vtt' ||
|
|
255
|
-
mime === 'text/vtt' ||
|
|
256
|
-
name.endsWith('.vtt')
|
|
257
|
-
)
|
|
258
|
-
})
|
|
248
|
+
// Get ALL VTT files from linked_assets (not just the first one)
|
|
249
|
+
const linkedCaptions = _.filter(
|
|
250
|
+
_.get(file, 'asset.linked_assets', []),
|
|
251
|
+
function (f) {
|
|
252
|
+
return _.get(f, 'asset.metadata.extension', '') === 'vtt'
|
|
253
|
+
}
|
|
254
|
+
)
|
|
259
255
|
|
|
260
|
-
return
|
|
256
|
+
return linkedCaptions || []
|
|
257
|
+
},
|
|
258
|
+
/**
|
|
259
|
+
* Get the label with locale in parentheses for a caption
|
|
260
|
+
* Format: "(EN-US)" - if no locale, assume English
|
|
261
|
+
* @param {Object} caption - The caption file object
|
|
262
|
+
* @returns {string} Label with locale code
|
|
263
|
+
*/
|
|
264
|
+
getCaptionLabel(caption) {
|
|
265
|
+
const localeCode = _.get(caption, 'locale.code') ||
|
|
266
|
+
_.get(caption, 'asset.metadata.locale') ||
|
|
267
|
+
'EN-US'
|
|
268
|
+
return `(${localeCode})`
|
|
261
269
|
},
|
|
262
270
|
},
|
|
263
271
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
<component
|
|
3
3
|
:is="convertedContent"
|
|
4
4
|
v-show="value"
|
|
5
|
-
:class="
|
|
5
|
+
:class="contentClasses"
|
|
6
|
+
:dir="textDirection"
|
|
6
7
|
></component>
|
|
7
8
|
</template>
|
|
8
9
|
|
|
@@ -28,7 +29,28 @@ export default {
|
|
|
28
29
|
...mapGetters({
|
|
29
30
|
course: 'course/get',
|
|
30
31
|
glossaryTerms: 'glossary/getTerms',
|
|
32
|
+
isRtl: 'course/isRtl',
|
|
33
|
+
courseTextDirection: 'course/textDirection',
|
|
31
34
|
}),
|
|
35
|
+
/**
|
|
36
|
+
* Get the text direction based on course locale
|
|
37
|
+
* @returns {string} 'rtl' or 'ltr'
|
|
38
|
+
*/
|
|
39
|
+
textDirection() {
|
|
40
|
+
return this.courseTextDirection || 'ltr'
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Generate CSS classes for the content container
|
|
44
|
+
* Includes RTL-specific classes when course is RTL
|
|
45
|
+
* @returns {Object} Object of CSS class names
|
|
46
|
+
*/
|
|
47
|
+
contentClasses() {
|
|
48
|
+
return {
|
|
49
|
+
'text-viewer': this.textViewer,
|
|
50
|
+
'rtl-content': this.isRtl,
|
|
51
|
+
'ltr-content': !this.isRtl,
|
|
52
|
+
}
|
|
53
|
+
},
|
|
32
54
|
convertedContent() {
|
|
33
55
|
let content = ''
|
|
34
56
|
if (this.value) {
|
|
@@ -96,4 +118,161 @@ caption {
|
|
|
96
118
|
color: var(--v-primary-base);
|
|
97
119
|
text-align: left;
|
|
98
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* RTL (Right-to-Left) Language Support Styles
|
|
124
|
+
* Applied when content is in RTL languages (Arabic, Hebrew, Persian, Urdu)
|
|
125
|
+
*
|
|
126
|
+
* Uses unicode-bidi: isolate to create a directional isolation boundary.
|
|
127
|
+
* This ensures RTL content displays correctly even when system UI is LTR.
|
|
128
|
+
*/
|
|
129
|
+
.rtl-content {
|
|
130
|
+
/* Base RTL text properties */
|
|
131
|
+
direction: rtl;
|
|
132
|
+
text-align: right;
|
|
133
|
+
unicode-bidi: isolate;
|
|
134
|
+
|
|
135
|
+
/* Paragraph and text alignment */
|
|
136
|
+
p,
|
|
137
|
+
h1,
|
|
138
|
+
h2,
|
|
139
|
+
h3,
|
|
140
|
+
h4,
|
|
141
|
+
h5,
|
|
142
|
+
h6,
|
|
143
|
+
li,
|
|
144
|
+
td,
|
|
145
|
+
th,
|
|
146
|
+
label,
|
|
147
|
+
span,
|
|
148
|
+
div {
|
|
149
|
+
text-align: right;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Lists - reverse bullet/number position */
|
|
153
|
+
ul,
|
|
154
|
+
ol {
|
|
155
|
+
padding-right: 40px;
|
|
156
|
+
padding-left: 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
li {
|
|
160
|
+
text-align: right;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Table alignment */
|
|
164
|
+
table {
|
|
165
|
+
direction: rtl;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
th,
|
|
169
|
+
td {
|
|
170
|
+
text-align: right;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Caption alignment for RTL */
|
|
174
|
+
caption {
|
|
175
|
+
text-align: right;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Blockquote RTL styling */
|
|
179
|
+
blockquote {
|
|
180
|
+
border-right: 4px solid var(--v-primary-base);
|
|
181
|
+
border-left: none;
|
|
182
|
+
padding-right: 16px;
|
|
183
|
+
padding-left: 0;
|
|
184
|
+
margin-right: 0;
|
|
185
|
+
margin-left: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Handle mixed LTR content within RTL */
|
|
189
|
+
/* Use explicit dir="ltr" on elements that need left-to-right */
|
|
190
|
+
[dir='ltr'] {
|
|
191
|
+
direction: ltr;
|
|
192
|
+
text-align: left;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Code blocks should remain LTR */
|
|
196
|
+
pre,
|
|
197
|
+
code {
|
|
198
|
+
direction: ltr;
|
|
199
|
+
text-align: left;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Images - keep centered or as specified */
|
|
203
|
+
img {
|
|
204
|
+
/* Images are typically direction-neutral */
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Form elements */
|
|
208
|
+
input,
|
|
209
|
+
textarea,
|
|
210
|
+
select {
|
|
211
|
+
direction: rtl;
|
|
212
|
+
text-align: right;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* LTR (Left-to-Right) Language Support Styles
|
|
218
|
+
* Applied when content is in LTR languages (English, Spanish, French, etc.)
|
|
219
|
+
*
|
|
220
|
+
* Uses unicode-bidi: isolate to create a directional isolation boundary.
|
|
221
|
+
* This ensures LTR content displays correctly even when system UI is RTL.
|
|
222
|
+
*/
|
|
223
|
+
.ltr-content {
|
|
224
|
+
direction: ltr;
|
|
225
|
+
text-align: left;
|
|
226
|
+
unicode-bidi: isolate;
|
|
227
|
+
|
|
228
|
+
/* Paragraph and text alignment */
|
|
229
|
+
p,
|
|
230
|
+
h1,
|
|
231
|
+
h2,
|
|
232
|
+
h3,
|
|
233
|
+
h4,
|
|
234
|
+
h5,
|
|
235
|
+
h6,
|
|
236
|
+
li,
|
|
237
|
+
td,
|
|
238
|
+
th,
|
|
239
|
+
label,
|
|
240
|
+
span,
|
|
241
|
+
div {
|
|
242
|
+
text-align: left;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Lists - standard bullet/number position */
|
|
246
|
+
ul,
|
|
247
|
+
ol {
|
|
248
|
+
padding-left: 40px;
|
|
249
|
+
padding-right: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
caption {
|
|
253
|
+
text-align: left;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Handle mixed RTL content within LTR */
|
|
257
|
+
[dir='rtl'] {
|
|
258
|
+
direction: rtl;
|
|
259
|
+
text-align: right;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* RTL-aware responsive styles
|
|
265
|
+
*/
|
|
266
|
+
@media (max-width: 600px) {
|
|
267
|
+
.rtl-content {
|
|
268
|
+
/* Maintain RTL on mobile */
|
|
269
|
+
text-align: right;
|
|
270
|
+
|
|
271
|
+
ul,
|
|
272
|
+
ol {
|
|
273
|
+
padding-right: 20px;
|
|
274
|
+
padding-left: 0;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
99
278
|
</style>
|
|
@@ -2,4 +2,22 @@ export default {
|
|
|
2
2
|
initial_setup: 'Add question text to get started.',
|
|
3
3
|
your_response: 'Your Response',
|
|
4
4
|
sample_response: 'Suggested/Sample Response',
|
|
5
|
+
ai_feedback: 'AI Feedback',
|
|
6
|
+
ai_feedback_generating: 'Generating feedback…',
|
|
7
|
+
ai_feedback_retry: 'Retry',
|
|
8
|
+
ai_feedback_sources: 'Suggested pages to review',
|
|
9
|
+
ai_feedback_not_available: 'No AI feedback available yet.',
|
|
10
|
+
ai_feedback_error:
|
|
11
|
+
'We could not generate AI feedback right now. Please try again.',
|
|
12
|
+
ai_feedback_timeout:
|
|
13
|
+
'AI feedback is taking longer than expected. Please try again.',
|
|
14
|
+
untitled_page: 'Untitled page',
|
|
15
|
+
understanding_level: 'Understanding level',
|
|
16
|
+
understanding_levels: {
|
|
17
|
+
strong: 'Strong Understanding',
|
|
18
|
+
clear: 'Clear Understanding',
|
|
19
|
+
developing: 'Developing Understanding',
|
|
20
|
+
emerging: 'Emerging Understanding',
|
|
21
|
+
limited: 'Limited Understanding',
|
|
22
|
+
},
|
|
5
23
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
question: 'Question',
|
|
3
3
|
sample_response: 'Suggested/Sample Response',
|
|
4
|
+
ai_mode_for_student: 'AI mode for student',
|
|
4
5
|
starting_text: 'Starting Text',
|
|
5
6
|
title: 'Open Response',
|
|
6
7
|
instructions:
|
|
7
8
|
'Type your answer in the text box and click "Submit" to save your response.',
|
|
9
|
+
validation: {
|
|
10
|
+
sample_response_required_ai_mode:
|
|
11
|
+
'Suggested/Sample Response is required when AI mode for student is enabled.',
|
|
12
|
+
},
|
|
8
13
|
}
|
|
@@ -2,4 +2,22 @@ export default {
|
|
|
2
2
|
initial_setup: 'Agrega el texto de la pregunta para comenzar.',
|
|
3
3
|
your_response: 'Tu respuesta',
|
|
4
4
|
sample_response: 'Respuesta sugerida/de muestra',
|
|
5
|
+
ai_feedback: 'Retroalimentación de IA',
|
|
6
|
+
ai_feedback_generating: 'Generando retroalimentación…',
|
|
7
|
+
ai_feedback_retry: 'Reintentar',
|
|
8
|
+
ai_feedback_sources: 'Páginas sugeridas para revisar',
|
|
9
|
+
ai_feedback_not_available: 'Aún no hay retroalimentación de IA disponible.',
|
|
10
|
+
ai_feedback_error:
|
|
11
|
+
'No pudimos generar la retroalimentación de IA en este momento. Por favor, vuelva a intentarlo.',
|
|
12
|
+
ai_feedback_timeout:
|
|
13
|
+
'La retroalimentación de IA está tardando más de lo esperado. Por favor, vuelva a intentarlo.',
|
|
14
|
+
untitled_page: 'Página sin título',
|
|
15
|
+
understanding_level: 'Nivel de comprensión',
|
|
16
|
+
understanding_levels: {
|
|
17
|
+
strong: 'Comprensión sólida',
|
|
18
|
+
clear: 'Comprensión clara',
|
|
19
|
+
developing: 'Comprensión en desarrollo',
|
|
20
|
+
emerging: 'Comprensión emergente',
|
|
21
|
+
limited: 'Comprensión limitada',
|
|
22
|
+
},
|
|
5
23
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
question: 'Pregunta',
|
|
3
3
|
sample_response: 'Respuesta sugerida/de muestra',
|
|
4
|
+
ai_mode_for_student: 'Modo de IA para el estudiante',
|
|
4
5
|
starting_text: 'Texto inicial',
|
|
5
6
|
title: 'Respuesta abierta',
|
|
6
7
|
instructions:
|
|
7
8
|
'Escriba su respuesta en el cuadro de texto y haga clic en "Enviar" para guardar su respuesta.',
|
|
9
|
+
validation: {
|
|
10
|
+
sample_response_required_ai_mode:
|
|
11
|
+
'La respuesta sugerida/de muestra es obligatoria cuando el modo de IA para el estudiante está habilitado.',
|
|
12
|
+
},
|
|
8
13
|
}
|
|
@@ -2,4 +2,22 @@ export default {
|
|
|
2
2
|
initial_setup: 'Lägg till frågetext för att komma igång.',
|
|
3
3
|
your_response: 'Ditt svar',
|
|
4
4
|
sample_response: 'Föreslagen/provsvar',
|
|
5
|
+
ai_feedback: 'AI-feedback',
|
|
6
|
+
ai_feedback_generating: 'Genererar feedback…',
|
|
7
|
+
ai_feedback_retry: 'Försök igen',
|
|
8
|
+
ai_feedback_sources: 'Föreslagna sidor att granska',
|
|
9
|
+
ai_feedback_not_available: 'Ingen AI-feedback tillgänglig ännu.',
|
|
10
|
+
ai_feedback_error:
|
|
11
|
+
'Vi kunde inte generera AI-feedback just nu. Försök igen.',
|
|
12
|
+
ai_feedback_timeout:
|
|
13
|
+
'AI-feedback tar längre tid än förväntat. Försök igen.',
|
|
14
|
+
untitled_page: 'Sida utan titel',
|
|
15
|
+
understanding_level: 'Förståelsenivå',
|
|
16
|
+
understanding_levels: {
|
|
17
|
+
strong: 'Stark förståelse',
|
|
18
|
+
clear: 'Tydlig förståelse',
|
|
19
|
+
developing: 'Förståelse under utveckling',
|
|
20
|
+
emerging: 'Begynnande förståelse',
|
|
21
|
+
limited: 'Begränsad förståelse',
|
|
22
|
+
},
|
|
5
23
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
question: 'Fråga',
|
|
3
3
|
sample_response: 'Föreslagen/provsvar',
|
|
4
|
+
ai_mode_for_student: 'AI-läge för studenten',
|
|
4
5
|
starting_text: 'Starttext',
|
|
5
6
|
title: 'Öppet svar',
|
|
6
7
|
instructions:
|
|
7
8
|
'Skriv ditt svar i textrutan och klicka på "Skicka" för att spara ditt svar.',
|
|
9
|
+
validation: {
|
|
10
|
+
sample_response_required_ai_mode:
|
|
11
|
+
'Föreslagen/provsvar krävs när AI-läge för studenten är aktiverat.',
|
|
12
|
+
},
|
|
8
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windward/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Windward UI Core Plugins",
|
|
5
5
|
"main": "plugin.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"homepage": "https://bitbucket.org/mindedge/windward-ui-plugin-core#readme",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@mindedge/vuetify-player": "^0.5.
|
|
24
|
+
"@mindedge/vuetify-player": "^0.5.2",
|
|
25
25
|
"@tinymce/tinymce-vue": "^3.2.8",
|
|
26
26
|
"accessibility-scanner": "^0.0.1",
|
|
27
27
|
"eslint": "^8.11.0",
|