diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/AnswersEditor/AnswersEditor.vue b/contentcuration/contentcuration/frontend/channelEdit/components/AnswersEditor/AnswersEditor.vue index e558478cb0..d141d98945 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/AnswersEditor/AnswersEditor.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/AnswersEditor/AnswersEditor.vue @@ -78,6 +78,7 @@ v-model="answer.answer" class="editor" :mode="isAnswerOpen(answerIdx) ? 'edit' : 'view'" + :imageProcessor="EditorImageProcessor" @update="updateAnswerText($event, answerIdx)" @minimize="emitClose" @open-editor="emitOpen(answerIdx)" @@ -127,6 +128,7 @@ import { AssessmentItemTypes } from 'shared/constants'; import { swapElements } from 'shared/utils/helpers'; import Checkbox from 'shared/views/form/Checkbox'; + import EditorImageProcessor from 'shared/views/TipTapEditor/TipTapEditor/services/imageService'; import TipTapEditor from 'shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue'; @@ -169,6 +171,7 @@ }, data() { return { + EditorImageProcessor, // Make it available in the template correctAnswersIndices: getCorrectAnswersIndices(this.questionKind, this.answers), numericRule: val => floatOrIntRegex.test(val) || this.$tr('numberFieldErrorLabel'), }; diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/AssessmentItemEditor/AssessmentItemEditor.vue b/contentcuration/contentcuration/frontend/channelEdit/components/AssessmentItemEditor/AssessmentItemEditor.vue index bfb2c9ed19..7f978c4d67 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/AssessmentItemEditor/AssessmentItemEditor.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/AssessmentItemEditor/AssessmentItemEditor.vue @@ -1,124 +1,119 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + {{ $tr('questionLabel') }} + + + + + + - - {{ $tr('questionLabel') }} - - - - - - - - + - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -139,10 +134,9 @@ FeatureFlagKeys, } from 'shared/constants'; import ErrorList from 'shared/views/ErrorList/ErrorList'; - import Uploader from 'shared/views/files/Uploader'; - import { FormatPresetsNames } from 'shared/leUtils/FormatPresets'; import DropdownWrapper from 'shared/views/form/DropdownWrapper'; import TipTapEditor from 'shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue'; + import EditorImageProcessor from 'shared/views/TipTapEditor/TipTapEditor/services/imageService'; export default { name: 'AssessmentItemEditor', @@ -151,7 +145,6 @@ ErrorList, AnswersEditor, HintsEditor, - Uploader, TipTapEditor, }, model: { @@ -212,6 +205,7 @@ openAnswerIdx: null, kindSelectKey: 0, AssessmentItemTypes, + EditorImageProcessor, }; }, computed: { @@ -224,9 +218,6 @@ return this.item.question; }, - imagePreset() { - return FormatPresetsNames.EXERCISE_IMAGE; - }, modality() { return this.getContentNode(this.nodeId)?.extra_fields?.options?.modality; }, diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/HintsEditor/HintsEditor.vue b/contentcuration/contentcuration/frontend/channelEdit/components/HintsEditor/HintsEditor.vue index 523451a055..6be4253ed5 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/HintsEditor/HintsEditor.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/HintsEditor/HintsEditor.vue @@ -38,6 +38,7 @@ ({ 'insert-image': target => imageHandler.openCreateModal({ targetElement: target }), @@ -294,6 +295,10 @@ type: [String, Number], default: 0, }, + imageProcessor: { + type: Object, + default: () => ({}), + }, }, emits: ['update', 'minimize', 'open-editor'], }); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditorStrings.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditorStrings.js index 99a57f1e52..641d769714 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditorStrings.js +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditorStrings.js @@ -301,6 +301,10 @@ const MESSAGES = { }, // Error Messages + errorUploadingImage: { + message: 'Error uploading image', + context: 'Title for the error modal when an image upload fails.', + }, noFileProvided: { message: 'No file provided.', context: 'Error message when no file is provided for upload', @@ -321,6 +325,10 @@ const MESSAGES = { message: 'Failed to process the image file.', context: 'Error message when image processing fails', }, + noEnoughStorageSpace: { + message: 'Not enough storage space available. File size exceeds remaining storage.', + context: 'Error message when there is insufficient storage space for the file', + }, // for MobileFormattingBar collapseFormattingBar: { diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue index 41afb25fe3..ffe1ab452f 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue @@ -122,10 +122,19 @@ if (imageRef.value && imageRef.value.naturalWidth && imageRef.value.naturalHeight) { naturalAspectRatio.value = imageRef.value.naturalWidth / imageRef.value.naturalHeight; - // If no dimensions are set, use natural dimensions + // If no dimensions are set, use natural dimensions but constrain to editor width if (!width.value && !height.value) { - width.value = imageRef.value.naturalWidth; - height.value = imageRef.value.naturalHeight; + // Get the editor's actual container width + const editorContainer = props.editor.view.dom.closest('.editor-container'); + const editorWidth = editorContainer + ? editorContainer.offsetWidth + : window.innerWidth * 0.4; // fallback: 40% of viewport width + + const maxWidth = Math.min(imageRef.value.naturalWidth, editorWidth); + + width.value = maxWidth; + height.value = Math.round(maxWidth / naturalAspectRatio.value); + saveSize(); } else if (width.value && !height.value) { // If we have width but no height, calculate height diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue index 211627d4eb..e79d136de1 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue @@ -137,6 +137,16 @@ + + {{ errorMessage }} + + {{ close$() }} + + @@ -144,8 +154,7 @@