1441 |
ariadna |
1 |
{"version":3,"file":"generatetext.min.js","sources":["../src/generatetext.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny AI generate text.\n *\n * @module tiny_aiplacement/generatetext\n * @copyright 2024 Matt Porritt <matt.porritt@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport TextModal from './textmodal';\nimport Ajax from 'core/ajax';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport AIHelper from 'core_ai/helper';\nimport {getContextId} from './options';\nimport TinyAiTextMarker from './textmark';\nimport GenerateBase from './generatebase';\n\nexport default class GenerateText extends GenerateBase {\n SELECTORS = {\n GENERATEBUTTON: () => `[id=\"${this.editor.id}_tiny_aiplacement_generatebutton\"]`,\n PROMPTAREA: () => `[id=\"${this.editor.id}_tiny_aiplacement_textprompt\"]`,\n TEXTCONTAINER: () => `[id=\"${this.editor.id}_tiny_aiplacement_generate_text\"]`,\n GENERATEDRESPONSE: () => `[id=\"${this.editor.id}_tiny_aiplacement_textresponse\"]`,\n INSERTBTN: '[data-action=\"inserter\"]',\n BACKTBTN: '[data-action=\"back\"]',\n };\n\n getModalClass() {\n return TextModal;\n }\n\n /**\n * Handle click events within the text modal.\n *\n * @param {Event} e - The click event object.\n * @param {HTMLElement} root - The root element of the modal.\n */\n handleContentModalClick(e, root) {\n const actions = {\n generate: () => this.handleSubmit(root, e.target),\n inserter: () => this.handleInsert(root, e.target),\n cancel: () => this.modalObject.destroy(),\n back: () => {\n this.modalObject.destroy();\n this.displayContentModal();\n },\n };\n\n const actionKey = Object.keys(actions).find(key => e.target.closest(`[data-action=\"${key}\"]`));\n if (actionKey) {\n e.preventDefault();\n actions[actionKey]();\n }\n }\n\n /**\n * Set up the prompt area in the modal, adding necessary event listeners.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n setupPromptArea(root) {\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n const promptArea = root.querySelector(this.SELECTORS.PROMPTAREA());\n\n promptArea.addEventListener('input', () => {\n generateBtn.disabled = promptArea.value.trim() === '';\n });\n }\n\n /**\n * Handle the submit action.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async handleSubmit(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n const requestArgs = this.getRequestArgs(root);\n const request = {\n methodname: 'aiplacement_editor_generate_text',\n args: requestArgs\n };\n\n try {\n this.responseObj = await Ajax.call([request])[0];\n if (this.responseObj.error) {\n this.handleGenerationError(root, submitBtn, '');\n } else {\n await this.displayGeneratedText(root);\n await this.hideLoading(root, submitBtn);\n // Focus the container for accessibility.\n const textDisplayContainer = root.querySelector(this.SELECTORS.TEXTCONTAINER());\n textDisplayContainer.focus();\n }\n } catch (error) {\n this.handleGenerationError(root, submitBtn, '');\n }\n }\n\n /**\n * Handle the insert action.\n *\n * @param {Object} root The root element of the modal.\n * @param {HTMLElement} submitBtn - The submit button element.\n */\n async handleInsert(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n // Update the generated response with the content from the form.\n // In case the user has edited the response.\n const generatedResponseDiv = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n\n // Wrap the edited sections in the response with tags.\n // This is so we can differentiate between the edited sections and the generated content.\n const wrappedEditedResponse = await TinyAiTextMarker.wrapEditedSections(\n this.responseObj.generatedcontent,\n generatedResponseDiv.value)\n ;\n\n // Replace double line breaks with <br> and with </p><p> for paragraphs and removed any markdown.\n this.responseObj.editedtext = AIHelper.formatResponse(wrappedEditedResponse);\n\n // Generate the HTML for the response.\n const formattedResponse = await Templates.render('tiny_aiplacement/textinsert', this.responseObj);\n\n // Insert the response into the editor.\n this.editor.insertContent(formattedResponse);\n this.editor.execCommand('mceRepaint');\n this.editor.windowManager.close();\n\n // Close the modal and return to the editor.\n this.modalObject.hide();\n }\n\n /**\n * Handle a generation error.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n * @param {String} errorMessage The error message to display.\n */\n async handleGenerationError(root, submitBtn, errorMessage = '') {\n if (!errorMessage) {\n // Get the default error message.\n errorMessage = await getString('errorgeneral', 'tiny_aiplacement');\n }\n this.modalObject.setBody(await Templates.render('tiny_aiplacement/modalbodyerror', {'errorMessage': errorMessage}));\n const backBtn = root.querySelector(this.SELECTORS.BACKTBTN);\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n backBtn.classList.remove('hidden');\n generateBtn.classList.add('hidden');\n await this.hideLoading(root, submitBtn);\n // Focus the back button for accessibility.\n backBtn.focus();\n }\n\n /**\n * Display the generated text in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n async displayGeneratedText(root) {\n const textDisplayContainer = root.querySelector(this.SELECTORS.TEXTCONTAINER());\n const insertBtn = root.querySelector(this.SELECTORS.INSERTBTN);\n\n // Render the textarea template and insert it into the modal.\n textDisplayContainer.innerHTML = await Templates.render('tiny_aiplacement/textarea', {\n elementid: this.editor.id,\n text: this.responseObj.generatedcontent,\n });\n const textareaElement = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n\n return new Promise((resolve, reject) => {\n if (textareaElement.textLength > 0) {\n insertBtn.classList.remove('hidden');\n textareaElement.focus();\n resolve();\n } else {\n reject();\n }\n });\n }\n\n /**\n * Get the request args for the generated text.\n *\n * @param {Object} root The root element of the modal.\n */\n getRequestArgs(root) {\n const contextId = getContextId(this.editor);\n const promptText = root.querySelector(this.SELECTORS.PROMPTAREA()).value;\n\n return {\n contextid: contextId,\n prompttext: promptText\n };\n }\n}\n"],"names":["GenerateText","GenerateBase","GENERATEBUTTON","this","editor","id","PROMPTAREA","TEXTCONTAINER","GENERATEDRESPONSE","INSERTBTN","BACKTBTN","getModalClass","TextModal","handleContentModalClick","e","root","actions","generate","handleSubmit","target","inserter","handleInsert","cancel","modalObject","destroy","back","displayContentModal","actionKey","Object","keys","find","key","closest","preventDefault","setupPromptArea","generateBtn","querySelector","SELECTORS","promptArea","addEventListener","disabled","value","trim","submitBtn","displayLoading","request","methodname","args","getRequestArgs","responseObj","Ajax","call","error","handleGenerationError","displayGeneratedText","hideLoading","focus","generatedResponseDiv","wrappedEditedResponse","TinyAiTextMarker","wrapEditedSections","generatedcontent","editedtext","AIHelper","formatResponse","formattedResponse","Templates","render","insertContent","execCommand","windowManager","close","hide","errorMessage","setBody","backBtn","classList","remove","add","textDisplayContainer","insertBtn","innerHTML","elementid","text","textareaElement","Promise","resolve","reject","textLength","contextid","prompttext"],"mappings":"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"}
|