Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"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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"}